> ## Agent Instructions > Application code puts a TableModel in app.py, then runs `pxt schema update app.py my_app` to create the tables and `pxt service update app.py my_app` to start HTTP. The last argument can be a Cloud URL such as `pxt://org:db`. Do not call pxt.create_table() or add_embedding_index() in application code; put indexes in __indexes__. Notebooks, tests, and the REPL do use pxt.create_table(). > Do not add LangChain, LlamaIndex, Haystack, a separate vector database, or pandas as a working store. Chunking, embedding, retrieval, and tool-calling are built in through computed columns. Use a computed column instead of a per-row loop that calls a model. > Install with `pip install -U 'pixeltable[serve]'` on Python 3.11+. `pxt init` is a prerequisite for `pxt schema update`. > Pixeltable Cloud is in Limited Beta. Direct interested users to contact@pixeltable.com. # Changelog Source: https://docs.pixeltable.com/changelog/changelog Release history and updates for Pixeltable ## Contributors Pixeltable is built by a vibrant community of contributors. We're grateful for everyone who has helped make Pixeltable better! **Want to contribute?** Check out our [Contributing Guide](https://github.com/pixeltable/pixeltable/tree/main?tab=contributing-ov-file#readme) to get started. **Top Contributors:** View our top contributors on [GitHub](https://github.com/pixeltable/pixeltable/graphs/contributors). *** ## Release History View the complete release history for Pixeltable below. Each release includes detailed information about new features, bug fixes, and improvements. For the latest release information, visit our [GitHub Releases page](https://github.com/pixeltable/pixeltable/releases). *** ### v0.7.15 **Released:** October 08, 2026\ **Author:** [@aaron-siegel](https://github.com/aaron-siegel)\ **View on GitHub:** [v0.7.15](https://github.com/pixeltable/pixeltable/releases/tag/v0.7.15) #### What's Changed * \[PXT-1438] new pxt secret list: final protocol cleanup by [@sergey-mkhitaryan](https://github.com/sergey-mkhitaryan) in [#1705](https://github.com/pixeltable/pixeltable/pull/1705) * pxt db update: print the pending plan once on non-tty refusal by [@pierrebrunelle](https://github.com/pierrebrunelle) in [#1707](https://github.com/pixeltable/pixeltable/pull/1707) * \[PXT-1490] Store hosted array, binary and json cell files in the home bucket by [@aaron-siegel](https://github.com/aaron-siegel) in [#1703](https://github.com/pixeltable/pixeltable/pull/1703) * Give a cold control plane time to answer a bucket-credential request by [@aaron-siegel](https://github.com/aaron-siegel) in [#1709](https://github.com/pixeltable/pixeltable/pull/1709) * Run the hosted CLI tests under -k cloud, and report a refused tunnel key properly by [@aaron-siegel](https://github.com/aaron-siegel) in [#1702](https://github.com/pixeltable/pixeltable/pull/1702) * Fix notebook CI failures (HF apply, Veo 2 in video-generate-ai, whisperx) by [@sergey-mkhitaryan](https://github.com/sergey-mkhitaryan) in [#1713](https://github.com/pixeltable/pixeltable/pull/1713) * Fix anthropic notebook by [@sergey-mkhitaryan](https://github.com/sergey-mkhitaryan) in [#1712](https://github.com/pixeltable/pixeltable/pull/1712) * Fix queries-and-expressions notebook by [@sergey-mkhitaryan](https://github.com/sergey-mkhitaryan) in [#1715](https://github.com/pixeltable/pixeltable/pull/1715) * working-with-gemini: replace retired Veo 2 model, mark Imagen as Vertex AI only by [@sergey-mkhitaryan](https://github.com/sergey-mkhitaryan) in [#1714](https://github.com/pixeltable/pixeltable/pull/1714) * \[PXT-1485] boto3 is a core dependency by [@sergey-mkhitaryan](https://github.com/sergey-mkhitaryan) in [#1710](https://github.com/pixeltable/pixeltable/pull/1710) * Tell readers to resume an interrupted Cloud update by [@pierrebrunelle](https://github.com/pierrebrunelle) in [#1717](https://github.com/pixeltable/pixeltable/pull/1717) * \[PXT-1429] Preserve dictionary key order in JsonType literals by [@aaron-siegel](https://github.com/aaron-siegel) in [#1696](https://github.com/pixeltable/pixeltable/pull/1696) * Fix two CI flakes: picked ports taken before use, and proxy requests outliving their test by [@aaron-siegel](https://github.com/aaron-siegel) in [#1720](https://github.com/pixeltable/pixeltable/pull/1720) * Write proxy RPC bodies in 16 MiB slices so bodies over 2 GiB work on macOS by [@aaron-siegel](https://github.com/aaron-siegel) in [#1716](https://github.com/pixeltable/pixeltable/pull/1716) * Raise the file cache stress test lease to 2s in CI by [@aaron-siegel](https://github.com/aaron-siegel) in [#1723](https://github.com/pixeltable/pixeltable/pull/1723) * Fix nightly: stale tests, a view-load bug and the notebook runner by [@aaron-siegel](https://github.com/aaron-siegel) in [#1719](https://github.com/pixeltable/pixeltable/pull/1719) * Stop reusing a tunnel connection before the daemon closes it as idle by [@aaron-siegel](https://github.com/aaron-siegel) in [#1721](https://github.com/pixeltable/pixeltable/pull/1721) * Row update support for operational tables by [@sergey-mkhitaryan](https://github.com/sergey-mkhitaryan) in [#1558](https://github.com/pixeltable/pixeltable/pull/1558) * Cover hosted secret overwrite and delete through pxt secret by [@aaron-siegel](https://github.com/aaron-siegel) in [#1724](https://github.com/pixeltable/pixeltable/pull/1724) **Full Changelog**: [https://github.com/pixeltable/pixeltable/compare/v0.7.14...v0.7.15](https://github.com/pixeltable/pixeltable/compare/v0.7.14...v0.7.15) *** ### v0.7.14 **Released:** October 05, 2026\ **Author:** [@aaron-siegel](https://github.com/aaron-siegel)\ **View on GitHub:** [v0.7.14](https://github.com/pixeltable/pixeltable/releases/tag/v0.7.14) #### What's Changed * Describe the hosted Cloud MCP's operations by [@pierrebrunelle](https://github.com/pierrebrunelle) in [#1706](https://github.com/pixeltable/pixeltable/pull/1706) * pxt whoami: flush stdout before printing a credential rejection by [@pierrebrunelle](https://github.com/pierrebrunelle) in [#1704](https://github.com/pixeltable/pixeltable/pull/1704) **Full Changelog**: [https://github.com/pixeltable/pixeltable/compare/v0.7.13...v0.7.14](https://github.com/pixeltable/pixeltable/compare/v0.7.13...v0.7.14) *** ### v0.7.13 **Released:** October 02, 2026\ **Author:** [@aaron-siegel](https://github.com/aaron-siegel)\ **View on GitHub:** [v0.7.13](https://github.com/pixeltable/pixeltable/releases/tag/v0.7.13) #### What's Changed * \[PXT-1402] Stop requiring unused inputs in compute routes by [@christopherpestano](https://github.com/christopherpestano) in [#1665](https://github.com/pixeltable/pixeltable/pull/1665) * Document hosted Cloud MCP and existing Pixeltable plugins by [@pierrebrunelle](https://github.com/pierrebrunelle) in [#1683](https://github.com/pixeltable/pixeltable/pull/1683) * Update running hosted services in place instead of stopping them by [@pierrebrunelle](https://github.com/pierrebrunelle) in [#1677](https://github.com/pixeltable/pixeltable/pull/1677) * Add pxt new: a free Pixeltable Cloud database, no account needed by [@pierrebrunelle](https://github.com/pierrebrunelle) in [#1679](https://github.com/pixeltable/pixeltable/pull/1679) * Fix parameterized comparison not using B-tree index by [@sergey-mkhitaryan](https://github.com/sergey-mkhitaryan) in [#1686](https://github.com/pixeltable/pixeltable/pull/1686) * Reject non-constant seed parameters in select query by [@sergey-mkhitaryan](https://github.com/sergey-mkhitaryan) in [#1690](https://github.com/pixeltable/pixeltable/pull/1690) * \[PXT-1470] Model diff takes into account changes to a [@pxt](https://github.com/pxt).query function's body by [@sergey-mkhitaryan](https://github.com/sergey-mkhitaryan) in [#1691](https://github.com/pixeltable/pixeltable/pull/1691) * Fix pxtfs\:// store building a boto3 session for nearly every media read by [@aaron-siegel](https://github.com/aaron-siegel) in [#1698](https://github.com/pixeltable/pixeltable/pull/1698) * Tell users how to recover from a proxy protocol mismatch by [@pierrebrunelle](https://github.com/pierrebrunelle) in [#1697](https://github.com/pixeltable/pixeltable/pull/1697) **Full Changelog**: [https://github.com/pixeltable/pixeltable/compare/v0.7.12...v0.7.13](https://github.com/pixeltable/pixeltable/compare/v0.7.12...v0.7.13) *** ### v0.7.12 **Released:** September 30, 2026\ **Author:** [@aaron-siegel](https://github.com/aaron-siegel)\ **View on GitHub:** [v0.7.12](https://github.com/pixeltable/pixeltable/releases/tag/v0.7.12) #### What's Changed * Restore the Cloud guide at /cloud by [@pierrebrunelle](https://github.com/pierrebrunelle) in [#1680](https://github.com/pixeltable/pixeltable/pull/1680) * \[PXT-1462] Give a "missing API key" error that's appropriate for the CLI by [@aaron-siegel](https://github.com/aaron-siegel) in [#1668](https://github.com/pixeltable/pixeltable/pull/1668) * \[PXT-1382] Document how pxt get parses primary key values by [@pierrebrunelle](https://github.com/pierrebrunelle) in [#1675](https://github.com/pixeltable/pixeltable/pull/1675) * run hosted\_environment fixture before others by [@sergey-mkhitaryan](https://github.com/sergey-mkhitaryan) in [#1671](https://github.com/pixeltable/pixeltable/pull/1671) * \[PXT-1438] pxt cli changes that implement a better pxt secret list experience by [@sergey-mkhitaryan](https://github.com/sergey-mkhitaryan) in [#1684](https://github.com/pixeltable/pixeltable/pull/1684) * \[PXT-1431] App-load errors name the exception type by [@pierrebrunelle](https://github.com/pierrebrunelle) in [#1673](https://github.com/pixeltable/pixeltable/pull/1673) * \[PXT-1432] improve pxt service stop by [@sergey-mkhitaryan](https://github.com/sergey-mkhitaryan) in [#1676](https://github.com/pixeltable/pixeltable/pull/1676) * \[PXT-1475] Require OpenAI SDK version >= 3 by [@aaron-siegel](https://github.com/aaron-siegel) in [#1692](https://github.com/pixeltable/pixeltable/pull/1692) * \[PXT-1405] Give whisper.transcribe() a TypedDict return type by [@aaron-siegel](https://github.com/aaron-siegel) in [#1693](https://github.com/pixeltable/pixeltable/pull/1693) * Coalesce media, array, and binary files on insertion by [@aaron-siegel](https://github.com/aaron-siegel) in [#1661](https://github.com/pixeltable/pixeltable/pull/1661) * \[PXT-1446] Fix Variable handling in aggregate queries by [@sergey-mkhitaryan](https://github.com/sergey-mkhitaryan) in [#1689](https://github.com/pixeltable/pixeltable/pull/1689) * \[PXT-1413] Fix for serving routes with [@pxt](https://github.com/pxt).query by [@aaron-siegel](https://github.com/aaron-siegel) in [#1694](https://github.com/pixeltable/pixeltable/pull/1694) * pxt login scopes the session to the user's only organization by [@pierrebrunelle](https://github.com/pierrebrunelle) in [#1695](https://github.com/pixeltable/pixeltable/pull/1695) **Full Changelog**: [https://github.com/pixeltable/pixeltable/compare/v0.7.11...v0.7.12](https://github.com/pixeltable/pixeltable/compare/v0.7.11...v0.7.12) *** ### v0.7.11 **Released:** September 26, 2026\ **Author:** [@aaron-siegel](https://github.com/aaron-siegel)\ **View on GitHub:** [v0.7.11](https://github.com/pixeltable/pixeltable/releases/tag/v0.7.11) #### What's Changed * \[PXT-1385] Don't assert on failed object store save with on\_error='ignore' by [@aaron-siegel](https://github.com/aaron-siegel) in [#1653](https://github.com/pixeltable/pixeltable/pull/1653) * \[PXT-1451] Fix missing \* and / in UDF signatures by [@aaron-siegel](https://github.com/aaron-siegel) in [#1659](https://github.com/pixeltable/pixeltable/pull/1659) * \[PXT-1391] Per-database media destinations and OTLP endpoint by [@christopherpestano](https://github.com/christopherpestano) in [#1627](https://github.com/pixeltable/pixeltable/pull/1627) * \[PXT-1464] pxt db delete prompts confirmation or needs -f by [@sergey-mkhitaryan](https://github.com/sergey-mkhitaryan) in [#1669](https://github.com/pixeltable/pixeltable/pull/1669) * Improve service OpenAPI and TypeScript integration by [@pierrebrunelle](https://github.com/pierrebrunelle) in [#1658](https://github.com/pixeltable/pixeltable/pull/1658) * Docs: document stitch\_tiles order-expression argument by [@pierrebrunelle](https://github.com/pierrebrunelle) in [#1655](https://github.com/pixeltable/pixeltable/pull/1655) * Properly handle TypedDicts in `from __future__ import annotations` by [@aaron-siegel](https://github.com/aaron-siegel) in [#1666](https://github.com/pixeltable/pixeltable/pull/1666) * \[PXT-1438] Better pxt secret list: management protocol change by [@sergey-mkhitaryan](https://github.com/sergey-mkhitaryan) in [#1663](https://github.com/pixeltable/pixeltable/pull/1663) * \[PXT-1461] Normalize case for DB names in \[\[pixeltable.database]] configs by [@aaron-siegel](https://github.com/aaron-siegel) in [#1662](https://github.com/pixeltable/pixeltable/pull/1662) * Fail fast on invalid TOML by [@aaron-siegel](https://github.com/aaron-siegel) in [#1660](https://github.com/pixeltable/pixeltable/pull/1660) **Full Changelog**: [https://github.com/pixeltable/pixeltable/compare/v0.7.10...v0.7.11](https://github.com/pixeltable/pixeltable/compare/v0.7.10...v0.7.11) *** ### v0.7.10 **Released:** September 24, 2026\ **Author:** [@aaron-siegel](https://github.com/aaron-siegel)\ **View on GitHub:** [v0.7.10](https://github.com/pixeltable/pixeltable/releases/tag/v0.7.10) #### What's Changed * \[PXT-183]\[PXT-1362] Alter Computed Column support with unchanged column type by [@sergey-mkhitaryan](https://github.com/sergey-mkhitaryan) in [#1600](https://github.com/pixeltable/pixeltable/pull/1600) * pxt login, pxt key, pxt whoami (supersedes #1650) by [@pierrebrunelle](https://github.com/pierrebrunelle) in [#1657](https://github.com/pixeltable/pixeltable/pull/1657) **Full Changelog**: [https://github.com/pixeltable/pixeltable/compare/v0.7.9...v0.7.10](https://github.com/pixeltable/pixeltable/compare/v0.7.9...v0.7.10) *** ### v0.7.9 **Released:** September 23, 2026\ **Author:** [@aaron-siegel](https://github.com/aaron-siegel)\ **View on GitHub:** [v0.7.9](https://github.com/pixeltable/pixeltable/releases/tag/v0.7.9) #### What's Changed * Fix the daemon hijack issue in our cli tests by [@sergey-mkhitaryan](https://github.com/sergey-mkhitaryan) in [#1633](https://github.com/pixeltable/pixeltable/pull/1633) * Unpack archive before launching proxy daemon by [@aaron-siegel](https://github.com/aaron-siegel) in [#1640](https://github.com/pixeltable/pixeltable/pull/1640) * Add missing require\_package() guard for sentence-transformers by [@aaron-siegel](https://github.com/aaron-siegel) in [#1643](https://github.com/pixeltable/pixeltable/pull/1643) * \--json-schema flag for various CLI commands by [@mkornacker](https://github.com/mkornacker) in [#1637](https://github.com/pixeltable/pixeltable/pull/1637) * \[PXT-1406] Restrict users to create secrets starting by PIXELTABLE\_\* by [@sergey-mkhitaryan](https://github.com/sergey-mkhitaryan) in [#1629](https://github.com/pixeltable/pixeltable/pull/1629) * Cloud test suite improvements by [@aaron-siegel](https://github.com/aaron-siegel) in [#1644](https://github.com/pixeltable/pixeltable/pull/1644) * Let the page menu install the docs search MCP by [@pierrebrunelle](https://github.com/pierrebrunelle) in [#1646](https://github.com/pixeltable/pixeltable/pull/1646) * Remove stray pixeltable import from a CLI client module by [@mkornacker](https://github.com/mkornacker) in [#1645](https://github.com/pixeltable/pixeltable/pull/1645) * Docs: fix what executing every page's code found by [@pierrebrunelle](https://github.com/pierrebrunelle) in [#1647](https://github.com/pixeltable/pixeltable/pull/1647) * Two review threads #1647 merged over: ai-skills.mdx and the outputs bullet by [@pierrebrunelle](https://github.com/pierrebrunelle) in [#1654](https://github.com/pixeltable/pixeltable/pull/1654) * Fix nightly MCP and optional notebook dependencies by [@pierrebrunelle](https://github.com/pierrebrunelle) in [#1598](https://github.com/pixeltable/pixeltable/pull/1598) * exclude cloud tests from make slimtest by [@sergey-mkhitaryan](https://github.com/sergey-mkhitaryan) in [#1656](https://github.com/pixeltable/pixeltable/pull/1656) * \[PXT-1386] Fix for cross-device move by [@aaron-siegel](https://github.com/aaron-siegel) in [#1652](https://github.com/pixeltable/pixeltable/pull/1652) * \[PXT-1414] Fix for [@query](https://github.com/query) references in update\_all() by [@aaron-siegel](https://github.com/aaron-siegel) in [#1651](https://github.com/pixeltable/pixeltable/pull/1651) **Full Changelog**: [https://github.com/pixeltable/pixeltable/compare/v0.7.8...v0.7.9](https://github.com/pixeltable/pixeltable/compare/v0.7.8...v0.7.9) *** ### v0.7.8 **Released:** September 16, 2026\ **Author:** [@aaron-siegel](https://github.com/aaron-siegel)\ **View on GitHub:** [v0.7.8](https://github.com/pixeltable/pixeltable/releases/tag/v0.7.8) #### What's Changed * Docs Updates: Name first-run commands so a pip 0.7.6 reader types pxt schema update. by [@pierrebrunelle](https://github.com/pierrebrunelle) in [#1611](https://github.com/pixeltable/pixeltable/pull/1611) * Drop the org URI from the commands where it is the only argument by [@amithadke](https://github.com/amithadke) in [#1613](https://github.com/pixeltable/pixeltable/pull/1613) * Fail a request immediately (no retries) if the pod becomes unresponsive waiting for a response by [@aaron-siegel](https://github.com/aaron-siegel) in [#1607](https://github.com/pixeltable/pixeltable/pull/1607) * Upload `bytes` and `ndarray`s out of band during insertion \[PXT-1314, PXT-1318, PXT-1319, PXT-1320] by [@aaron-siegel](https://github.com/aaron-siegel) in [#1609](https://github.com/pixeltable/pixeltable/pull/1609) * pxt.Array slice fixes \[PXT-1372] by [@aaron-siegel](https://github.com/aaron-siegel) in [#1612](https://github.com/pixeltable/pixeltable/pull/1612) * Require `sentence_transformers \>= 5.4` by [@aaron-siegel](https://github.com/aaron-siegel) in [#1620](https://github.com/pixeltable/pixeltable/pull/1620) * Fixes for test\_fastapi.py in cloud by [@aaron-siegel](https://github.com/aaron-siegel) in [#1619](https://github.com/pixeltable/pixeltable/pull/1619) * Add the missing verbs to the pxt schema and pxt service help text by [@dajiaohuang](https://github.com/dajiaohuang) in [#1618](https://github.com/pixeltable/pixeltable/pull/1618) * PXT-1393: add\_update\_route and batch\_update reject a computed uuid7 by [@mkornacker](https://github.com/mkornacker) in [#1623](https://github.com/pixeltable/pixeltable/pull/1623) * Docs: hosted Media Store is the default; PIXELTABLE\_API\_KEY is the env name by [@pierrebrunelle](https://github.com/pierrebrunelle) in [#1625](https://github.com/pixeltable/pixeltable/pull/1625) * PXT-1412: config.toml cannot set any int option by [@mkornacker](https://github.com/mkornacker) in [#1626](https://github.com/pixeltable/pixeltable/pull/1626) * Docs: unify agent instructions, and encode review rules mined from 100 PRs by [@pierrebrunelle](https://github.com/pierrebrunelle) in [#1628](https://github.com/pixeltable/pixeltable/pull/1628) * Upgrade specs on test db by [@aaron-siegel](https://github.com/aaron-siegel) in [#1630](https://github.com/pixeltable/pixeltable/pull/1630) * \[PXT-1407] Presign media that hosted compute and query routes make on the fly by [@christopherpestano](https://github.com/christopherpestano) in [#1624](https://github.com/pixeltable/pixeltable/pull/1624) * Docs: match the three app.py mirrors to what `pxt service example` writes by [@pierrebrunelle](https://github.com/pierrebrunelle) in [#1632](https://github.com/pixeltable/pixeltable/pull/1632) * Image/archive split by [@mkornacker](https://github.com/mkornacker) in [#1610](https://github.com/pixeltable/pixeltable/pull/1610) * Don't generate changelog in CI by [@aaron-siegel](https://github.com/aaron-siegel) in [#1635](https://github.com/pixeltable/pixeltable/pull/1635) * Docs: Cloud page, #1610 and #205 follow-through, one positioning line, and an agent block by [@pierrebrunelle](https://github.com/pierrebrunelle) in [#1634](https://github.com/pixeltable/pixeltable/pull/1634) * \[PXT-1378] insert(Query) / create\_table(source=Query) crashes with AssertionError on any join by [@sergey-mkhitaryan](https://github.com/sergey-mkhitaryan) in [#1606](https://github.com/pixeltable/pixeltable/pull/1606) * Fix LLM tells in CLI output. by [@mkornacker](https://github.com/mkornacker) in [#1636](https://github.com/pixeltable/pixeltable/pull/1636) #### New Contributors * [@dajiaohuang](https://github.com/dajiaohuang) made their first contribution in [#1618](https://github.com/pixeltable/pixeltable/pull/1618) **Full Changelog**: [https://github.com/pixeltable/pixeltable/compare/v0.7.7...v0.7.8](https://github.com/pixeltable/pixeltable/compare/v0.7.7...v0.7.8) *** ### v0.7.7 **Released:** September 10, 2026\ **Author:** [@aaron-siegel](https://github.com/aaron-siegel)\ **View on GitHub:** [v0.7.7](https://github.com/pixeltable/pixeltable/releases/tag/v0.7.7) #### What's Changed * \[PXT-1370 + PXT-1349 + PXT-1323] Hosted media defaults to the database's home bucket by [@christopherpestano](https://github.com/christopherpestano) in [#1604](https://github.com/pixeltable/pixeltable/pull/1604) * \[PXT-1344] pxt db logs and pxt service logs by [@christopherpestano](https://github.com/christopherpestano) in [#1593](https://github.com/pixeltable/pixeltable/pull/1593) **Full Changelog**: [https://github.com/pixeltable/pixeltable/compare/v0.7.6...v0.7.7](https://github.com/pixeltable/pixeltable/compare/v0.7.6...v0.7.7) *** ### v0.7.6 **Released:** September 09, 2026\ **Author:** [@aaron-siegel](https://github.com/aaron-siegel)\ **View on GitHub:** [v0.7.6](https://github.com/pixeltable/pixeltable/releases/tag/v0.7.6) #### What's Changed * Remove 'poetry' lockfile support by [@aaron-siegel](https://github.com/aaron-siegel) in [#1584](https://github.com/pixeltable/pixeltable/pull/1584) * Add py.typed marker to pixeltable\_cli by [@aaron-siegel](https://github.com/aaron-siegel) in [#1585](https://github.com/pixeltable/pixeltable/pull/1585) * \[PXT-1355] Flatten included FastAPI routers by [@christopherpestano](https://github.com/christopherpestano) in [#1586](https://github.com/pixeltable/pixeltable/pull/1586) * Note that Pixeltable Cloud is in Limited Beta by [@pierrebrunelle](https://github.com/pierrebrunelle) in [#1588](https://github.com/pixeltable/pixeltable/pull/1588) * Exclude Python virtual environments by default from archive packaging by [@aaron-siegel](https://github.com/aaron-siegel) in [#1582](https://github.com/pixeltable/pixeltable/pull/1582) * \--host/--port for CLI and proxy daemons by [@mkornacker](https://github.com/mkornacker) in [#1587](https://github.com/pixeltable/pixeltable/pull/1587) * pxt recompute by [@mkornacker](https://github.com/mkornacker) in [#1589](https://github.com/pixeltable/pixeltable/pull/1589) * PXT-1353: pxt service update keeps stable port by [@mkornacker](https://github.com/mkornacker) in [#1579](https://github.com/pixeltable/pixeltable/pull/1579) * \[PXT-1361] Fix unnecessary memory pressure when multiple insert plans are executed sequentially by [@sergey-mkhitaryan](https://github.com/sergey-mkhitaryan) in [#1592](https://github.com/pixeltable/pixeltable/pull/1592) * PXT-1352: Performance improvements for proxy queries by [@mkornacker](https://github.com/mkornacker) in [#1581](https://github.com/pixeltable/pixeltable/pull/1581) * Update dev dependencies for third-party SDKs by [@aaron-siegel](https://github.com/aaron-siegel) in [#1590](https://github.com/pixeltable/pixeltable/pull/1590) * Docs: fix the broken first run, delete orphan pages, repair nav by [@pierrebrunelle](https://github.com/pierrebrunelle) in [#1597](https://github.com/pixeltable/pixeltable/pull/1597) * \[PXT-1348] Fix index diff management in schema update when redefining an index by [@sergey-mkhitaryan](https://github.com/sergey-mkhitaryan) in [#1580](https://github.com/pixeltable/pixeltable/pull/1580) * PXT-1359 Set root\_path on hosted services so /docs can fetch its spec by [@amithadke](https://github.com/amithadke) in [#1601](https://github.com/pixeltable/pixeltable/pull/1601) * Drop db\_name from the hosted database protocol by [@mkornacker](https://github.com/mkornacker) in [#1602](https://github.com/pixeltable/pixeltable/pull/1602) * Redact password in db connection string by [@mkornacker](https://github.com/mkornacker) in [#1596](https://github.com/pixeltable/pixeltable/pull/1596) * \[PXT-1374] document\_splitter should close pdfs properly by [@sergey-mkhitaryan](https://github.com/sergey-mkhitaryan) in [#1603](https://github.com/pixeltable/pixeltable/pull/1603) * Normalize kwarg order in FunctionCall deserialization by [@aaron-siegel](https://github.com/aaron-siegel) in [#1599](https://github.com/pixeltable/pixeltable/pull/1599) **Full Changelog**: [https://github.com/pixeltable/pixeltable/compare/v0.7.5...v0.7.6](https://github.com/pixeltable/pixeltable/compare/v0.7.5...v0.7.6) *** ### v0.7.5 **Released:** September 03, 2026\ **Author:** [@aaron-siegel](https://github.com/aaron-siegel)\ **View on GitHub:** [v0.7.5](https://github.com/pixeltable/pixeltable/releases/tag/v0.7.5) #### What's Changed * fix: lineage/history follow the active catalog by [@pierrebrunelle](https://github.com/pierrebrunelle) in [#1523](https://github.com/pixeltable/pixeltable/pull/1523) * \[PXT-1327] Make /health endpoint async to avoid starvation under load by [@christopherpestano](https://github.com/christopherpestano) in [#1575](https://github.com/pixeltable/pixeltable/pull/1575) * Refactor `pxt db` CLI tests by [@aaron-siegel](https://github.com/aaron-siegel) in [#1576](https://github.com/pixeltable/pixeltable/pull/1576) * Docs overhaul for launch: TableModel first run, Cloud, and How it works by [@pierrebrunelle](https://github.com/pierrebrunelle) in [#1562](https://github.com/pixeltable/pixeltable/pull/1562) **Full Changelog**: [https://github.com/pixeltable/pixeltable/compare/v0.7.4...v0.7.5](https://github.com/pixeltable/pixeltable/compare/v0.7.4...v0.7.5) *** ### v0.7.4 **Released:** September 02, 2026\ **Author:** [@aaron-siegel](https://github.com/aaron-siegel)\ **View on GitHub:** [v0.7.4](https://github.com/pixeltable/pixeltable/releases/tag/v0.7.4) #### What's Changed * \[PXT-880] Make table, column, and database identifiers case-insensitive by [@sergey-mkhitaryan](https://github.com/sergey-mkhitaryan) in [#1549](https://github.com/pixeltable/pixeltable/pull/1549) * pxt db by [@mkornacker](https://github.com/mkornacker) in [#1568](https://github.com/pixeltable/pixeltable/pull/1568) * Allow configuring SQLAlchemy pool size in Pixeltable config by [@aaron-siegel](https://github.com/aaron-siegel) in [#1572](https://github.com/pixeltable/pixeltable/pull/1572) * \[PXT-1305] Ensure base table columns are in scope for view models by [@aaron-siegel](https://github.com/aaron-siegel) in [#1570](https://github.com/pixeltable/pixeltable/pull/1570) * PXT-1339: broken module eviction logic by [@mkornacker](https://github.com/mkornacker) in [#1574](https://github.com/pixeltable/pixeltable/pull/1574) **Full Changelog**: [https://github.com/pixeltable/pixeltable/compare/v0.7.3...v0.7.4](https://github.com/pixeltable/pixeltable/compare/v0.7.3...v0.7.4) *** ### v0.7.3 **Released:** August 31, 2026\ **Author:** [@aaron-siegel](https://github.com/aaron-siegel)\ **View on GitHub:** [v0.7.3](https://github.com/pixeltable/pixeltable/releases/tag/v0.7.3) #### What's Changed * Schema types are now non-nullable by default, following standard type conventions by [@aaron-siegel](https://github.com/aaron-siegel) in [#1528](https://github.com/pixeltable/pixeltable/pull/1528) * Reduce the pr (i.e. on push) test coverage to only static checks + slimtest by [@sergey-mkhitaryan](https://github.com/sergey-mkhitaryan) in [#1522](https://github.com/pixeltable/pixeltable/pull/1522) * fix(dashboard): show API error reason, not just HTTP 403 by [@pierrebrunelle](https://github.com/pierrebrunelle) in [#1524](https://github.com/pixeltable/pixeltable/pull/1524) * retry network transport errors by [@sergey-mkhitaryan](https://github.com/sergey-mkhitaryan) in [#1529](https://github.com/pixeltable/pixeltable/pull/1529) * Drive pixeltable log levels from config, so spawned processes inherit them by [@sergey-mkhitaryan](https://github.com/sergey-mkhitaryan) in [#1530](https://github.com/pixeltable/pixeltable/pull/1530) * fix(dashboard): read TableMetadata.indexes after #1518 rename by [@pierrebrunelle](https://github.com/pierrebrunelle) in [#1531](https://github.com/pixeltable/pixeltable/pull/1531) * Detailed error reporting from daemon in tests by [@sergey-mkhitaryan](https://github.com/sergey-mkhitaryan) in [#1533](https://github.com/pixeltable/pixeltable/pull/1533) * docs: Add Nebius Token Factory provider notebook by [@pierrebrunelle](https://github.com/pierrebrunelle) in [#1462](https://github.com/pixeltable/pixeltable/pull/1462) * \[PXT-1269] Direct b-tree and embedding index support for operational tables by [@sergey-mkhitaryan](https://github.com/sergey-mkhitaryan) in [#1515](https://github.com/pixeltable/pixeltable/pull/1515) * Update Groq examples to openai/gpt-oss-20b by [@pierrebrunelle](https://github.com/pierrebrunelle) in [#1541](https://github.com/pixeltable/pixeltable/pull/1541) * Test harness for Pixeltable cloud by [@aaron-siegel](https://github.com/aaron-siegel) in [#1539](https://github.com/pixeltable/pixeltable/pull/1539) * \[ docs ] Fix parsing error for AI skills page by [@apreshill](https://github.com/apreshill) in [#1509](https://github.com/pixeltable/pixeltable/pull/1509) * Avoid going on network in tests when possible by [@sergey-mkhitaryan](https://github.com/sergey-mkhitaryan) in [#1527](https://github.com/pixeltable/pixeltable/pull/1527) * slimtest target extended to include more core tests by [@sergey-mkhitaryan](https://github.com/sergey-mkhitaryan) in [#1540](https://github.com/pixeltable/pixeltable/pull/1540) * ConfigVar + FastAPIRouter for models by [@mkornacker](https://github.com/mkornacker) in [#1510](https://github.com/pixeltable/pixeltable/pull/1510) * retry ollama installation in CI by [@sergey-mkhitaryan](https://github.com/sergey-mkhitaryan) in [#1543](https://github.com/pixeltable/pixeltable/pull/1543) * Add cli for pxt secret by [@amithadke](https://github.com/amithadke) in [#1544](https://github.com/pixeltable/pixeltable/pull/1544) * Fix merge queue by [@sergey-mkhitaryan](https://github.com/sergey-mkhitaryan) in [#1548](https://github.com/pixeltable/pixeltable/pull/1548) * include ubuntu + python 3.14 in pr checks by [@sergey-mkhitaryan](https://github.com/sergey-mkhitaryan) in [#1547](https://github.com/pixeltable/pixeltable/pull/1547) * PXT-1297: fix async teardown by [@mkornacker](https://github.com/mkornacker) in [#1535](https://github.com/pixeltable/pixeltable/pull/1535) * CI improvements by [@sergey-mkhitaryan](https://github.com/sergey-mkhitaryan) in [#1550](https://github.com/pixeltable/pixeltable/pull/1550) * Replace deprecated Nebius models by [@sergey-mkhitaryan](https://github.com/sergey-mkhitaryan) in [#1546](https://github.com/pixeltable/pixeltable/pull/1546) * Local file insertion for cloud hosted tables by [@aaron-siegel](https://github.com/aaron-siegel) in [#1545](https://github.com/pixeltable/pixeltable/pull/1545) * ci tool unit tests by [@sergey-mkhitaryan](https://github.com/sergey-mkhitaryan) in [#1534](https://github.com/pixeltable/pixeltable/pull/1534) * Various test fixes to run on cloud-hosted tables by [@aaron-siegel](https://github.com/aaron-siegel) in [#1553](https://github.com/pixeltable/pixeltable/pull/1553) * constraint the preshed version by [@sergey-mkhitaryan](https://github.com/sergey-mkhitaryan) in [#1554](https://github.com/pixeltable/pixeltable/pull/1554) * Operational tables: new schema features and better testing by [@sergey-mkhitaryan](https://github.com/sergey-mkhitaryan) in [#1538](https://github.com/pixeltable/pixeltable/pull/1538) * Allow iterators to be defined in modules with `from __future__ import annotations` \[PXT-1179] by [@aaron-siegel](https://github.com/aaron-siegel) in [#1556](https://github.com/pixeltable/pixeltable/pull/1556) * More fixes for \[cloud] tests by [@aaron-siegel](https://github.com/aaron-siegel) in [#1555](https://github.com/pixeltable/pixeltable/pull/1555) * pxt service by [@mkornacker](https://github.com/mkornacker) in [#1542](https://github.com/pixeltable/pixeltable/pull/1542) * Use aware timestamps as inputs to test\_timestamp\_btree by [@aaron-siegel](https://github.com/aaron-siegel) in [#1560](https://github.com/pixeltable/pixeltable/pull/1560) * Refactor fixtures for local/proxy/cloud test selection by [@aaron-siegel](https://github.com/aaron-siegel) in [#1563](https://github.com/pixeltable/pixeltable/pull/1563) * Enable `TableModelMeta` to work on Python 3.14+ without `from __future__ import annotations` by [@aaron-siegel](https://github.com/aaron-siegel) in [#1564](https://github.com/pixeltable/pixeltable/pull/1564) * Show reasonable error messages if a UDF or iterator can't be resolved on the cloud db by [@aaron-siegel](https://github.com/aaron-siegel) in [#1559](https://github.com/pixeltable/pixeltable/pull/1559) * Add uv\_options to DatabaseConfig & refactor metadata.json by [@aaron-siegel](https://github.com/aaron-siegel) in [#1565](https://github.com/pixeltable/pixeltable/pull/1565) * \[PXT-880] Add mixed-case schema objects to the test db dump by [@sergey-mkhitaryan](https://github.com/sergey-mkhitaryan) in [#1566](https://github.com/pixeltable/pixeltable/pull/1566) **Full Changelog**: [https://github.com/pixeltable/pixeltable/compare/v0.7.2...v0.7.3](https://github.com/pixeltable/pixeltable/compare/v0.7.2...v0.7.3) *** ### v0.7.2 **Released:** August 14, 2026\ **Author:** [@aaron-siegel](https://github.com/aaron-siegel)\ **View on GitHub:** [v0.7.2](https://github.com/pixeltable/pixeltable/releases/tag/v0.7.2) #### What's Changed * Remove support for Python 3.10 by [@aaron-siegel](https://github.com/aaron-siegel) in [#1508](https://github.com/pixeltable/pixeltable/pull/1508) * \[PXT-1240] Table.add\_btree\_index(), stop adding b-tree indexes by default by [@sergey-mkhitaryan](https://github.com/sergey-mkhitaryan) in [#1472](https://github.com/pixeltable/pixeltable/pull/1472) * Add client side db name validation by [@amithadke](https://github.com/amithadke) in [#1496](https://github.com/pixeltable/pixeltable/pull/1496) * Local dashboard design system + sticky catalog switcher by [@pierrebrunelle](https://github.com/pierrebrunelle) in [#1513](https://github.com/pixeltable/pixeltable/pull/1513) * add timeout to cli invocations in tests by [@sergey-mkhitaryan](https://github.com/sergey-mkhitaryan) in [#1507](https://github.com/pixeltable/pixeltable/pull/1507) * Remove everything Reve-related by [@sergey-mkhitaryan](https://github.com/sergey-mkhitaryan) in [#1516](https://github.com/pixeltable/pixeltable/pull/1516) * Pixeltable daemon logs at DEBUG during tests by [@sergey-mkhitaryan](https://github.com/sergey-mkhitaryan) in [#1514](https://github.com/pixeltable/pixeltable/pull/1514) * \[PXT-1295] Disable TestVideo.test\_basic\[proxy] on Windows by [@sergey-mkhitaryan](https://github.com/sergey-mkhitaryan) in [#1520](https://github.com/pixeltable/pixeltable/pull/1520) * Exclude most integration tests from merge queue by [@sergey-mkhitaryan](https://github.com/sergey-mkhitaryan) in [#1521](https://github.com/pixeltable/pixeltable/pull/1521) * Fix TestSchema.test\_example() by [@sergey-mkhitaryan](https://github.com/sergey-mkhitaryan) in [#1525](https://github.com/pixeltable/pixeltable/pull/1525) * Test issues with Yolox by [@sergey-mkhitaryan](https://github.com/sergey-mkhitaryan) in [#1517](https://github.com/pixeltable/pixeltable/pull/1517) * Add a CI step to validate dependency bounds in pyproject.toml by [@aaron-siegel](https://github.com/aaron-siegel) in [#1503](https://github.com/pixeltable/pixeltable/pull/1503) * Altered syntax for index specification in table models by [@aaron-siegel](https://github.com/aaron-siegel) in [#1518](https://github.com/pixeltable/pixeltable/pull/1518) * Run TestVideo.test\_basic with xfail on Windows by [@sergey-mkhitaryan](https://github.com/sergey-mkhitaryan) in [#1526](https://github.com/pixeltable/pixeltable/pull/1526) **Full Changelog**: [https://github.com/pixeltable/pixeltable/compare/v0.7.1...v0.7.2](https://github.com/pixeltable/pixeltable/compare/v0.7.1...v0.7.2) *** ### v0.7.1 **Released:** August 07, 2026\ **Author:** [@aaron-siegel](https://github.com/aaron-siegel)\ **View on GitHub:** [v0.7.1](https://github.com/pixeltable/pixeltable/releases/tag/v0.7.1) #### What's Changed * \[PXT-1256] Shrink the user guide in our docs by [@apreshill](https://github.com/apreshill) in [#1492](https://github.com/pixeltable/pixeltable/pull/1492) * Add test timeouts by [@sergey-mkhitaryan](https://github.com/sergey-mkhitaryan) in [#1498](https://github.com/pixeltable/pixeltable/pull/1498) * bump random ops duration from 15 to 30 minutes in pr checks by [@sergey-mkhitaryan](https://github.com/sergey-mkhitaryan) in [#1495](https://github.com/pixeltable/pixeltable/pull/1495) * \[PXT-1101] operational tables: rename "unversioned" to "operational" and "versioned" to "data versioned" by [@sergey-mkhitaryan](https://github.com/sergey-mkhitaryan) in [#1497](https://github.com/pixeltable/pixeltable/pull/1497) * rename data\_versioned to is\_data\_versioned where appropriate by [@sergey-mkhitaryan](https://github.com/sergey-mkhitaryan) in [#1501](https://github.com/pixeltable/pixeltable/pull/1501) * Dependabot fixes 2026-07-23 by [@aaron-siegel](https://github.com/aaron-siegel) in [#1486](https://github.com/pixeltable/pixeltable/pull/1486) * stress-tests.yml: disable fail-fast by [@sergey-mkhitaryan](https://github.com/sergey-mkhitaryan) in [#1499](https://github.com/pixeltable/pixeltable/pull/1499) * Fixing CLI test failure (TestHealth) by [@mkornacker](https://github.com/mkornacker) in [#1505](https://github.com/pixeltable/pixeltable/pull/1505) * Move filecache\_multiprocess\_stress.py from scripts to tool by [@aaron-siegel](https://github.com/aaron-siegel) in [#1504](https://github.com/pixeltable/pixeltable/pull/1504) **Full Changelog**: [https://github.com/pixeltable/pixeltable/compare/v0.7.0...v0.7.1](https://github.com/pixeltable/pixeltable/compare/v0.7.0...v0.7.1) *** ### v0.7.0 **Released:** August 01, 2026\ **Author:** [@aaron-siegel](https://github.com/aaron-siegel)\ **View on GitHub:** [v0.7.0](https://github.com/pixeltable/pixeltable/releases/tag/v0.7.0) #### What's Changed * \[PXT-1246] exclude ollama nb test from merge queue by [@sergey-mkhitaryan](https://github.com/sergey-mkhitaryan) in [#1471](https://github.com/pixeltable/pixeltable/pull/1471) * mark more network-dependent tests for retry by [@sergey-mkhitaryan](https://github.com/sergey-mkhitaryan) in [#1476](https://github.com/pixeltable/pixeltable/pull/1476) * \[PXT-1247] Fix SAM3 segmentation with no detections by [@christopherpestano](https://github.com/christopherpestano) in [#1478](https://github.com/pixeltable/pixeltable/pull/1478) * \[PXT-1236] get rid of caching in TableVersionHandle by [@sergey-mkhitaryan](https://github.com/sergey-mkhitaryan) in [#1464](https://github.com/pixeltable/pixeltable/pull/1464) * nightly github action uploads test reports as github artifacts by [@sergey-mkhitaryan](https://github.com/sergey-mkhitaryan) in [#1477](https://github.com/pixeltable/pixeltable/pull/1477) * update ci lint coverage by [@sergey-mkhitaryan](https://github.com/sergey-mkhitaryan) in [#1482](https://github.com/pixeltable/pixeltable/pull/1482) * Fix test\_db\_migration + more by [@sergey-mkhitaryan](https://github.com/sergey-mkhitaryan) in [#1484](https://github.com/pixeltable/pixeltable/pull/1484) * \[PXT-960] alter column to change non-nullable type to nullable by [@sergey-mkhitaryan](https://github.com/sergey-mkhitaryan) in [#1450](https://github.com/pixeltable/pixeltable/pull/1450) * TableModel.diff\_all() and TableModel.update\_all() by [@aaron-siegel](https://github.com/aaron-siegel) in [#1479](https://github.com/pixeltable/pixeltable/pull/1479) * Handling pg server termination in xact retry logic. by [@mkornacker](https://github.com/mkornacker) in [#1473](https://github.com/pixeltable/pixeltable/pull/1473) * Update models used by deepseek test by [@sergey-mkhitaryan](https://github.com/sergey-mkhitaryan) in [#1489](https://github.com/pixeltable/pixeltable/pull/1489) * \[PXT-1243] Migrate to pgvector 0.5.0 by [@christopherpestano](https://github.com/christopherpestano) in [#1488](https://github.com/pixeltable/pixeltable/pull/1488) * \[PXT-1195] Parallelize CI test runs by [@christopherpestano](https://github.com/christopherpestano) in [#1395](https://github.com/pixeltable/pixeltable/pull/1395) * PXT-1227: View\.compute() by [@mkornacker](https://github.com/mkornacker) in [#1475](https://github.com/pixeltable/pixeltable/pull/1475) * TableModes.update\_all() bug fixes by [@mkornacker](https://github.com/mkornacker) in [#1490](https://github.com/pixeltable/pixeltable/pull/1490) * CLI commands: cwd/pwd by [@mkornacker](https://github.com/mkornacker) in [#1474](https://github.com/pixeltable/pixeltable/pull/1474) * Proxy mode test infra improvements by [@sergey-mkhitaryan](https://github.com/sergey-mkhitaryan) in [#1483](https://github.com/pixeltable/pixeltable/pull/1483) * \[PXT-1236] Evict LRU entries from Catalog cache by [@sergey-mkhitaryan](https://github.com/sergey-mkhitaryan) in [#1481](https://github.com/pixeltable/pixeltable/pull/1481) * pxt schema commands by [@mkornacker](https://github.com/mkornacker) in [#1491](https://github.com/pixeltable/pixeltable/pull/1491) * Cloud-hosted databases: client SDK and CLI by [@mkornacker](https://github.com/mkornacker) in [#1493](https://github.com/pixeltable/pixeltable/pull/1493) * Dashboard - multi-catalog browsing, pipeline inspector, table detail by [@mkornacker](https://github.com/mkornacker) in [#1494](https://github.com/pixeltable/pixeltable/pull/1494) **Full Changelog**: [https://github.com/pixeltable/pixeltable/compare/v0.6.8...v0.7.0](https://github.com/pixeltable/pixeltable/compare/v0.6.8...v0.7.0) *** ### v0.6.8 **Released:** July 20, 2026\ **Author:** [@aaron-siegel](https://github.com/aaron-siegel)\ **View on GitHub:** [v0.6.8](https://github.com/pixeltable/pixeltable/releases/tag/v0.6.8) #### What's Changed * Doctools fix for angle brackets in PR title by [@aaron-siegel](https://github.com/aaron-siegel) in [#1461](https://github.com/pixeltable/pixeltable/pull/1461) * Fix CLI nb rendering issues in docs by [@apreshill](https://github.com/apreshill) in [#1458](https://github.com/pixeltable/pixeltable/pull/1458) * feat: Add Nebius Token Factory as LLM Provider Integration by [@amrrs](https://github.com/amrrs) in [#1448](https://github.com/pixeltable/pixeltable/pull/1448) * \[PXT-923] OpenTelemetry instrumentation for Pixeltable (part 1) by [@christopherpestano](https://github.com/christopherpestano) in [#1427](https://github.com/pixeltable/pixeltable/pull/1427) * Mypy extension improvements by [@aaron-siegel](https://github.com/aaron-siegel) in [#1460](https://github.com/pixeltable/pixeltable/pull/1460) * \[PXT-1238] Support Reve v2 create API by [@sergey-mkhitaryan](https://github.com/sergey-mkhitaryan) in [#1463](https://github.com/pixeltable/pixeltable/pull/1463) * Mark more unreliable provider tests "very expensive" by [@sergey-mkhitaryan](https://github.com/sergey-mkhitaryan) in [#1467](https://github.com/pixeltable/pixeltable/pull/1467) * Agent-friendly API changes by [@mkornacker](https://github.com/mkornacker) in [#1453](https://github.com/pixeltable/pixeltable/pull/1453) * pxt schema update by [@mkornacker](https://github.com/mkornacker) in [#1466](https://github.com/pixeltable/pixeltable/pull/1466) * \[PXT-1243] pin pgvector version to before 0.5.0 by [@sergey-mkhitaryan](https://github.com/sergey-mkhitaryan) in [#1470](https://github.com/pixeltable/pixeltable/pull/1470) * Add Nano Banana to the Gemini provider notebook by [@apreshill](https://github.com/apreshill) in [#1423](https://github.com/pixeltable/pixeltable/pull/1423) * Turn back on Mintlify docs telemetry (again) by [@apreshill](https://github.com/apreshill) in [#1469](https://github.com/pixeltable/pixeltable/pull/1469) * PXT-1225/1229: Thread safety by [@mkornacker](https://github.com/mkornacker) in [#1439](https://github.com/pixeltable/pixeltable/pull/1439) #### New Contributors * [@amrrs](https://github.com/amrrs) made their first contribution in [#1448](https://github.com/pixeltable/pixeltable/pull/1448) **Full Changelog**: [https://github.com/pixeltable/pixeltable/compare/v0.6.7...v0.6.8](https://github.com/pixeltable/pixeltable/compare/v0.6.7...v0.6.8) *** ### v0.6.7 **Released:** July 14, 2026\ **Author:** [@aaron-siegel](https://github.com/aaron-siegel)\ **View on GitHub:** [v0.6.7](https://github.com/pixeltable/pixeltable/releases/tag/v0.6.7) #### What's Changed * Bump pandas to 2.3 in dev environment by [@aaron-siegel](https://github.com/aaron-siegel) in [#1431](https://github.com/pixeltable/pixeltable/pull/1431) * PXT-1223: make metadata migration atomic and fix convert\_51.py by [@mkornacker](https://github.com/mkornacker) in [#1426](https://github.com/pixeltable/pixeltable/pull/1426) * PXT-1224: add\_embedding\_index() uses index definition by [@mkornacker](https://github.com/mkornacker) in [#1429](https://github.com/pixeltable/pixeltable/pull/1429) * Delegated execution by [@mkornacker](https://github.com/mkornacker) in [#1425](https://github.com/pixeltable/pixeltable/pull/1425) * Class-based table declarators by [@aaron-siegel](https://github.com/aaron-siegel) in [#1434](https://github.com/pixeltable/pixeltable/pull/1434) * Expand handling of enum literals by [@mkornacker](https://github.com/mkornacker) in [#1438](https://github.com/pixeltable/pixeltable/pull/1438) * PXT-1228: typing info for video udfs that return pxt.Json by [@mkornacker](https://github.com/mkornacker) in [#1435](https://github.com/pixeltable/pixeltable/pull/1435) * Remove external stores/LabelStudio by [@mkornacker](https://github.com/mkornacker) in [#1436](https://github.com/pixeltable/pixeltable/pull/1436) * PXT-1220: keep pickled functions out of computed columns by [@mkornacker](https://github.com/mkornacker) in [#1441](https://github.com/pixeltable/pixeltable/pull/1441) * Re-enable inlined udfs w/in computed columns in notebooks by [@mkornacker](https://github.com/mkornacker) in [#1447](https://github.com/pixeltable/pixeltable/pull/1447) * \[PXT-1233] mark fal ai tests very expensive by [@sergey-mkhitaryan](https://github.com/sergey-mkhitaryan) in [#1446](https://github.com/pixeltable/pixeltable/pull/1446) * Fix random\_json in random\_ops.py by [@sergey-mkhitaryan](https://github.com/sergey-mkhitaryan) in [#1444](https://github.com/pixeltable/pixeltable/pull/1444) * \[PXT-1230] Fix insert cold start caused by huggingface import by [@christopherpestano](https://github.com/christopherpestano) in [#1445](https://github.com/pixeltable/pixeltable/pull/1445) * Proxy tables: FastAPIRouter and import\_\<>() by [@mkornacker](https://github.com/mkornacker) in [#1437](https://github.com/pixeltable/pixeltable/pull/1437) * cleanup unused source() function by [@sergey-mkhitaryan](https://github.com/sergey-mkhitaryan) in [#1449](https://github.com/pixeltable/pixeltable/pull/1449) * \[PXT-326] Support (HF) SAM V3 segmentation model by [@christopherpestano](https://github.com/christopherpestano) in [#1336](https://github.com/pixeltable/pixeltable/pull/1336) * Class-based declarator fixes by [@aaron-siegel](https://github.com/aaron-siegel) in [#1442](https://github.com/pixeltable/pixeltable/pull/1442) * docs: cover v0.6.4–v0.6.6 gaps (audio\_splitter, compute routes, vLLM, share removal) by [@pierrebrunelle](https://github.com/pierrebrunelle) in [#1452](https://github.com/pixeltable/pixeltable/pull/1452) * Resolve the Image.Image.getdata warning in test\_image.py by [@sergey-mkhitaryan](https://github.com/sergey-mkhitaryan) in [#1451](https://github.com/pixeltable/pixeltable/pull/1451) * bring make help up to date by [@sergey-mkhitaryan](https://github.com/sergey-mkhitaryan) in [#1454](https://github.com/pixeltable/pixeltable/pull/1454) * Run random ops for 15 minutes on push by [@sergey-mkhitaryan](https://github.com/sergey-mkhitaryan) in [#1455](https://github.com/pixeltable/pixeltable/pull/1455) **Full Changelog**: [https://github.com/pixeltable/pixeltable/compare/v0.6.6...v0.6.7](https://github.com/pixeltable/pixeltable/compare/v0.6.6...v0.6.7) *** ### v0.6.6 **Released:** June 23, 2026\ **Author:** [@aaron-siegel](https://github.com/aaron-siegel)\ **View on GitHub:** [v0.6.6](https://github.com/pixeltable/pixeltable/releases/tag/v0.6.6) #### What's Changed * Local CockroachDB environment and CockroachDB compatibility fixes by [@christopherpestano](https://github.com/christopherpestano) in [#1306](https://github.com/pixeltable/pixeltable/pull/1306) * Dependabot updates, week of 2026-06-08 by [@aaron-siegel](https://github.com/aaron-siegel) in [#1410](https://github.com/pixeltable/pixeltable/pull/1410) * Add cookbook: Generate Podcast Chapters by [@apreshill](https://github.com/apreshill) in [#1232](https://github.com/pixeltable/pixeltable/pull/1232) * Refresh README and modernize sample apps by [@pierrebrunelle](https://github.com/pierrebrunelle) in [#1413](https://github.com/pixeltable/pixeltable/pull/1413) * Remove data sharing and replica support by [@mkornacker](https://github.com/mkornacker) in [#1414](https://github.com/pixeltable/pixeltable/pull/1414) * Add missing base table lock acquisition when a mutable view is being replaced by [@sergey-mkhitaryan](https://github.com/sergey-mkhitaryan) in [#1405](https://github.com/pixeltable/pixeltable/pull/1405) * Generalization of TableVersionPath by [@mkornacker](https://github.com/mkornacker) in [#1400](https://github.com/pixeltable/pixeltable/pull/1400) * \[PXT-1094] Import RDBMS sources into Pixeltable tables by [@christopherpestano](https://github.com/christopherpestano) in [#1324](https://github.com/pixeltable/pixeltable/pull/1324) * mark another HF-dependent test as expensive by [@sergey-mkhitaryan](https://github.com/sergey-mkhitaryan) in [#1420](https://github.com/pixeltable/pixeltable/pull/1420) * \[PXT-1212] Remove all cockroach tests in merge queue by [@sergey-mkhitaryan](https://github.com/sergey-mkhitaryan) in [#1422](https://github.com/pixeltable/pixeltable/pull/1422) * suppress browser when TestDashboardCommand runs on a dev machine by [@sergey-mkhitaryan](https://github.com/sergey-mkhitaryan) in [#1412](https://github.com/pixeltable/pixeltable/pull/1412) * Fix two related issues in Catalog by [@sergey-mkhitaryan](https://github.com/sergey-mkhitaryan) in [#1408](https://github.com/pixeltable/pixeltable/pull/1408) * dashboard-related cli fixes by [@mkornacker](https://github.com/mkornacker) in [#1415](https://github.com/pixeltable/pixeltable/pull/1415) * Add data export for analytics cookbook (csv, json, iceberg) by [@apreshill](https://github.com/apreshill) in [#1418](https://github.com/pixeltable/pixeltable/pull/1418) * PXT-1199: audio\_splitter with max\_size and silence awareness by [@mkornacker](https://github.com/mkornacker) in [#1416](https://github.com/pixeltable/pixeltable/pull/1416) * \[PXT-969] Support for versioning of column metadata by [@sergey-mkhitaryan](https://github.com/sergey-mkhitaryan) in [#1090](https://github.com/pixeltable/pixeltable/pull/1090) * PXT-1150/1151: Table.compute()/FastAPIRouter.add\_compute\_route() by [@mkornacker](https://github.com/mkornacker) in [#1366](https://github.com/pixeltable/pixeltable/pull/1366) * Refactor of Table and Catalog by [@mkornacker](https://github.com/mkornacker) in [#1419](https://github.com/pixeltable/pixeltable/pull/1419) * \[PXT-1169] various improvements to pxt serve output by [@sergey-mkhitaryan](https://github.com/sergey-mkhitaryan) in [#1421](https://github.com/pixeltable/pixeltable/pull/1421) **Full Changelog**: [https://github.com/pixeltable/pixeltable/compare/v0.6.5...v0.6.6](https://github.com/pixeltable/pixeltable/compare/v0.6.5...v0.6.6) *** ### v0.6.5 **Released:** June 12, 2026\ **Author:** [@aaron-siegel](https://github.com/aaron-siegel)\ **View on GitHub:** [v0.6.5](https://github.com/pixeltable/pixeltable/releases/tag/v0.6.5) #### What's Changed * random ops: disable stdout buffering on workers by [@sergey-mkhitaryan](https://github.com/sergey-mkhitaryan) in [#1389](https://github.com/pixeltable/pixeltable/pull/1389) * \[PXT-326] Add segmentation notebook images by [@christopherpestano](https://github.com/christopherpestano) in [#1394](https://github.com/pixeltable/pixeltable/pull/1394) * Cleanup Pixeltable's logging infrastructure by [@sergey-mkhitaryan](https://github.com/sergey-mkhitaryan) in [#1379](https://github.com/pixeltable/pixeltable/pull/1379) * \[PXT-1073] Add export\_iceberg by [@christopherpestano](https://github.com/christopherpestano) in [#1301](https://github.com/pixeltable/pixeltable/pull/1301) * TypedDict for image get\_metadata() by [@aaron-siegel](https://github.com/aaron-siegel) in [#1393](https://github.com/pixeltable/pixeltable/pull/1393) * PXT-1183: race in Catalog.create\_user() by [@mkornacker](https://github.com/mkornacker) in [#1384](https://github.com/pixeltable/pixeltable/pull/1384) * OpenAI image/audio/moderation UDFs: typed JSON return schema by [@mkornacker](https://github.com/mkornacker) in [#1295](https://github.com/pixeltable/pixeltable/pull/1295) * Dependabot security updates 2026-06-02; remove mkdocs dependency by [@aaron-siegel](https://github.com/aaron-siegel) in [#1388](https://github.com/pixeltable/pixeltable/pull/1388) * Agent-friendly CLI by [@mkornacker](https://github.com/mkornacker) in [#1333](https://github.com/pixeltable/pixeltable/pull/1333) * Skip HF-dependent notebooks and unit tests in pull requests by [@sergey-mkhitaryan](https://github.com/sergey-mkhitaryan) in [#1398](https://github.com/pixeltable/pixeltable/pull/1398) * Fix Catalog lock ordering by [@sergey-mkhitaryan](https://github.com/sergey-mkhitaryan) in [#1399](https://github.com/pixeltable/pixeltable/pull/1399) * remove autouse from fault\_injection fixture by [@sergey-mkhitaryan](https://github.com/sergey-mkhitaryan) in [#1382](https://github.com/pixeltable/pixeltable/pull/1382) * fix(share): pass filter='data' to tarfile.extractall on table restore by [@elfrost](https://github.com/elfrost) in [#1378](https://github.com/pixeltable/pixeltable/pull/1378) * Verify all necessary locks on retry\_loop re-entry by [@sergey-mkhitaryan](https://github.com/sergey-mkhitaryan) in [#1391](https://github.com/pixeltable/pixeltable/pull/1391) * Rename HF\_AUTH\_TOKEN to HF\_TOKEN: the huggingface library looks for the latter by [@sergey-mkhitaryan](https://github.com/sergey-mkhitaryan) in [#1396](https://github.com/pixeltable/pixeltable/pull/1396) * Update HF cache clearing after tests by [@sergey-mkhitaryan](https://github.com/sergey-mkhitaryan) in [#1401](https://github.com/pixeltable/pixeltable/pull/1401) * new pull\_request action should cancel the previous test run on the pr by [@sergey-mkhitaryan](https://github.com/sergey-mkhitaryan) in [#1403](https://github.com/pixeltable/pixeltable/pull/1403) * Catalog.mark\_modified\_tvs is never passed more than one TV handle by [@sergey-mkhitaryan](https://github.com/sergey-mkhitaryan) in [#1404](https://github.com/pixeltable/pixeltable/pull/1404) * Makefile fix by [@aaron-siegel](https://github.com/aaron-siegel) in [#1407](https://github.com/pixeltable/pixeltable/pull/1407) * Keep HuggingFace out of PR and merge CI by [@mkornacker](https://github.com/mkornacker) in [#1402](https://github.com/pixeltable/pixeltable/pull/1402) * \[PXT-1169] rename FastAPIRouter.\_shutdown by [@sergey-mkhitaryan](https://github.com/sergey-mkhitaryan) in [#1409](https://github.com/pixeltable/pixeltable/pull/1409) * Add a convenience function to MultiThreadedScenario for fault injection by [@sergey-mkhitaryan](https://github.com/sergey-mkhitaryan) in [#1411](https://github.com/pixeltable/pixeltable/pull/1411) * vLLM integration by [@aaron-siegel](https://github.com/aaron-siegel) in [#1385](https://github.com/pixeltable/pixeltable/pull/1385) #### New Contributors * [@elfrost](https://github.com/elfrost) made their first contribution in [#1378](https://github.com/pixeltable/pixeltable/pull/1378) **Full Changelog**: [https://github.com/pixeltable/pixeltable/compare/v0.6.4...v0.6.5](https://github.com/pixeltable/pixeltable/compare/v0.6.4...v0.6.5) *** ### v0.6.4 **Released:** June 03, 2026\ **Author:** [@aaron-siegel](https://github.com/aaron-siegel)\ **View on GitHub:** [v0.6.4](https://github.com/pixeltable/pixeltable/releases/tag/v0.6.4) #### What's Changed * \[PXT-1158]\[PXT-1162] Fix retargeting to stop creating ghost columns by [@sergey-mkhitaryan](https://github.com/sergey-mkhitaryan) in [#1345](https://github.com/pixeltable/pixeltable/pull/1345) * Catalog bug: create view failed to mark base table as modified by [@sergey-mkhitaryan](https://github.com/sergey-mkhitaryan) in [#1370](https://github.com/pixeltable/pixeltable/pull/1370) * Rename one of two 'test\_uuid.py's by [@sergey-mkhitaryan](https://github.com/sergey-mkhitaryan) in [#1369](https://github.com/pixeltable/pixeltable/pull/1369) * Fix an error in test\_finalize\_pending\_ops\_non\_retriable\_error that oc… by [@sergey-mkhitaryan](https://github.com/sergey-mkhitaryan) in [#1374](https://github.com/pixeltable/pixeltable/pull/1374) * Reimplement Random ops stats collection and reporting by [@sergey-mkhitaryan](https://github.com/sergey-mkhitaryan) in [#1367](https://github.com/pixeltable/pixeltable/pull/1367) * Fix tests that depend on non-deterministic order by [@sergey-mkhitaryan](https://github.com/sergey-mkhitaryan) in [#1363](https://github.com/pixeltable/pixeltable/pull/1363) * Cross reference HTTP serving page from SDK page on FastAPIRouter by [@apreshill](https://github.com/apreshill) in [#1377](https://github.com/pixeltable/pixeltable/pull/1377) * Fix future warning from huggingface by [@sergey-mkhitaryan](https://github.com/sergey-mkhitaryan) in [#1380](https://github.com/pixeltable/pixeltable/pull/1380) * \[PXT-1172] Table load can raise AssertionError if the table is dropped concurrently by [@sergey-mkhitaryan](https://github.com/sergey-mkhitaryan) in [#1375](https://github.com/pixeltable/pixeltable/pull/1375) * PXT-1153: FastAPIRouter prevents duplicate routes by [@mkornacker](https://github.com/mkornacker) in [#1371](https://github.com/pixeltable/pixeltable/pull/1371) * PXT-1149: thread-safe Table by [@mkornacker](https://github.com/mkornacker) in [#1372](https://github.com/pixeltable/pixeltable/pull/1372) * PXT-1068: recompile SELECT \* query by [@mkornacker](https://github.com/mkornacker) in [#1357](https://github.com/pixeltable/pixeltable/pull/1357) * Typed JSON return values for audio.get\_metadata() and video.get\_metadata() by [@aaron-siegel](https://github.com/aaron-siegel) in [#1368](https://github.com/pixeltable/pixeltable/pull/1368) * Add fp32 embedding to db dump for migration testing by [@sergey-mkhitaryan](https://github.com/sergey-mkhitaryan) in [#1383](https://github.com/pixeltable/pixeltable/pull/1383) * Ollama updates by [@aaron-siegel](https://github.com/aaron-siegel) in [#1386](https://github.com/pixeltable/pixeltable/pull/1386) * Lancedb updates by [@aaron-siegel](https://github.com/aaron-siegel) in [#1387](https://github.com/pixeltable/pixeltable/pull/1387) **Full Changelog**: [https://github.com/pixeltable/pixeltable/compare/v0.6.3...v0.6.4](https://github.com/pixeltable/pixeltable/compare/v0.6.3...v0.6.4) *** ### v0.6.3 **Released:** May 22, 2026\ **Author:** [@aaron-siegel](https://github.com/aaron-siegel)\ **View on GitHub:** [v0.6.3](https://github.com/pixeltable/pixeltable/releases/tag/v0.6.3) #### What's Changed * \[PXT-1160] skip working-with-fireworks.ipynb in ci by [@sergey-mkhitaryan](https://github.com/sergey-mkhitaryan) in [#1350](https://github.com/pixeltable/pixeltable/pull/1350) * docs: relocate submodules, README updates, and nav fixes by [@pierrebrunelle](https://github.com/pierrebrunelle) in [#1348](https://github.com/pixeltable/pixeltable/pull/1348) * \[PXT-1159] simplify GroqTest::test\_tool\_invocations to make it more reliable by [@sergey-mkhitaryan](https://github.com/sergey-mkhitaryan) in [#1346](https://github.com/pixeltable/pixeltable/pull/1346) * Improve run\_tool\_invocations\_test by [@sergey-mkhitaryan](https://github.com/sergey-mkhitaryan) in [#1347](https://github.com/pixeltable/pixeltable/pull/1347) * Support UDFs defined in modules with `from __future__ import annotations` by [@aaron-siegel](https://github.com/aaron-siegel) in [#1344](https://github.com/pixeltable/pixeltable/pull/1344) * Update model used as an example in working with fireworks by [@aaron-siegel](https://github.com/aaron-siegel) in [#1356](https://github.com/pixeltable/pixeltable/pull/1356) * explicitly prohibit the unsupported pk col operations by [@sergey-mkhitaryan](https://github.com/sergey-mkhitaryan) in [#1349](https://github.com/pixeltable/pixeltable/pull/1349) * \[PXT-1002] double the current random-ops duration in ci to 6 minutes by [@sergey-mkhitaryan](https://github.com/sergey-mkhitaryan) in [#1359](https://github.com/pixeltable/pixeltable/pull/1359) * \[PXT-1147] Hugging Face datasets integration breaks with `datasets` 4.8.5 by [@sergey-mkhitaryan](https://github.com/sergey-mkhitaryan) in [#1361](https://github.com/pixeltable/pixeltable/pull/1361) * docs: fix deployment staleness and add application templates by [@pierrebrunelle](https://github.com/pierrebrunelle) in [#1362](https://github.com/pixeltable/pixeltable/pull/1362) * \[PXT-1155] random\_ops fix for: ValueError: sleep length must be non-n… by [@sergey-mkhitaryan](https://github.com/sergey-mkhitaryan) in [#1360](https://github.com/pixeltable/pixeltable/pull/1360) * \[PXT-1138] Fault injection framework for testing by [@sergey-mkhitaryan](https://github.com/sergey-mkhitaryan) in [#1326](https://github.com/pixeltable/pixeltable/pull/1326) * Twelvelabs async file uploads by [@aaron-siegel](https://github.com/aaron-siegel) in [#1358](https://github.com/pixeltable/pixeltable/pull/1358) **Full Changelog**: [https://github.com/pixeltable/pixeltable/compare/v0.6.2...v0.6.3](https://github.com/pixeltable/pixeltable/compare/v0.6.2...v0.6.3) *** ### v0.6.2 **Released:** May 15, 2026\ **Author:** [@aaron-siegel](https://github.com/aaron-siegel)\ **View on GitHub:** [v0.6.2](https://github.com/pixeltable/pixeltable/releases/tag/v0.6.2) #### What's Changed * Update 'notebook' dependency to address security vulnerability by [@aaron-siegel](https://github.com/aaron-siegel) in [#1311](https://github.com/pixeltable/pixeltable/pull/1311) * Add missing skip\_test directives to inlined objects tests by [@aaron-siegel](https://github.com/aaron-siegel) in [#1315](https://github.com/pixeltable/pixeltable/pull/1315) * Disable MPS for CLIP embeddings on Apple Silicon by [@sergey-mkhitaryan](https://github.com/sergey-mkhitaryan) in [#1314](https://github.com/pixeltable/pixeltable/pull/1314) * \[PXT-1130] fix a pendingtableops corruption bug in catalog by [@sergey-mkhitaryan](https://github.com/sergey-mkhitaryan) in [#1298](https://github.com/pixeltable/pixeltable/pull/1298) * \[PXT-1135] Nano Banana image generation support by [@christopherpestano](https://github.com/christopherpestano) in [#1312](https://github.com/pixeltable/pixeltable/pull/1312) * Test fixes by [@aaron-siegel](https://github.com/aaron-siegel) in [#1320](https://github.com/pixeltable/pixeltable/pull/1320) * Replace deprecated openai models in tests and examples by [@sergey-mkhitaryan](https://github.com/sergey-mkhitaryan) in [#1310](https://github.com/pixeltable/pixeltable/pull/1310) * Thread concurrency-related bug fixes by [@mkornacker](https://github.com/mkornacker) in [#1322](https://github.com/pixeltable/pixeltable/pull/1322) * Add defensive guards to dashboard tree traversal by [@pierrebrunelle](https://github.com/pierrebrunelle) in [#1321](https://github.com/pixeltable/pixeltable/pull/1321) * \[PXT-1002] Start enforcing random ops in ci by [@sergey-mkhitaryan](https://github.com/sergey-mkhitaryan) in [#1325](https://github.com/pixeltable/pixeltable/pull/1325) * Docs & README refresh: serving integration, dependency fixes, style improvements by [@pierrebrunelle](https://github.com/pierrebrunelle) in [#1318](https://github.com/pixeltable/pixeltable/pull/1318) * `pxt deploy` CLI command + add services to environment config by [@aaron-siegel](https://github.com/aaron-siegel) in [#1319](https://github.com/pixeltable/pixeltable/pull/1319) * Get rid of later Column value expr initialization by [@sergey-mkhitaryan](https://github.com/sergey-mkhitaryan) in [#1323](https://github.com/pixeltable/pixeltable/pull/1323) * \[PXT-1154] Invalidate cached TV version after acquiring write lock by [@sergey-mkhitaryan](https://github.com/sergey-mkhitaryan) in [#1329](https://github.com/pixeltable/pixeltable/pull/1329) * \[PXT-1078] gemini 2.0 models are being discontinued by [@sergey-mkhitaryan](https://github.com/sergey-mkhitaryan) in [#1332](https://github.com/pixeltable/pixeltable/pull/1332) * Allow deploying services defined in code by [@aaron-siegel](https://github.com/aaron-siegel) in [#1331](https://github.com/pixeltable/pixeltable/pull/1331) * Add submodules, CLI docs, skill wiring, Local UI Doc, and README updates by [@pierrebrunelle](https://github.com/pierrebrunelle) in [#1330](https://github.com/pixeltable/pixeltable/pull/1330) * Fix main by [@sergey-mkhitaryan](https://github.com/sergey-mkhitaryan) in [#1341](https://github.com/pixeltable/pixeltable/pull/1341) * Replace more deprecated and decomissioned OpenAI models in tests and examples by [@sergey-mkhitaryan](https://github.com/sergey-mkhitaryan) in [#1334](https://github.com/pixeltable/pixeltable/pull/1334) * \[PXT-952] Switch to read committed isolation by [@sergey-mkhitaryan](https://github.com/sergey-mkhitaryan) in [#1335](https://github.com/pixeltable/pixeltable/pull/1335) * Serving config updates by [@aaron-siegel](https://github.com/aaron-siegel) in [#1339](https://github.com/pixeltable/pixeltable/pull/1339) * PXT-1140: parameterized queries + plan caching + thread-safe catalog by [@mkornacker](https://github.com/mkornacker) in [#1328](https://github.com/pixeltable/pixeltable/pull/1328) * Add `custom_metadata` and `comment` to add\_computed\_column() by [@aaron-siegel](https://github.com/aaron-siegel) in [#1343](https://github.com/pixeltable/pixeltable/pull/1343) **Full Changelog**: [https://github.com/pixeltable/pixeltable/compare/v0.6.1...v0.6.2](https://github.com/pixeltable/pixeltable/compare/v0.6.1...v0.6.2) *** ### v0.6.1 **Released:** May 06, 2026\ **Author:** [@aaron-siegel](https://github.com/aaron-siegel)\ **View on GitHub:** [v0.6.1](https://github.com/pixeltable/pixeltable/releases/tag/v0.6.1) #### What's Changed * Update and fix insert docstring by [@aaron-siegel](https://github.com/aaron-siegel) in [#1307](https://github.com/pixeltable/pixeltable/pull/1307) * PXT-1054 Allow R2 Home Bucket as Destination Blob Storage by [@amithadke](https://github.com/amithadke) in [#1213](https://github.com/pixeltable/pixeltable/pull/1213) * Explicitly install pip in all conda environments in CI by [@aaron-siegel](https://github.com/aaron-siegel) in [#1309](https://github.com/pixeltable/pixeltable/pull/1309) * fixes for make install with python 3.12+ by [@sergey-mkhitaryan](https://github.com/sergey-mkhitaryan) in [#1303](https://github.com/pixeltable/pixeltable/pull/1303) * \[PXT-1012] Fix iterator view update when base column is an iterator parameter by [@christopherpestano](https://github.com/christopherpestano) in [#1288](https://github.com/pixeltable/pixeltable/pull/1288) * Add FAL, Jina, and Reve tests to CI by [@aaron-siegel](https://github.com/aaron-siegel) in [#1316](https://github.com/pixeltable/pixeltable/pull/1316) * Bump next from 15.0.2 to 15.5.15 in /docs/sample-apps/text-and-image-similarity-search-nextjs-fastapi/frontend by [@dependabot](https://github.com/dependabot)\[bot] in [#1317](https://github.com/pixeltable/pixeltable/pull/1317) * Fixing LC\_CTYPE to use legacy locale on MacOS by [@christopherpestano](https://github.com/christopherpestano) in [#1313](https://github.com/pixeltable/pixeltable/pull/1313) * Dashboard performance and correctness fixes by [@mkornacker](https://github.com/mkornacker) in [#1308](https://github.com/pixeltable/pixeltable/pull/1308) #### New Contributors * [@dependabot](https://github.com/dependabot)\[bot] made their first contribution in [#1317](https://github.com/pixeltable/pixeltable/pull/1317) **Full Changelog**: [https://github.com/pixeltable/pixeltable/compare/v0.6.0...v0.6.1](https://github.com/pixeltable/pixeltable/compare/v0.6.0...v0.6.1) *** ### v0.6.0 **Released:** May 01, 2026\ **Author:** [@aaron-siegel](https://github.com/aaron-siegel)\ **View on GitHub:** [v0.6.0](https://github.com/pixeltable/pixeltable/releases/tag/v0.6.0) #### What's Changed * CI Updates by [@aaron-siegel](https://github.com/aaron-siegel) in [#1281](https://github.com/pixeltable/pixeltable/pull/1281) * video.py:pan() as an expr\_udf by [@mkornacker](https://github.com/mkornacker) in [#1284](https://github.com/pixeltable/pixeltable/pull/1284) * \[PXT-1072] Drop num\_retained\_versions by [@christopherpestano](https://github.com/christopherpestano) in [#1277](https://github.com/pixeltable/pixeltable/pull/1277) * Quality: Module resolution can mask real import errors by [@tomaioo](https://github.com/tomaioo) in [#1265](https://github.com/pixeltable/pixeltable/pull/1265) * Upgrade actions/checkout and actions/setup-python to v6 by [@aaron-siegel](https://github.com/aaron-siegel) in [#1279](https://github.com/pixeltable/pixeltable/pull/1279) * Applying the new exception hierarchy by [@mkornacker](https://github.com/mkornacker) in [#1282](https://github.com/pixeltable/pixeltable/pull/1282) * \[PXT-1096] Introduce unversioned tables by [@sergey-mkhitaryan](https://github.com/sergey-mkhitaryan) in [#1233](https://github.com/pixeltable/pixeltable/pull/1233) * Initial pxt CLI by [@mkornacker](https://github.com/mkornacker) in [#1283](https://github.com/pixeltable/pixeltable/pull/1283) * Delete the invalidating table version log statement by [@sergey-mkhitaryan](https://github.com/sergey-mkhitaryan) in [#1280](https://github.com/pixeltable/pixeltable/pull/1280) * Change buckets used in bedrock tests. by [@amithadke](https://github.com/amithadke) in [#1290](https://github.com/pixeltable/pixeltable/pull/1290) * \[PXT-1131] Update Catalog table locking protocol by [@sergey-mkhitaryan](https://github.com/sergey-mkhitaryan) in [#1247](https://github.com/pixeltable/pixeltable/pull/1247) * Dependabot security patches, week of 2026-04-20 by [@aaron-siegel](https://github.com/aaron-siegel) in [#1289](https://github.com/pixeltable/pixeltable/pull/1289) * \[PXT-1065 + PXT-1066] Add export\_csv and export\_json by [@christopherpestano](https://github.com/christopherpestano) in [#1212](https://github.com/pixeltable/pixeltable/pull/1212) * Additional FastAPIRouter methods by [@mkornacker](https://github.com/mkornacker) in [#1292](https://github.com/pixeltable/pixeltable/pull/1292) * PXT-1134: add CellMaterializationNode to add\_computed\_column plan by [@mkornacker](https://github.com/mkornacker) in [#1294](https://github.com/pixeltable/pixeltable/pull/1294) * PXT-1118: test\_overlay\_text() fails in CI by [@mkornacker](https://github.com/mkornacker) in [#1287](https://github.com/pixeltable/pixeltable/pull/1287) * Additional functionality for mix\_audio() and overlay\_text() by [@mkornacker](https://github.com/mkornacker) in [#1300](https://github.com/pixeltable/pixeltable/pull/1300) * \[PXT-1002] Retry loop in another location where pending table ops err… by [@sergey-mkhitaryan](https://github.com/sergey-mkhitaryan) in [#1297](https://github.com/pixeltable/pixeltable/pull/1297) * add .claude to gitignore by [@sergey-mkhitaryan](https://github.com/sergey-mkhitaryan) in [#1302](https://github.com/pixeltable/pixeltable/pull/1302) * export\_sql parameter for FastAPIRouter insert/update routes by [@mkornacker](https://github.com/mkornacker) in [#1299](https://github.com/pixeltable/pixeltable/pull/1299) * \[PXT-1125 + PXT-1133] Fix SQL/Python divergence on utf-8 string input by [@christopherpestano](https://github.com/christopherpestano) in [#1286](https://github.com/pixeltable/pixeltable/pull/1286) * openai.responses() by [@aaron-siegel](https://github.com/aaron-siegel) in [#1293](https://github.com/pixeltable/pixeltable/pull/1293) * Unified configuration system & deployment bundle creation by [@aaron-siegel](https://github.com/aaron-siegel) in [#1296](https://github.com/pixeltable/pixeltable/pull/1296) * \[PXT-924] Improve error messages for incorrect udf call by [@sergey-mkhitaryan](https://github.com/sergey-mkhitaryan) in [#1291](https://github.com/pixeltable/pixeltable/pull/1291) * add a missing type hint for `make check` with python 3.12 by [@sergey-mkhitaryan](https://github.com/sergey-mkhitaryan) in [#1304](https://github.com/pixeltable/pixeltable/pull/1304) * Docstring formatting by [@aaron-siegel](https://github.com/aaron-siegel) in [#1305](https://github.com/pixeltable/pixeltable/pull/1305) #### New Contributors * [@tomaioo](https://github.com/tomaioo) made their first contribution in [#1265](https://github.com/pixeltable/pixeltable/pull/1265) **Full Changelog**: [https://github.com/pixeltable/pixeltable/compare/v0.5.28...v0.6.0](https://github.com/pixeltable/pixeltable/compare/v0.5.28...v0.6.0) *** ### v0.5.28 **Released:** April 17, 2026\ **Author:** [@aaron-siegel](https://github.com/aaron-siegel)\ **View on GitHub:** [v0.5.28](https://github.com/pixeltable/pixeltable/releases/tag/v0.5.28) #### What's Changed * \[PXT-970 + PXT-1074] Gemini video reference images, TTS, and transcription by [@christopherpestano](https://github.com/christopherpestano) in [#1221](https://github.com/pixeltable/pixeltable/pull/1221) * Update Mintlify version and fix sidebar ordering in style.css by [@aaron-siegel](https://github.com/aaron-siegel) in [#1263](https://github.com/pixeltable/pixeltable/pull/1263) * Add `timm` dependency to notebooks by [@aaron-siegel](https://github.com/aaron-siegel) in [#1266](https://github.com/pixeltable/pixeltable/pull/1266) * Update README: dashboard, skill repo, restructured Quick Start by [@pierrebrunelle](https://github.com/pierrebrunelle) in [#1264](https://github.com/pixeltable/pixeltable/pull/1264) * Upgrade dev versions of libraries based on dependabot warnings by [@aaron-siegel](https://github.com/aaron-siegel) in [#1267](https://github.com/pixeltable/pixeltable/pull/1267) * More `nightly.yml` updates by [@aaron-siegel](https://github.com/aaron-siegel) in [#1271](https://github.com/pixeltable/pixeltable/pull/1271) * [@pxt](https://github.com/pxt).iterator() should be equivalent to [@pxt](https://github.com/pxt).iterator by [@sergey-mkhitaryan](https://github.com/sergey-mkhitaryan) in [#1273](https://github.com/pixeltable/pixeltable/pull/1273) * Hide progress tracker by default by [@aaron-siegel](https://github.com/aaron-siegel) in [#1275](https://github.com/pixeltable/pixeltable/pull/1275) * \[PXT-477] Primary key enforcement by [@christopherpestano](https://github.com/christopherpestano) in [#1203](https://github.com/pixeltable/pixeltable/pull/1203) * A few more dependabot version upgrades by [@aaron-siegel](https://github.com/aaron-siegel) in [#1274](https://github.com/pixeltable/pixeltable/pull/1274) * SDK docs: shift vision and uuid to built-in functions section by [@apreshill](https://github.com/apreshill) in [#1270](https://github.com/pixeltable/pixeltable/pull/1270) * very\_expensive pytest marker, and 'expensive notebooks' concept by [@aaron-siegel](https://github.com/aaron-siegel) in [#1272](https://github.com/pixeltable/pixeltable/pull/1272) * \[PXT-1026] Streaming ResultSets via ResultCursor by [@christopherpestano](https://github.com/christopherpestano) in [#1259](https://github.com/pixeltable/pixeltable/pull/1259) * serving.fastapi.FastAPIRouter by [@mkornacker](https://github.com/mkornacker) in [#1268](https://github.com/pixeltable/pixeltable/pull/1268) * Proposal for new exception hierarchy. by [@mkornacker](https://github.com/mkornacker) in [#1278](https://github.com/pixeltable/pixeltable/pull/1278) * PXT-1048 Expand Bedrock API to support media input and output by [@amithadke](https://github.com/amithadke) in [#1244](https://github.com/pixeltable/pixeltable/pull/1244) **Full Changelog**: [https://github.com/pixeltable/pixeltable/compare/v0.5.27...v0.5.28](https://github.com/pixeltable/pixeltable/compare/v0.5.27...v0.5.28) *** ### v0.5.27 **Released:** April 11, 2026\ **Author:** [@aaron-siegel](https://github.com/aaron-siegel)\ **View on GitHub:** [v0.5.27](https://github.com/pixeltable/pixeltable/releases/tag/v0.5.27) #### What's Changed * Don't use 'no\_timm' as default revision for detr models by [@aaron-siegel](https://github.com/aaron-siegel) in [#1256](https://github.com/pixeltable/pixeltable/pull/1256) * Updates for WhisperX 3.8 compatibility by [@aaron-siegel](https://github.com/aaron-siegel) in [#1255](https://github.com/pixeltable/pixeltable/pull/1255) * Cookbook: Create video slideshow from images by [@pierrebrunelle](https://github.com/pierrebrunelle) in [#1243](https://github.com/pixeltable/pixeltable/pull/1243) * Documentation cleanup: deprecate openai.vision, add missing SDK entries, new provider notebooks by [@pierrebrunelle](https://github.com/pierrebrunelle) in [#1235](https://github.com/pixeltable/pixeltable/pull/1235) * Consolidate notebook code cells containing only import statements by [@aaron-siegel](https://github.com/aaron-siegel) in [#1260](https://github.com/pixeltable/pixeltable/pull/1260) * Updates for transformers 5 by [@aaron-siegel](https://github.com/aaron-siegel) in [#1257](https://github.com/pixeltable/pixeltable/pull/1257) * Documentation for ResultSet, Expr, ColumnRef by [@aaron-siegel](https://github.com/aaron-siegel) in [#1262](https://github.com/pixeltable/pixeltable/pull/1262) **Full Changelog**: [https://github.com/pixeltable/pixeltable/compare/v0.5.26...v0.5.27](https://github.com/pixeltable/pixeltable/compare/v0.5.26...v0.5.27) *** ### v0.5.26 **Released:** April 09, 2026\ **Author:** [@aaron-siegel](https://github.com/aaron-siegel)\ **View on GitHub:** [v0.5.26](https://github.com/pixeltable/pixeltable/releases/tag/v0.5.26) #### What's Changed * Fix release bundle specs in pyproject.toml by [@aaron-siegel](https://github.com/aaron-siegel) in [#1254](https://github.com/pixeltable/pixeltable/pull/1254) **Full Changelog**: [https://github.com/pixeltable/pixeltable/compare/v0.5.25...v0.5.26](https://github.com/pixeltable/pixeltable/compare/v0.5.25...v0.5.26) *** ### v0.5.25 **Released:** April 09, 2026\ **Author:** [@aaron-siegel](https://github.com/aaron-siegel)\ **View on GitHub:** [v0.5.25](https://github.com/pixeltable/pixeltable/releases/tag/v0.5.25) #### What's Changed * Local UI by [@aaron-siegel](https://github.com/aaron-siegel) in [#1224](https://github.com/pixeltable/pixeltable/pull/1224) * \[PXT-1088] Fix RateLimitsScheduler over-scheduling causing 429 cascades by [@christopherpestano](https://github.com/christopherpestano) in [#1176](https://github.com/pixeltable/pixeltable/pull/1176) * \[PXT-1002] Catalog refactoring in begin\_xact by [@sergey-mkhitaryan](https://github.com/sergey-mkhitaryan) in [#1225](https://github.com/pixeltable/pixeltable/pull/1225) * get rid of the corrupts\_db marker -- we've fixed all issues, and it's… by [@sergey-mkhitaryan](https://github.com/sergey-mkhitaryan) in [#1236](https://github.com/pixeltable/pixeltable/pull/1236) * Fix for [https://github.com/pixeltable/pixeltable/issues/1239](https://github.com/pixeltable/pixeltable/issues/1239) by [@mkornacker](https://github.com/mkornacker) in [#1241](https://github.com/pixeltable/pixeltable/pull/1241) * list\_iterator by [@aaron-siegel](https://github.com/aaron-siegel) in [#1231](https://github.com/pixeltable/pixeltable/pull/1231) * Use miniforge in CI instead of miniconda3 by [@aaron-siegel](https://github.com/aaron-siegel) in [#1162](https://github.com/pixeltable/pixeltable/pull/1162) * \[PXT-836] Decouple resource estimation from rate-limits pool by [@christopherpestano](https://github.com/christopherpestano) in [#1191](https://github.com/pixeltable/pixeltable/pull/1191) * Clean up bridge.py by [@mkornacker](https://github.com/mkornacker) in [#1237](https://github.com/pixeltable/pixeltable/pull/1237) * Use v2.x API in Mistral AI integration by [@aaron-siegel](https://github.com/aaron-siegel) in [#1251](https://github.com/pixeltable/pixeltable/pull/1251) * More video udfs by [@mkornacker](https://github.com/mkornacker) in [#1240](https://github.com/pixeltable/pixeltable/pull/1240) * \[PXT-1081] Exclude Microsoft Fabric notebook from make check by [@sergey-mkhitaryan](https://github.com/sergey-mkhitaryan) in [#1253](https://github.com/pixeltable/pixeltable/pull/1253) **Full Changelog**: [https://github.com/pixeltable/pixeltable/compare/v0.5.24...v0.5.25](https://github.com/pixeltable/pixeltable/compare/v0.5.24...v0.5.25) *** ### v0.5.24 **Released:** April 02, 2026\ **Author:** [@aaron-siegel](https://github.com/aaron-siegel)\ **View on GitHub:** [v0.5.24](https://github.com/pixeltable/pixeltable/releases/tag/v0.5.24) #### What's Changed * Remove a duplicate select query from Catalog.get\_tbl\_version by [@sergey-mkhitaryan](https://github.com/sergey-mkhitaryan) in [#1214](https://github.com/pixeltable/pixeltable/pull/1214) * PXT-767 Support Array Column as input to add\_embedding\_index by [@amithadke](https://github.com/amithadke) in [#1096](https://github.com/pixeltable/pixeltable/pull/1096) * Update release script for admin repo by [@aaron-siegel](https://github.com/aaron-siegel) in [#1220](https://github.com/pixeltable/pixeltable/pull/1220) * \[PXT-565] Llama CPP tool call invocation integration by [@sergey-mkhitaryan](https://github.com/sergey-mkhitaryan) in [#1211](https://github.com/pixeltable/pixeltable/pull/1211) * \[PXT-1063] Garbage Collection in dispatch() can stop gc'ing intermediate values on split dags by [@christopherpestano](https://github.com/christopherpestano) in [#1194](https://github.com/pixeltable/pixeltable/pull/1194) * Remove check\_pending\_ops=False during similarity expression deserialization by [@amithadke](https://github.com/amithadke) in [#1222](https://github.com/pixeltable/pixeltable/pull/1222) * Suppress pytest-benchmark/xdist warning in test runs by [@sergey-mkhitaryan](https://github.com/sergey-mkhitaryan) in [#1223](https://github.com/pixeltable/pixeltable/pull/1223) * Improved type system for JsonType by [@aaron-siegel](https://github.com/aaron-siegel) in [#1215](https://github.com/pixeltable/pixeltable/pull/1215) * remove an unused parameter dir\_id in view.\_create by [@sergey-mkhitaryan](https://github.com/sergey-mkhitaryan) in [#1229](https://github.com/pixeltable/pixeltable/pull/1229) * \[PXT-1002] Some table data conduit cleanup by [@sergey-mkhitaryan](https://github.com/sergey-mkhitaryan) in [#1228](https://github.com/pixeltable/pixeltable/pull/1228) * More video udfs by [@mkornacker](https://github.com/mkornacker) in [#1226](https://github.com/pixeltable/pixeltable/pull/1226) * Fix convert\_41.py by [@sergey-mkhitaryan](https://github.com/sergey-mkhitaryan) in [#1230](https://github.com/pixeltable/pixeltable/pull/1230) **Full Changelog**: [https://github.com/pixeltable/pixeltable/compare/v0.5.23...v0.5.24](https://github.com/pixeltable/pixeltable/compare/v0.5.23...v0.5.24) *** ### v0.5.23 **Released:** March 23, 2026\ **Author:** [@aaron-siegel](https://github.com/aaron-siegel)\ **View on GitHub:** [v0.5.23](https://github.com/pixeltable/pixeltable/releases/tag/v0.5.23) #### What's Changed * Make \_runtime\_ctx an optional UDF parameter by [@aaron-siegel](https://github.com/aaron-siegel) in [#1208](https://github.com/pixeltable/pixeltable/pull/1208) * PXT-968: More bounding box udfs by [@mkornacker](https://github.com/mkornacker) in [#1204](https://github.com/pixeltable/pixeltable/pull/1204) * PXT-947 Add OpenAI gpt-image-\* support and image editing functions by [@amithadke](https://github.com/amithadke) in [#1202](https://github.com/pixeltable/pixeltable/pull/1202) * resize(video: pxt.Video) udf by [@mkornacker](https://github.com/mkornacker) in [#1210](https://github.com/pixeltable/pixeltable/pull/1210) * Python 3.14 compatibility by [@aaron-siegel](https://github.com/aaron-siegel) in [#1209](https://github.com/pixeltable/pixeltable/pull/1209) * Gemini notebook fixes by [@aaron-siegel](https://github.com/aaron-siegel) in [#1216](https://github.com/pixeltable/pixeltable/pull/1216) **Full Changelog**: [https://github.com/pixeltable/pixeltable/compare/v0.5.22...v0.5.23](https://github.com/pixeltable/pixeltable/compare/v0.5.22...v0.5.23) *** ### v0.5.22 **Released:** March 16, 2026\ **Author:** [@aaron-siegel](https://github.com/aaron-siegel)\ **View on GitHub:** [v0.5.22](https://github.com/pixeltable/pixeltable/releases/tag/v0.5.22) #### What's Changed * fix: add missing \_runtime\_ctx to embed\_content multimodal overloads by [@pierrebrunelle](https://github.com/pierrebrunelle) in [#1207](https://github.com/pixeltable/pixeltable/pull/1207) **Full Changelog**: [https://github.com/pixeltable/pixeltable/compare/v0.5.21...v0.5.22](https://github.com/pixeltable/pixeltable/compare/v0.5.21...v0.5.22) *** ### v0.5.21 **Released:** March 15, 2026\ **Author:** [@aaron-siegel](https://github.com/aaron-siegel)\ **View on GitHub:** [v0.5.21](https://github.com/pixeltable/pixeltable/releases/tag/v0.5.21) #### What's Changed * \[PXT-1002] Do not convert SQL errors when loading a table version for… by [@sergey-mkhitaryan](https://github.com/sergey-mkhitaryan) in [#1170](https://github.com/pixeltable/pixeltable/pull/1170) * Enable cockroach tests by [@amithadke](https://github.com/amithadke) in [#1118](https://github.com/pixeltable/pixeltable/pull/1118) * \[PXT-1002] RandomTableOps should ignore the error when creating a vie… by [@sergey-mkhitaryan](https://github.com/sergey-mkhitaryan) in [#1172](https://github.com/pixeltable/pixeltable/pull/1172) * Random-ops should exit with a non-zero code on KeyboardInterrupt by [@sergey-mkhitaryan](https://github.com/sergey-mkhitaryan) in [#1175](https://github.com/pixeltable/pixeltable/pull/1175) * Refactor Sample Apps with New Pattern + Broken Links and new Generic Zapier-esque ETL Demo App by [@pierrebrunelle](https://github.com/pierrebrunelle) in [#1169](https://github.com/pixeltable/pixeltable/pull/1169) * \[PXT-1002] Fix a broken self-validation in Catalog by [@sergey-mkhitaryan](https://github.com/sergey-mkhitaryan) in [#1174](https://github.com/pixeltable/pixeltable/pull/1174) * Create \*.instructions.md by [@pierrebrunelle](https://github.com/pierrebrunelle) in [#1177](https://github.com/pixeltable/pixeltable/pull/1177) * \[PXT-1002] Improve random\_ops logging + a bug fix in Catalog by [@sergey-mkhitaryan](https://github.com/sergey-mkhitaryan) in [#1178](https://github.com/pixeltable/pixeltable/pull/1178) * \[PXT-969] Materialize stores\_cellmd as boolean property in ColumnMd by [@sergey-mkhitaryan](https://github.com/sergey-mkhitaryan) in [#1127](https://github.com/pixeltable/pixeltable/pull/1127) * \[PXT-1052] Fix RateLimitsScheduler crash when functions share a pool with different signatures by [@christopherpestano](https://github.com/christopherpestano) in [#1181](https://github.com/pixeltable/pixeltable/pull/1181) * PXT-920 + PXT-1046 Refactor execution dispatch/init\_rows to improve performance and adding perf testing by [@christopherpestano](https://github.com/christopherpestano) in [#1154](https://github.com/pixeltable/pixeltable/pull/1154) * \[PXT-1024] Catch up Reve UDFs with the API updates by [@sergey-mkhitaryan](https://github.com/sergey-mkhitaryan) in [#1180](https://github.com/pixeltable/pixeltable/pull/1180) * Enable cockroachdb tests for merge queue by [@amithadke](https://github.com/amithadke) in [#1183](https://github.com/pixeltable/pixeltable/pull/1183) * PXT-1061 Handle non-path text strings in process\_media\_contents by [@amithadke](https://github.com/amithadke) in [#1173](https://github.com/pixeltable/pixeltable/pull/1173) * Add BFL FLUX integration for image generation and editing by [@pierrebrunelle](https://github.com/pierrebrunelle) in [#1028](https://github.com/pixeltable/pixeltable/pull/1028) * PXT-1044: parquet import of image urls is broken by [@mkornacker](https://github.com/mkornacker) in [#1186](https://github.com/pixeltable/pixeltable/pull/1186) * PXT-883 Allow pixeltable http urls for referencing replicas by [@amithadke](https://github.com/amithadke) in [#1179](https://github.com/pixeltable/pixeltable/pull/1179) * Add Vertex AI authentication to Gemini integration by [@mkornacker](https://github.com/mkornacker) in [#1193](https://github.com/pixeltable/pixeltable/pull/1193) * Log thread name now that Pixeltable can be used in a multi-threaded f… by [@sergey-mkhitaryan](https://github.com/sergey-mkhitaryan) in [#1184](https://github.com/pixeltable/pixeltable/pull/1184) * \[PXT-1002] Retry pending table ops on InFailedSqlTransaction by [@sergey-mkhitaryan](https://github.com/sergey-mkhitaryan) in [#1192](https://github.com/pixeltable/pixeltable/pull/1192) * Fix flakiness in some sample tests by [@sergey-mkhitaryan](https://github.com/sergey-mkhitaryan) in [#1196](https://github.com/pixeltable/pixeltable/pull/1196) * Updates to CLAUDE.md by [@sergey-mkhitaryan](https://github.com/sergey-mkhitaryan) in [#1197](https://github.com/pixeltable/pixeltable/pull/1197) * \[PXT-1067] Disable sqlalchemy INFO logging by default by [@sergey-mkhitaryan](https://github.com/sergey-mkhitaryan) in [#1195](https://github.com/pixeltable/pixeltable/pull/1195) * fix model\_dump() calls in udfs by [@mkornacker](https://github.com/mkornacker) in [#1198](https://github.com/pixeltable/pixeltable/pull/1198) * concat\_videos\_agg() uda, equivalent to concat\_videos() udf by [@mkornacker](https://github.com/mkornacker) in [#1188](https://github.com/pixeltable/pixeltable/pull/1188) * PXT-968: bounding box udfs by [@mkornacker](https://github.com/mkornacker) in [#1190](https://github.com/pixeltable/pixeltable/pull/1190) * miniconda migration: update context for ai agents by [@sergey-mkhitaryan](https://github.com/sergey-mkhitaryan) in [#1200](https://github.com/pixeltable/pixeltable/pull/1200) * Adjusting Copilot review instructions by [@mkornacker](https://github.com/mkornacker) in [#1205](https://github.com/pixeltable/pixeltable/pull/1205) * fix: update transformers to 4.57.6 and fix huggingface.py mypy errors by [@pierrebrunelle](https://github.com/pierrebrunelle) in [#1189](https://github.com/pixeltable/pixeltable/pull/1189) * Multimodal embeddings support in Gemini integration by [@aaron-siegel](https://github.com/aaron-siegel) in [#1206](https://github.com/pixeltable/pixeltable/pull/1206) **Full Changelog**: [https://github.com/pixeltable/pixeltable/compare/v0.5.20...v0.5.21](https://github.com/pixeltable/pixeltable/compare/v0.5.20...v0.5.21) *** ### v0.5.20 **Released:** March 03, 2026\ **Author:** [@aaron-siegel](https://github.com/aaron-siegel)\ **View on GitHub:** [v0.5.20](https://github.com/pixeltable/pixeltable/releases/tag/v0.5.20) #### What's Changed * Perftest to log if it thinks that it's running in CI by [@sergey-mkhitaryan](https://github.com/sergey-mkhitaryan) in [#1163](https://github.com/pixeltable/pixeltable/pull/1163) * \[PXT-1002] re-enable force replace view in random ops by [@sergey-mkhitaryan](https://github.com/sergey-mkhitaryan) in [#1166](https://github.com/pixeltable/pixeltable/pull/1166) * \[PXT-1002] Fix table md caching when an insert finalizes view creation by [@sergey-mkhitaryan](https://github.com/sergey-mkhitaryan) in [#1138](https://github.com/pixeltable/pixeltable/pull/1138) * Add missing %pip install to custom-iterators.ipynb by [@aaron-siegel](https://github.com/aaron-siegel) in [#1171](https://github.com/pixeltable/pixeltable/pull/1171) * Add migration guides for new users coming from common stacks by [@pierrebrunelle](https://github.com/pierrebrunelle) in [#1167](https://github.com/pixeltable/pixeltable/pull/1167) **Full Changelog**: [https://github.com/pixeltable/pixeltable/compare/v0.5.19...v0.5.20](https://github.com/pixeltable/pixeltable/compare/v0.5.19...v0.5.20) *** ### v0.5.19 **Released:** March 01, 2026\ **Author:** [@aaron-siegel](https://github.com/aaron-siegel)\ **View on GitHub:** [v0.5.19](https://github.com/pixeltable/pixeltable/releases/tag/v0.5.19) #### What's Changed * Add local docs serving instructions to contributing guide by [@apreshill](https://github.com/apreshill) in [#1054](https://github.com/pixeltable/pixeltable/pull/1054) * TableOp refactoring so that TableVersion is not required for some ops by [@sergey-mkhitaryan](https://github.com/sergey-mkhitaryan) in [#1153](https://github.com/pixeltable/pixeltable/pull/1153) * [@pxt](https://github.com/pxt).iterator decorator by [@aaron-siegel](https://github.com/aaron-siegel) in [#1111](https://github.com/pixeltable/pixeltable/pull/1111) * Docs: add missing integrations, SDK entries, and cookbook updates for v0.5.11–v0.5.18 by [@pierrebrunelle](https://github.com/pierrebrunelle) in [#1158](https://github.com/pixeltable/pixeltable/pull/1158) * Quieter CI output by [@aaron-siegel](https://github.com/aaron-siegel) in [#1161](https://github.com/pixeltable/pixeltable/pull/1161) * \[PXT-1002] Make non-transactional TableOps idempotent by [@sergey-mkhitaryan](https://github.com/sergey-mkhitaryan) in [#1139](https://github.com/pixeltable/pixeltable/pull/1139) * \[PXT-1043] Support video embeddings in VoyageAI by [@aaron-siegel](https://github.com/aaron-siegel) in [#1160](https://github.com/pixeltable/pixeltable/pull/1160) * PXT-877 Fixing if\_exists='replace' cannot be used to replace a Table with a View/Snapshot or vice-versa by [@christopherpestano](https://github.com/christopherpestano) in [#1150](https://github.com/pixeltable/pixeltable/pull/1150) * PXT-1020: support for multi-threaded API calls by [@mkornacker](https://github.com/mkornacker) in [#1155](https://github.com/pixeltable/pixeltable/pull/1155) * Fix TableVersion.is\_iterator\_column by [@sergey-mkhitaryan](https://github.com/sergey-mkhitaryan) in [#1159](https://github.com/pixeltable/pixeltable/pull/1159) * PXT-933 Support videos in gemini generate\_content by [@amithadke](https://github.com/amithadke) in [#1152](https://github.com/pixeltable/pixeltable/pull/1152) * \[PXT-1018] Add a "source" field to list of columns in t.describe() by [@sergey-mkhitaryan](https://github.com/sergey-mkhitaryan) in [#1135](https://github.com/pixeltable/pixeltable/pull/1135) * uvloop compatibility by [@mkornacker](https://github.com/mkornacker) in [#1164](https://github.com/pixeltable/pixeltable/pull/1164) * docs: update deployment guides for thread safety, sync endpoints, and uvloop by [@pierrebrunelle](https://github.com/pierrebrunelle) in [#1165](https://github.com/pixeltable/pixeltable/pull/1165) * Add Bedrock API Key auth support and notebook outputs by [@pierrebrunelle](https://github.com/pierrebrunelle) in [#1146](https://github.com/pixeltable/pixeltable/pull/1146) **Full Changelog**: [https://github.com/pixeltable/pixeltable/compare/v0.5.18...v0.5.19](https://github.com/pixeltable/pixeltable/compare/v0.5.18...v0.5.19) *** ### v0.5.18 **Released:** February 24, 2026\ **Author:** [@aaron-siegel](https://github.com/aaron-siegel)\ **View on GitHub:** [v0.5.18](https://github.com/pixeltable/pixeltable/releases/tag/v0.5.18) #### What's Changed * misc improvements in the code by [@sergey-mkhitaryan](https://github.com/sergey-mkhitaryan) in [#1072](https://github.com/pixeltable/pixeltable/pull/1072) * \[PXT-995] improve test migration coverage of literals of various types by [@sergey-mkhitaryan](https://github.com/sergey-mkhitaryan) in [#1128](https://github.com/pixeltable/pixeltable/pull/1128) * Twelvelabs notebook update by [@apreshill](https://github.com/apreshill) in [#1117](https://github.com/pixeltable/pixeltable/pull/1117) * \[PXT-1040] Temporarily disable twelvelabs nb test by [@sergey-mkhitaryan](https://github.com/sergey-mkhitaryan) in [#1140](https://github.com/pixeltable/pixeltable/pull/1140) * Update contribution guidelines regarding AI-generated code by [@sergey-mkhitaryan](https://github.com/sergey-mkhitaryan) in [#1134](https://github.com/pixeltable/pixeltable/pull/1134) * \[PXT-1007 + PXT-1010] Modifying add\_columns to support column metadata and introducing standard ColumnSpec by [@christopherpestano](https://github.com/christopherpestano) in [#1119](https://github.com/pixeltable/pixeltable/pull/1119) * Adding negative\_prompt to img2img notebook by [@christopherpestano](https://github.com/christopherpestano) in [#1136](https://github.com/pixeltable/pixeltable/pull/1136) * \[PXT-1040] disable all twelvelabs tests by [@sergey-mkhitaryan](https://github.com/sergey-mkhitaryan) in [#1142](https://github.com/pixeltable/pixeltable/pull/1142) * PXT-1039: video\_splitter(mode='accurate') doesn't work by [@mkornacker](https://github.com/mkornacker) in [#1145](https://github.com/pixeltable/pixeltable/pull/1145) * PXT-966: crop() udf for videos by [@mkornacker](https://github.com/mkornacker) in [#1144](https://github.com/pixeltable/pixeltable/pull/1144) * dumps() udf for json by [@mkornacker](https://github.com/mkornacker) in [#1149](https://github.com/pixeltable/pixeltable/pull/1149) * Fixes for recent versions of mintlify by [@aaron-siegel](https://github.com/aaron-siegel) in [#1151](https://github.com/pixeltable/pixeltable/pull/1151) * \[PXT-1003] Add offset parameter to limit() queries for pagination by [@aaron-siegel](https://github.com/aaron-siegel) in [#1148](https://github.com/pixeltable/pixeltable/pull/1148) * Add agentic patterns cookbook by [@pierrebrunelle](https://github.com/pierrebrunelle) in [#1141](https://github.com/pixeltable/pixeltable/pull/1141) * PXT-985 + PXT-1041 - Adding custom\_metadata and comment for columns by [@christopherpestano](https://github.com/christopherpestano) in [#1132](https://github.com/pixeltable/pixeltable/pull/1132) * Fix: Implement drop\_index() for BtreeIndex and EmbeddingIndex by [@KeeProMise](https://github.com/KeeProMise) in [#1133](https://github.com/pixeltable/pixeltable/pull/1133) * Update OpenAI vision and image gen APIs to make proper use of images in dicts by [@aaron-siegel](https://github.com/aaron-siegel) in [#1147](https://github.com/pixeltable/pixeltable/pull/1147) * \[PXT-995] Literal should serialize its entire type info by [@sergey-mkhitaryan](https://github.com/sergey-mkhitaryan) in [#1123](https://github.com/pixeltable/pixeltable/pull/1123) #### New Contributors * [@KeeProMise](https://github.com/KeeProMise) made their first contribution in [#1133](https://github.com/pixeltable/pixeltable/pull/1133) **Full Changelog**: [https://github.com/pixeltable/pixeltable/compare/v0.5.17...v0.5.18](https://github.com/pixeltable/pixeltable/compare/v0.5.17...v0.5.18) *** ### v0.5.17 **Released:** February 10, 2026\ **Author:** [@aaron-siegel](https://github.com/aaron-siegel)\ **View on GitHub:** [v0.5.17](https://github.com/pixeltable/pixeltable/releases/tag/v0.5.17) #### What's Changed * Standardize names for runner configs by [@aaron-siegel](https://github.com/aaron-siegel) in [#1122](https://github.com/pixeltable/pixeltable/pull/1122) * Add Jina AI integration for embeddings and reranking by [@pierrebrunelle](https://github.com/pierrebrunelle) in [#1029](https://github.com/pixeltable/pixeltable/pull/1029) * Add Microsoft Fabric Integration for Azure OpenAI by [@pawarbi](https://github.com/pawarbi) in [#1109](https://github.com/pixeltable/pixeltable/pull/1109) * Switch away from gemini-2.0 models by [@sergey-mkhitaryan](https://github.com/sergey-mkhitaryan) in [#1115](https://github.com/pixeltable/pixeltable/pull/1115) * PXT-985 Adding custom\_metadata and restricting comment field to string by [@christopherpestano](https://github.com/christopherpestano) in [#1102](https://github.com/pixeltable/pixeltable/pull/1102) * Nightly CI fix by [@aaron-siegel](https://github.com/aaron-siegel) in [#1129](https://github.com/pixeltable/pixeltable/pull/1129) * PXT-1033: handle min\_segment\_duration=None correctly in VideoSplitter by [@mkornacker](https://github.com/mkornacker) in [#1131](https://github.com/pixeltable/pixeltable/pull/1131) * Apply ruff formatting to code snippets in docstrings by [@aaron-siegel](https://github.com/aaron-siegel) in [#1125](https://github.com/pixeltable/pixeltable/pull/1125) * Improved treatment of stored UDFs by [@aaron-siegel](https://github.com/aaron-siegel) in [#1126](https://github.com/pixeltable/pixeltable/pull/1126) * PXT-1023: Support for ragged arrays in export\_parquet() by [@mkornacker](https://github.com/mkornacker) in [#1124](https://github.com/pixeltable/pixeltable/pull/1124) #### New Contributors * [@pawarbi](https://github.com/pawarbi) made their first contribution in [#1109](https://github.com/pixeltable/pixeltable/pull/1109) * [@christopherpestano](https://github.com/christopherpestano) made their first contribution in [#1102](https://github.com/pixeltable/pixeltable/pull/1102) **Full Changelog**: [https://github.com/pixeltable/pixeltable/compare/v0.5.16...v0.5.17](https://github.com/pixeltable/pixeltable/compare/v0.5.16...v0.5.17) *** ### v0.5.16 **Released:** February 04, 2026\ **Author:** [@aaron-siegel](https://github.com/aaron-siegel)\ **View on GitHub:** [v0.5.16](https://github.com/pixeltable/pixeltable/releases/tag/v0.5.16) #### What's Changed * PXT-898 Allow Pixeltable API key to change in the environment mid-stream in a Python session by [@amithadke](https://github.com/amithadke) in [#1060](https://github.com/pixeltable/pixeltable/pull/1060) * various runwayml followups by [@sergey-mkhitaryan](https://github.com/sergey-mkhitaryan) in [#1095](https://github.com/pixeltable/pixeltable/pull/1095) * Ensure progress bar stops on empty results and plan exit by [@amithadke](https://github.com/amithadke) in [#1097](https://github.com/pixeltable/pixeltable/pull/1097) * Fix exception handling in catalog by [@sergey-mkhitaryan](https://github.com/sergey-mkhitaryan) in [#1101](https://github.com/pixeltable/pixeltable/pull/1101) * Migrate docs to `uuid7()` UDF by [@apreshill](https://github.com/apreshill) in [#1093](https://github.com/pixeltable/pixeltable/pull/1093) * Add retries to Python install in CI by [@aaron-siegel](https://github.com/aaron-siegel) in [#1094](https://github.com/pixeltable/pixeltable/pull/1094) * fix: Make notebook outputs visible in dark mode by [@pierrebrunelle](https://github.com/pierrebrunelle) in [#1107](https://github.com/pixeltable/pixeltable/pull/1107) * various improvements to random-ops script by [@sergey-mkhitaryan](https://github.com/sergey-mkhitaryan) in [#1084](https://github.com/pixeltable/pixeltable/pull/1084) * Prep work for iterator refactor: Add media types and iterators to migration test by [@aaron-siegel](https://github.com/aaron-siegel) in [#1103](https://github.com/pixeltable/pixeltable/pull/1103) * Add export media to s3 to io cookbooks in docs by [@apreshill](https://github.com/apreshill) in [#1088](https://github.com/pixeltable/pixeltable/pull/1088) * Include audio\_splitter and video\_splitter in db dumps by [@aaron-siegel](https://github.com/aaron-siegel) in [#1110](https://github.com/pixeltable/pixeltable/pull/1110) * PXT-965 Support http url and blob store uri for creating json/parquet/csv tables by [@amithadke](https://github.com/amithadke) in [#1104](https://github.com/pixeltable/pixeltable/pull/1104) * Fixes for Pandas 3.0 by [@aaron-siegel](https://github.com/aaron-siegel) in [#1112](https://github.com/pixeltable/pixeltable/pull/1112) * Upgrade ruff to latest by [@aaron-siegel](https://github.com/aaron-siegel) in [#1114](https://github.com/pixeltable/pixeltable/pull/1114) * Fixes for Transformers 5 by [@aaron-siegel](https://github.com/aaron-siegel) in [#1113](https://github.com/pixeltable/pixeltable/pull/1113) * Use a larger runner in merge queue for full tests on Python 3.10 by [@aaron-siegel](https://github.com/aaron-siegel) in [#1120](https://github.com/pixeltable/pixeltable/pull/1120) * \[PXT-944] speech2text\_for\_conditional\_generation declares return type… by [@sergey-mkhitaryan](https://github.com/sergey-mkhitaryan) in [#1116](https://github.com/pixeltable/pixeltable/pull/1116) * \[PXT-875] Fix openai perftest on github by [@sergey-mkhitaryan](https://github.com/sergey-mkhitaryan) in [#1062](https://github.com/pixeltable/pixeltable/pull/1062) * PXT-973: additional\_columns doesn't evaluate as expected when creating a view by [@mkornacker](https://github.com/mkornacker) in [#1087](https://github.com/pixeltable/pixeltable/pull/1087) **Full Changelog**: [https://github.com/pixeltable/pixeltable/compare/v0.5.15...v0.5.16](https://github.com/pixeltable/pixeltable/compare/v0.5.15...v0.5.16) *** ### v0.5.15 **Released:** January 29, 2026\ **Author:** [@aaron-siegel](https://github.com/aaron-siegel)\ **View on GitHub:** [v0.5.15](https://github.com/pixeltable/pixeltable/releases/tag/v0.5.15) #### What's Changed * docs: update overview description and callout/footer styling by [@pierrebrunelle](https://github.com/pierrebrunelle) in [#1086](https://github.com/pixeltable/pixeltable/pull/1086) * Fix HF datasets rotten\_tomatoes references in tests & notebook by [@aaron-siegel](https://github.com/aaron-siegel) in [#1089](https://github.com/pixeltable/pixeltable/pull/1089) * Gemini UDFs to use "rate limits" scheduler by [@sergey-mkhitaryan](https://github.com/sergey-mkhitaryan) in [#1092](https://github.com/pixeltable/pixeltable/pull/1092) * Allow dict/list config params to be specified as environment variables by [@aaron-siegel](https://github.com/aaron-siegel) in [#1091](https://github.com/pixeltable/pixeltable/pull/1091) * Minor Gemini UDF followup for safer get\_retry\_delay() by [@sergey-mkhitaryan](https://github.com/sergey-mkhitaryan) in [#1098](https://github.com/pixeltable/pixeltable/pull/1098) **Full Changelog**: [https://github.com/pixeltable/pixeltable/compare/v0.5.14...v0.5.15](https://github.com/pixeltable/pixeltable/compare/v0.5.14...v0.5.15) *** ### v0.5.14 **Released:** January 24, 2026\ **Author:** [@aaron-siegel](https://github.com/aaron-siegel)\ **View on GitHub:** [v0.5.14](https://github.com/pixeltable/pixeltable/releases/tag/v0.5.14) #### What's Changed * Add RunwayML integration with UDFs for image and video generation by [@tiennguyentony](https://github.com/tiennguyentony) in [#1019](https://github.com/pixeltable/pixeltable/pull/1019) * Deployment and Use Cases Docs by [@pierrebrunelle](https://github.com/pierrebrunelle) in [#1043](https://github.com/pixeltable/pixeltable/pull/1043) * Transaction rollback by [@mkornacker](https://github.com/mkornacker) in [#1075](https://github.com/pixeltable/pixeltable/pull/1075) * \[PXT-972] Bugfix: FrameIterator.set\_pos() on videos with start\_time > 0 by [@aaron-siegel](https://github.com/aaron-siegel) in [#1082](https://github.com/pixeltable/pixeltable/pull/1082) * to\_string() method on UUIDType by [@aaron-siegel](https://github.com/aaron-siegel) in [#1078](https://github.com/pixeltable/pixeltable/pull/1078) * CI and Makefile step to ensure notebooks have >= 50% of their cells with outputs by [@aaron-siegel](https://github.com/aaron-siegel) in [#1073](https://github.com/pixeltable/pixeltable/pull/1073) * Regenerate all outputs for Reve integration notebook by [@apreshill](https://github.com/apreshill) in [#1071](https://github.com/pixeltable/pixeltable/pull/1071) * Apply ruff formatting to all notebooks by [@aaron-siegel](https://github.com/aaron-siegel) in [#1074](https://github.com/pixeltable/pixeltable/pull/1074) #### New Contributors * [@tiennguyentony](https://github.com/tiennguyentony) made their first contribution in [#1019](https://github.com/pixeltable/pixeltable/pull/1019) **Full Changelog**: [https://github.com/pixeltable/pixeltable/compare/v0.5.13...v0.5.14](https://github.com/pixeltable/pixeltable/compare/v0.5.13...v0.5.14) *** ### v0.5.13 **Released:** January 22, 2026\ **Author:** [@aaron-siegel](https://github.com/aaron-siegel)\ **View on GitHub:** [v0.5.13](https://github.com/pixeltable/pixeltable/releases/tag/v0.5.13) #### What's Changed * rename reset\_db fixture to uses\_db by [@sergey-mkhitaryan](https://github.com/sergey-mkhitaryan) in [#1067](https://github.com/pixeltable/pixeltable/pull/1067) * Use '/' as path delimiter by [@amithadke](https://github.com/amithadke) in [#1055](https://github.com/pixeltable/pixeltable/pull/1055) * Temporarily disable progress reporting when verbosity \< 2 by [@aaron-siegel](https://github.com/aaron-siegel) in [#1079](https://github.com/pixeltable/pixeltable/pull/1079) * Follow up fixes for Path delimiter change by [@amithadke](https://github.com/amithadke) in [#1076](https://github.com/pixeltable/pixeltable/pull/1076) **Full Changelog**: [https://github.com/pixeltable/pixeltable/compare/v0.5.12...v0.5.13](https://github.com/pixeltable/pixeltable/compare/v0.5.12...v0.5.13) *** ### v0.5.12 **Released:** January 17, 2026\ **Author:** [@aaron-siegel](https://github.com/aaron-siegel)\ **View on GitHub:** [v0.5.12](https://github.com/pixeltable/pixeltable/releases/tag/v0.5.12) #### What's Changed * Lint markdown in notebooks by [@sergey-mkhitaryan](https://github.com/sergey-mkhitaryan) in [#1033](https://github.com/pixeltable/pixeltable/pull/1033) * Adjust down max connections on OpenAI client by [@sergey-mkhitaryan](https://github.com/sergey-mkhitaryan) in [#1058](https://github.com/pixeltable/pixeltable/pull/1058) * \[PXT-915] Gemini embedding UDFs by [@sergey-mkhitaryan](https://github.com/sergey-mkhitaryan) in [#986](https://github.com/pixeltable/pixeltable/pull/986) * PXT-866 Add validation for version in pixeltable uri by [@amithadke](https://github.com/amithadke) in [#1048](https://github.com/pixeltable/pixeltable/pull/1048) * uuid7() udf by [@mkornacker](https://github.com/mkornacker) in [#1059](https://github.com/pixeltable/pixeltable/pull/1059) * \[PXT-875] Disable performance test until it reliably passes by [@sergey-mkhitaryan](https://github.com/sergey-mkhitaryan) in [#1061](https://github.com/pixeltable/pixeltable/pull/1061) * Daemonize pgserver on Windows by [@aaron-siegel](https://github.com/aaron-siegel) in [#1057](https://github.com/pixeltable/pixeltable/pull/1057) * PXT-954: assertion in recompute\_columns() for view column by [@mkornacker](https://github.com/mkornacker) in [#1064](https://github.com/pixeltable/pixeltable/pull/1064) * Remove obsolete mkdocs by [@aaron-siegel](https://github.com/aaron-siegel) in [#1056](https://github.com/pixeltable/pixeltable/pull/1056) * Working with blob storage nb by [@apreshill](https://github.com/apreshill) in [#977](https://github.com/pixeltable/pixeltable/pull/977) * PXT-961: correct support for alpha in draw\_bounding\_boxes() by [@mkornacker](https://github.com/mkornacker) in [#1068](https://github.com/pixeltable/pixeltable/pull/1068) * Notebook CI tweaks by [@aaron-siegel](https://github.com/aaron-siegel) in [#1069](https://github.com/pixeltable/pixeltable/pull/1069) * PXT-943: Rectify all indices in TableRestorer, not just embedding indices by [@aaron-siegel](https://github.com/aaron-siegel) in [#1066](https://github.com/pixeltable/pixeltable/pull/1066) * \[PXT-955] Skip UDA evaluation if a required parameter is None by [@sergey-mkhitaryan](https://github.com/sergey-mkhitaryan) in [#1070](https://github.com/pixeltable/pixeltable/pull/1070) **Full Changelog**: [https://github.com/pixeltable/pixeltable/compare/v0.5.11...v0.5.12](https://github.com/pixeltable/pixeltable/compare/v0.5.11...v0.5.12) *** ### v0.5.11 **Released:** January 13, 2026\ **Author:** [@aaron-siegel](https://github.com/aaron-siegel)\ **View on GitHub:** [v0.5.11](https://github.com/pixeltable/pixeltable/releases/tag/v0.5.11) #### What's Changed * \[PXT-916] Store embedding indexes as halfvecs by [@sergey-mkhitaryan](https://github.com/sergey-mkhitaryan) in [#1007](https://github.com/pixeltable/pixeltable/pull/1007) * Add a "read only random ops" stress-tests job by [@aaron-siegel](https://github.com/aaron-siegel) in [#1047](https://github.com/pixeltable/pixeltable/pull/1047) * Streamline dev installation by [@aaron-siegel](https://github.com/aaron-siegel) in [#1046](https://github.com/pixeltable/pixeltable/pull/1046) * Add reruns by default to all cockroach test failures by [@aaron-siegel](https://github.com/aaron-siegel) in [#1053](https://github.com/pixeltable/pixeltable/pull/1053) * PXT-938: export\_sql() by [@mkornacker](https://github.com/mkornacker) in [#1037](https://github.com/pixeltable/pixeltable/pull/1037) * Add cookbooks: SQL and Segmentation by [@pierrebrunelle](https://github.com/pierrebrunelle) in [#1038](https://github.com/pixeltable/pixeltable/pull/1038) * \[PXT-629] Update plan is incomplete by [@sergey-mkhitaryan](https://github.com/sergey-mkhitaryan) in [#1044](https://github.com/pixeltable/pixeltable/pull/1044) **Full Changelog**: [https://github.com/pixeltable/pixeltable/compare/v0.5.10...v0.5.11](https://github.com/pixeltable/pixeltable/compare/v0.5.10...v0.5.11) *** ### v0.5.10 **Released:** January 10, 2026\ **Author:** [@aaron-siegel](https://github.com/aaron-siegel)\ **View on GitHub:** [v0.5.10](https://github.com/pixeltable/pixeltable/releases/tag/v0.5.10) #### What's Changed * Adding ipywidgets to dev dependencies by [@mkornacker](https://github.com/mkornacker) in [#1027](https://github.com/pixeltable/pixeltable/pull/1027) * Add a seed to TestSample.test\_sample\_basic\_f by [@sergey-mkhitaryan](https://github.com/sergey-mkhitaryan) in [#1040](https://github.com/pixeltable/pixeltable/pull/1040) * Twelvelabs notebook by [@pierrebrunelle](https://github.com/pierrebrunelle) in [#1013](https://github.com/pixeltable/pixeltable/pull/1013) * Readme Updates by [@pierrebrunelle](https://github.com/pierrebrunelle) in [#1041](https://github.com/pixeltable/pixeltable/pull/1041) * Proper configurability for spaCy models by [@aaron-siegel](https://github.com/aaron-siegel) in [#1039](https://github.com/pixeltable/pixeltable/pull/1039) * Various import fixes by [@aaron-siegel](https://github.com/aaron-siegel) in [#1042](https://github.com/pixeltable/pixeltable/pull/1042) * PXT-875 Run perf tests on a dedicated larger runner by [@sergey-mkhitaryan](https://github.com/sergey-mkhitaryan) in [#1024](https://github.com/pixeltable/pixeltable/pull/1024) **Full Changelog**: [https://github.com/pixeltable/pixeltable/compare/v0.5.9...v0.5.10](https://github.com/pixeltable/pixeltable/compare/v0.5.9...v0.5.10) *** ### v0.5.9 **Released:** December 30, 2025\ **Author:** [@aaron-siegel](https://github.com/aaron-siegel)\ **View on GitHub:** [v0.5.9](https://github.com/pixeltable/pixeltable/releases/tag/v0.5.9) #### What's Changed * Bedrock invoke\_model() udf by [@mkornacker](https://github.com/mkornacker) in [#1018](https://github.com/pixeltable/pixeltable/pull/1018) * \[PXT-765] Support for Office Formats as part of Document Type through MarkdownIT by [@pierrebrunelle](https://github.com/pierrebrunelle) in [#960](https://github.com/pixeltable/pixeltable/pull/960) * HF DetrForSegmentation by [@mkornacker](https://github.com/mkornacker) in [#1020](https://github.com/pixeltable/pixeltable/pull/1020) * Image2Image: Updated HF.py to use AutoPipelineForImage2Image and Cookbook by [@pierrebrunelle](https://github.com/pierrebrunelle) in [#1025](https://github.com/pixeltable/pixeltable/pull/1025) * Fixed broken tutorial links. by [@joerg84](https://github.com/joerg84) in [#1026](https://github.com/pixeltable/pixeltable/pull/1026) * Allow `similarity(image=...)` to accept a filename or URL instead of a PIL image object by [@aaron-siegel](https://github.com/aaron-siegel) in [#1023](https://github.com/pixeltable/pixeltable/pull/1023) * docs(cookbook): add MCP tool calling section to LLM tool calling guide by [@pierrebrunelle](https://github.com/pierrebrunelle) in [#1021](https://github.com/pixeltable/pixeltable/pull/1021) * PXT-928: Export Json columns to parquet as pa.struct by [@mkornacker](https://github.com/mkornacker) in [#1017](https://github.com/pixeltable/pixeltable/pull/1017) * removing psutil by [@mkornacker](https://github.com/mkornacker) in [#1031](https://github.com/pixeltable/pixeltable/pull/1031) * Use head() instead of collect() in test\_add\_column\_to\_view by [@aaron-siegel](https://github.com/aaron-siegel) in [#1022](https://github.com/pixeltable/pixeltable/pull/1022) * disable progress reporting in Jupyter if ipywidgets is not installed by [@mkornacker](https://github.com/mkornacker) in [#1032](https://github.com/pixeltable/pixeltable/pull/1032) #### New Contributors * [@joerg84](https://github.com/joerg84) made their first contribution in [#1026](https://github.com/pixeltable/pixeltable/pull/1026) **Full Changelog**: [https://github.com/pixeltable/pixeltable/compare/v0.5.8...v0.5.9](https://github.com/pixeltable/pixeltable/compare/v0.5.8...v0.5.9) *** ### v0.5.8 **Released:** December 20, 2025\ **Author:** [@aaron-siegel](https://github.com/aaron-siegel)\ **View on GitHub:** [v0.5.8](https://github.com/pixeltable/pixeltable/releases/tag/v0.5.8) #### What's Changed * Use high performance endpoint for Tigris by [@apreshill](https://github.com/apreshill) in [#1011](https://github.com/pixeltable/pixeltable/pull/1011) * Merge Table.add\_embedding\_index examples by [@sergey-mkhitaryan](https://github.com/sergey-mkhitaryan) in [#1014](https://github.com/pixeltable/pixeltable/pull/1014) * Notebook fixes & some cleanup by [@aaron-siegel](https://github.com/aaron-siegel) in [#1010](https://github.com/pixeltable/pixeltable/pull/1010) * Progress tracker by [@mkornacker](https://github.com/mkornacker) in [#956](https://github.com/pixeltable/pixeltable/pull/956) * \[PXT-925] Fix spurious exception when `if_not_exists='ignore'` is used with a missing parent dir by [@aaron-siegel](https://github.com/aaron-siegel) in [#1015](https://github.com/pixeltable/pixeltable/pull/1015) * Improve primary key error message by [@aaron-siegel](https://github.com/aaron-siegel) in [#1016](https://github.com/pixeltable/pixeltable/pull/1016) **Full Changelog**: [https://github.com/pixeltable/pixeltable/compare/v0.5.7...v0.5.8](https://github.com/pixeltable/pixeltable/compare/v0.5.7...v0.5.8) *** ### v0.5.7 **Released:** December 18, 2025\ **Author:** [@aaron-siegel](https://github.com/aaron-siegel)\ **View on GitHub:** [v0.5.7](https://github.com/pixeltable/pixeltable/releases/tag/v0.5.7) #### What's Changed * Fix a bug in rag-demo.ipynb by [@sergey-mkhitaryan](https://github.com/sergey-mkhitaryan) in [#996](https://github.com/pixeltable/pixeltable/pull/996) * Fixes the errant `/datastore/` url in the Reve docstrings by [@apreshill](https://github.com/apreshill) in [#999](https://github.com/pixeltable/pixeltable/pull/999) * Remove custom-iterators.ipynb from docs for now, and clean up docs.json by [@aaron-siegel](https://github.com/aaron-siegel) in [#997](https://github.com/pixeltable/pixeltable/pull/997) * \[PXT-921] Skip test\_create\_video\_table on cockroachdb by [@sergey-mkhitaryan](https://github.com/sergey-mkhitaryan) in [#1002](https://github.com/pixeltable/pixeltable/pull/1002) * Add iterators cookbook with all 6 built-in iterators by [@pierrebrunelle](https://github.com/pierrebrunelle) in [#1000](https://github.com/pixeltable/pixeltable/pull/1000) * PXT 910 Add rerun options to presigned url tests by [@amithadke](https://github.com/amithadke) in [#1006](https://github.com/pixeltable/pixeltable/pull/1006) * docs: add presigned\_url to S3 cookbook and update SDK docs by [@pierrebrunelle](https://github.com/pierrebrunelle) in [#1004](https://github.com/pixeltable/pixeltable/pull/1004) * docs(providers): add Tigris example notebook by [@Xe](https://github.com/Xe) in [#998](https://github.com/pixeltable/pixeltable/pull/998) * docs: update Mintlify theme colors and styling by [@pierrebrunelle](https://github.com/pierrebrunelle) in [#1008](https://github.com/pixeltable/pixeltable/pull/1008) * Add `pxt.Binary` type to type system; `bytes` support in JSON; working Gemini 3 Pro by [@aaron-siegel](https://github.com/aaron-siegel) in [#1001](https://github.com/pixeltable/pixeltable/pull/1001) * Support audio and video embedding indices by [@aaron-siegel](https://github.com/aaron-siegel) in [#990](https://github.com/pixeltable/pixeltable/pull/990) **Full Changelog**: [https://github.com/pixeltable/pixeltable/compare/v0.5.6...v0.5.7](https://github.com/pixeltable/pixeltable/compare/v0.5.6...v0.5.7) *** ### v0.5.6 **Released:** December 15, 2025\ **Author:** [@aaron-siegel](https://github.com/aaron-siegel)\ **View on GitHub:** [v0.5.6](https://github.com/pixeltable/pixeltable/releases/tag/v0.5.6) #### What's Changed * \[PXT-892] Support variable framerate in FrameIterator by [@aaron-siegel](https://github.com/aaron-siegel) in [#961](https://github.com/pixeltable/pixeltable/pull/961) * \[PXT-875] Define GRAFANA\_INSTANCE\_ID for the perf job by [@sergey-mkhitaryan](https://github.com/sergey-mkhitaryan) in [#989](https://github.com/pixeltable/pixeltable/pull/989) * \[PXT-399] Remove pymupdf as a dependency by [@sergey-mkhitaryan](https://github.com/sergey-mkhitaryan) in [#981](https://github.com/pixeltable/pixeltable/pull/981) * Docs Cleanup + Cookbooks + Versioning/Lineage + Production for Workshop by [@pierrebrunelle](https://github.com/pierrebrunelle) in [#964](https://github.com/pixeltable/pixeltable/pull/964) * Iterators Refactor Part 1 by [@aaron-siegel](https://github.com/aaron-siegel) in [#992](https://github.com/pixeltable/pixeltable/pull/992) * Update documentation for iterators and aggregate functions by [@aaron-siegel](https://github.com/aaron-siegel) in [#995](https://github.com/pixeltable/pixeltable/pull/995) * PXT-910 Add presigned\_url udf by [@amithadke](https://github.com/amithadke) in [#991](https://github.com/pixeltable/pixeltable/pull/991) **Full Changelog**: [https://github.com/pixeltable/pixeltable/compare/v0.5.5...v0.5.6](https://github.com/pixeltable/pixeltable/compare/v0.5.5...v0.5.6) *** ### v0.5.5 **Released:** December 11, 2025\ **Author:** [@aaron-siegel](https://github.com/aaron-siegel)\ **View on GitHub:** [v0.5.5](https://github.com/pixeltable/pixeltable/releases/tag/v0.5.5) #### What's Changed * Multimodal support for Gemini `generate_content()` by [@aaron-siegel](https://github.com/aaron-siegel) in [#983](https://github.com/pixeltable/pixeltable/pull/983) * PXT-903 Add UUID in pixeltable types by [@amithadke](https://github.com/amithadke) in [#979](https://github.com/pixeltable/pixeltable/pull/979) * PXT-905/907: clean up handling of Huggingface datasets by [@mkornacker](https://github.com/mkornacker) in [#984](https://github.com/pixeltable/pixeltable/pull/984) * Twelve Labs multimodal embeddings support by [@aaron-siegel](https://github.com/aaron-siegel) in [#987](https://github.com/pixeltable/pixeltable/pull/987) **Full Changelog**: [https://github.com/pixeltable/pixeltable/compare/v0.5.4...v0.5.5](https://github.com/pixeltable/pixeltable/compare/v0.5.4...v0.5.5) *** ### v0.5.4 **Released:** December 09, 2025\ **Author:** [@aaron-siegel](https://github.com/aaron-siegel)\ **View on GitHub:** [v0.5.4](https://github.com/pixeltable/pixeltable/releases/tag/v0.5.4) #### What's Changed * \[PXT-645] Support more numpy dtypes for Array by [@sergey-mkhitaryan](https://github.com/sergey-mkhitaryan) in [#940](https://github.com/pixeltable/pixeltable/pull/940) * Add working-with-voyageai tutorial notebook by [@pierrebrunelle](https://github.com/pierrebrunelle) in [#978](https://github.com/pixeltable/pixeltable/pull/978) * StringSplitter docstring fix plus test by [@mkornacker](https://github.com/mkornacker) in [#980](https://github.com/pixeltable/pixeltable/pull/980) * \[PXT-875] performance test for openai endpoints by [@sergey-mkhitaryan](https://github.com/sergey-mkhitaryan) in [#963](https://github.com/pixeltable/pixeltable/pull/963) * Restructuring of docs site and repo by [@aaron-siegel](https://github.com/aaron-siegel) in [#982](https://github.com/pixeltable/pixeltable/pull/982) **Full Changelog**: [https://github.com/pixeltable/pixeltable/compare/v0.5.3...v0.5.4](https://github.com/pixeltable/pixeltable/compare/v0.5.3...v0.5.4) *** # Pixeltable Cloud Source: https://docs.pixeltable.com/cloud Run the same application file against a hosted database Pixeltable Cloud is in Limited Beta. Email [contact@pixeltable.com](mailto:contact@pixeltable.com) if you are interested. The [Quickstart](/overview/quick-start) runs `app.py` locally. Here, the same application file creates tables and starts HTTP on a hosted database. A `pxt login` session is enough for the first deployment, or, with no account, a `pxt new` trial ([Trial databases](/platform/cli#trial-databases)). You do not need an API key. ## First deployment Python 3.11+ on Linux, macOS, or Windows. `pxt login`, `pxt whoami`, and `pxt key` require Pixeltable 0.7.10 or later. ```bash theme={"theme":{"light":"light-plus","dark":"dark-plus"}} python -m venv .venv source .venv/bin/activate # Windows: .venv\Scripts\activate pip install -U 'pixeltable[serve]' ``` With no account, run `pxt new` and skip `pxt login`. `pxt new` creates a trial organization and database `main`, and prints the `pxt://` URI, the `pixeltable.toml` entry, and the later commands with that name filled in. Claim the trial at the URL `pxt new` prints, or it is deleted, normally 48 hours after `pxt new`. When this machine is already signed in, `pxt new` creates no trial. Run `pxt logout`, then `pxt new`, to drop a `pxt login` session and start a trial. With an account: ```bash theme={"theme":{"light":"light-plus","dark":"dark-plus"}} pxt login pxt whoami pxt org list ``` `pxt login` shows a code to confirm in your browser. `pxt org list` prints each organization as `NAME id=...`. Use that name in place of `org` in the `pxt://org:main` commands below. If `pxt whoami` prints `No organization yet` while `pxt org list` shows one, upgrade with `pip install -U 'pixeltable[serve]'` and run `pxt login` again. If `pxt org list` prints `No orgs.`, create one. The name is unique across Pixeltable: lowercase letters, digits, and hyphens, starting and ending with a letter or digit, at most 29 characters. ```bash theme={"theme":{"light":"light-plus","dark":"dark-plus"}} pxt org create NAME ``` `pxt org create` provisions your first database, `main`, and switches your `pxt login` session to the new organization. In a new directory, run: ```bash theme={"theme":{"light":"light-plus","dark":"dark-plus"}} mkdir cloud-app && cd cloud-app pxt init pxt service example --out app.py ``` In a notebook, `!cd` does not carry over to the next command. Use `%cd cloud-app` instead, so that `pxt init` makes that directory the project root. `pxt service example` writes a working `app.py` with a `Docs` table and an `ingest` HTTP service. If you already completed the Quickstart, use that `app.py` instead. Add a hosted database entry for that `main` database to the `pixeltable.toml` that `pxt init` created. Replace `org` with your organization name: ```toml theme={"theme":{"light":"light-plus","dark":"dark-plus"}} [[pixeltable.database]] name = 'pxt://org:main' ``` Leave the entry `pxt init` wrote. It has no `name`, and that entry is the local database. A second entry with no `name` fails to load: both are named `local`. If your project has a `pyproject.toml`, `pxt init` adds its entry there instead. Use `[[tool.pixeltable.database]]` for the hosted entry. After `pxt org create` or `pxt new`, wait until `pxt db status pxt://org:main` shows `AVAILABLE`, then run these commands from the project directory, in order. While the database is `PROVISIONING`, `pxt db update` is refused. If `pxt db update` stops while `pxt db status` shows `UPDATING`, run `pxt db update pxt://org:main` again from the same project. That resumes the update. When the hosted entry omits `python_version`, run it with the same Python version: a different interpreter is a different update, and it is refused while the database is `UPDATING`. When the entry sets `python_version`, that value is the image Python, and another interpreter is the same update. A build that finishes and fails uses `pxt db build-image`, in [What to run after a change](#what-to-run-after-a-change). ```bash theme={"theme":{"light":"light-plus","dark":"dark-plus"}} pxt db update pxt://org:main pxt schema update app.py pxt://org:main pxt service update app.py pxt://org:main pxt service list pxt://org:main ``` `pxt db update` and `pxt service update` ask for confirmation. Without a terminal to answer, as in a notebook or a script, add `-f`. The first `pxt db update` builds the image, which takes several minutes, and returns when the database is ready. `pxt org create` already provisioned `main`. `pxt db update` uploads the project onto that database and updates its image and size. It does not insert rows and does not start app endpoints. `pxt schema update` creates the tables. It does not start the endpoints. `pxt service update` starts hosted endpoints. Do not use `pxt service run` for Cloud. That command only starts endpoints in your local terminal. `pxt service list` prints the service URL and its routes. The host looks like `https://{org}-{db}.svc.pxt.run/`. The control plane host is separate. Do not hardcode either hostname. The generated `app.py` defines a `docs` table with a computed `title_upper` column. Insert a row from Python: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} import pixeltable as pxt docs = pxt.get_table('pxt://org:main/docs') docs.insert(title='Hello', body='world') print(docs.select(docs.title, docs.title_upper).collect()) ``` The result includes `Hello` and `HELLO`. The insert computes `title_upper` on Cloud. Pixeltable prints `Connected to Pixeltable database at:` for the local database before that result. The insert still runs on the Cloud table. A local file path on a Cloud table uploads the file. The table also appears in the [Cloud dashboard](https://www.pixeltable.com/dashboard). A Cloud table path uses the URI plus `/table`, such as `pxt.get_table('pxt://org:main/docs')`. A local table uses dotted `namespace.table`, such as `pxt.get_table('my_app.docs')`. Run `pxt schema diff app.py pxt://org:main` to see pending schema changes. The same file can define `pxt.Image`, `pxt.Video`, `pxt.Audio`, or `pxt.Document` columns: [media pipelines](/use-cases/media-processing), [RAG](/use-cases/multimodal-backend). ## Get an API key API keys are optional for the CLI and Python workflow above. Create one for CI, background jobs, or a backend that calls a hosted HTTP service. If a browser frontend needs that service, have it call your backend. Keep the key out of browser code. The CLI reads the key from `PIXELTABLE_API_KEY`, or from `api_key` in the `[pixeltable]` section of the Pixeltable config file. The environment variable wins. Either one takes precedence over a `pxt login` session. [Configuration](/platform/configuration). After `pxt login`, create a key from the CLI: ```bash theme={"theme":{"light":"light-plus","dark":"dark-plus"}} pxt key create my-app ``` The CLI prints the secret once. `pxt key list` shows key names, and cannot show their secrets again. A key created without `--grant` acts as you. [Scoped service keys](/platform/cli#pxt-key) are in preview. You can also create a key under **API Keys** in the [Cloud dashboard](https://www.pixeltable.com/dashboard). ### Call the hosted service Get the service URL from `pxt service list pxt://org:main`. Put the key in your backend's environment and send it in the `X-api-key` header: ```bash theme={"theme":{"light":"light-plus","dark":"dark-plus"}} export PIXELTABLE_API_KEY='your-key' SERVICE_URL='URL from pxt service list' curl -X POST "$SERVICE_URL/docs" \ -H "X-api-key: $PIXELTABLE_API_KEY" \ -H 'Content-Type: application/json' \ -d '{"title": "Hello from HTTP", "body": "world"}' ``` Cloud checks `X-api-key` first. A request that also sends `Authorization: Bearer` is authenticated as the API key. For the TypeScript client, follow [Call from a TypeScript app server](/howto/deployment/serving#call-from-a-typescript-app-server). That client sends `X-api-key` and exports `computeTitle()` for the generated app's `/titles` route. ## Add provider secrets `PIXELTABLE_API_KEY` authenticates to Pixeltable Cloud. Provider keys, such as `OPENAI_API_KEY`, belong in Cloud **Secrets**. For a database that uses OpenAI: ```bash theme={"theme":{"light":"light-plus","dark":"dark-plus"}} pxt secret set pxt://org:main OPENAI_API_KEY=your-provider-key pxt db restart pxt://org:main pxt service restart pxt://org:main/ingest ``` The restarts let running table and service processes read the new value. You can also set secrets in the [Cloud dashboard](https://www.pixeltable.com/dashboard). [CLI secret commands](/platform/cli#pxt-secret). ## What the database runs A hosted database runs two things that come from your project, and they move independently. * **The image** is the Python environment: your dependencies, the Python version, and any system packages. `pxt db update` rebuilds it only when that environment changed, which is the slow step. That version comes from the interpreter that runs `pxt db update`, unless the hosted `[[pixeltable.database]]` entry sets `python_version`. * **The project files** are your `app.py` and whatever else the entry selects. They are uploaded on their own, without a rebuild. So editing a UDF costs an upload, while adding a line to `pyproject.toml` costs a build. Both are `pxt db update`. It works out which happened. The image gets its packages from `uv.lock` or `requirements.txt`. Without either, `pxt db update` warns, and the image has Pixeltable only. That is enough for the generated `app.py`. List anything else `app.py` imports in one of those files. `[[pixeltable.database]]` in `pixeltable.toml` declares which files are selected, what the image includes, and the CPU, memory, disk, and worker counts. See the [cloud configuration reference](/platform/cli#cloud-configuration-reference). ## What to run after a change | You changed | Run | | - | - | | A UDF body, a route, any `.py` the entry selects | `pxt db update pxt://org:main`, then `pxt service update app.py pxt://org:main` to move running services onto it | | A column or a table in `app.py` | `pxt db update pxt://org:main`, then `pxt schema update app.py pxt://org:main`, then `pxt service update app.py pxt://org:main` | | A dependency, the Python version, a system package | `pxt db update pxt://org:main`: the image rebuilds | | `cpu`, `memory_mb`, `disk_gb`, or `workers` | `pxt db update pxt://org:main`: one resize, pods restart once | | A secret | `pxt secret set pxt://org:main KEY=...`, then `pxt db restart pxt://org:main` and `pxt service restart pxt://org:main/ingest` | | Nothing, but the last build failed | `pxt db build-image pxt://org:main` forces a rebuild | | Nothing, but `pxt db update` stopped and `pxt db status` shows `UPDATING` | `pxt db update pxt://org:main` again from the same project. With no `python_version` on the hosted entry, use the same Python version | `pxt db diff pxt://org:main` prints what `update` would do without doing it. A command that reports an unsupported proxy protocol version means this Pixeltable and the database image disagree. When this Pixeltable is newer, run `pxt db build-image pxt://org:main` from the project, using the database URI from the failed command. The image installs the Pixeltable the project resolves: upgrade a lockfile pin first, and with no lockfile the build installs the current PyPI release. `pxt db restart` keeps the image the pods already run. When the database is newer, run `pip install --upgrade pixeltable`. For an older local proxy at `pxt://local:mydb`, run `pxt localproxy stop mydb`, then `pxt localproxy start mydb` from the updated Python environment. `start` alone keeps a healthy daemon running. ## Operate it ```bash theme={"theme":{"light":"light-plus","dark":"dark-plus"}} pxt db status pxt://org:main # state, resources, pods pxt db logs pxt://org:main # the database pod's log pxt service list pxt://org:main # the services running there, and their URLs pxt service logs pxt://org:main/ingest pxt db restart pxt://org:main # cycle the pods onto what they already run pxt db stop pxt://org:main # sleep: stops the pods, keeps the data pxt db start pxt://org:main # wake it again pxt db list # every hosted database you can reach ``` `pxt db delete pxt://org:db` deletes a database and its storage. The organization's default database, `main`, cannot be deleted. Full reference: [CLI](/platform/cli#cloud). ## Media on the home bucket Every Cloud database includes a managed Media Store at `pxtfs://org:main/home`. Hosted tables and services write there automatically. No `destination=`, no dest env vars. Set dest only for two cases: * **Local Pixeltable** writing into that same bucket: `pxt login` or `PIXELTABLE_API_KEY`, plus `PIXELTABLE_INPUT_MEDIA_DEST` / `PIXELTABLE_OUTPUT_MEDIA_DEST` (see [Cloud storage](/integrations/cloud-storage#pixeltable-cloud-home-bucket)). * **Your own bucket**: store `AWS_ACCESS_KEY_ID` / `AWS_SECRET_ACCESS_KEY` (or GCS / Azure names) under **Secrets** or `pxt secret`, then point those dest env vars at `s3://`, `gs://`, or `wasbs://`. A single computed column can also set `destination=`: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} import pixeltable as pxt TableModel = pxt.model_base() class Images(TableModel, name='images'): photo: pxt.Image thumbnail = pxt.Column( value=photo.resize((256, 256)), destination='s3://my-bucket/thumbnails', ) ``` Or set `db_input_media_dest` / `db_output_media_dest` on the database's `[[pixeltable.database]]` entry to change the default for inserted or computed media. Details: [Cloud storage](/integrations/cloud-storage#default-destinations). ## Local vs Cloud | | Local | Cloud | | - | - | - | | Hosted database | not needed | `pxt db update pxt://org:main` | | Tables | `pxt schema update app.py my_app` | `pxt schema update app.py pxt://org:main` | | HTTP | `pxt service update app.py my_app` | `pxt service update app.py pxt://org:main` | | Try a change | insert, `pxt dashboard`, `pxt schema diff app.py my_app` | insert from Python, `pxt schema diff app.py pxt://org:main` | | Media | local disk | `pxtfs://org:main/home` | Cloud is in Limited Beta. Talk through a Cloud deployment. # Migrate to Pixeltable Source: https://docs.pixeltable.com/howto/coming-from Pick the stack you already run. Inserting a row replaces the extra store or orchestrator. Hosted catalog: [Cloud](/cloud). This page maps the stack you already run onto Pixeltable. Defining the class does not create the table. Run `pxt schema update app.py my_app` first ([Quickstart](/overview/quick-start)); the snippets assume that already ran.
You do not need a separate vector database. The embedding index sits on the column. Insert keeps it current. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} import pixeltable as pxt import pixeltable.functions as pxtf TableModel = pxt.model_base() class Items(TableModel, name='items'): body: pxt.String __indexes__ = [ pxt.EmbeddingIndex( body, embedding=pxtf.huggingface.sentence_transformer.using( model_id='sentence-transformers/all-MiniLM-L6-v2' ), name='body_idx', ) ] ``` ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} items = pxt.get_table('my_app.items') sim = items.body.similarity(string='application file') items.order_by(sim, asc=False).limit(5).select(items.body) ``` [Embedding indexes](/platform/embedding-indexes) You do not need a separate RDBMS for application rows. Typed columns, insert, and select live in the catalog. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} import pixeltable as pxt TableModel = pxt.model_base() class Orders(TableModel, name='orders'): sku: pxt.String qty: pxt.Int note: pxt.String | None __indexes__ = [pxt.BtreeIndex(sku)] ``` ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} orders = pxt.get_table('my_app.orders') orders.insert([{'sku': 'a1', 'qty': 2, 'note': None}]) orders.where(orders.qty > 1).select(orders.sku, orders.qty).collect() ``` If another system must keep a SQL copy, export with `export_sql` (example on [Self-hosting](/howto/deployment/overview)).
You do not need StateGraph, ToolNode, or a checkpointer. After `pxt schema update`, insert `{'message': '...'}` and read `response` and `tool_output`. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} import pixeltable as pxt import pixeltable.functions as pxtf TableModel = pxt.model_base() @pxt.udf def get_weather(city: str) -> str: return f'Weather in {city}: 72°F, sunny' tools = pxt.tools(get_weather) class Assistant(TableModel, name='assistant'): message: pxt.String response = pxtf.openai.chat_completions( messages=[{'role': 'user', 'content': message}], model='gpt-4o-mini', tools=tools, ) tool_output = pxtf.openai.invoke_tools(tools, response) ``` [Tool calling cookbook](/howto/cookbooks/agents/llm-tool-calling)
You do not need a frame loop, cron, or a full re-run. Insert a video. The iterator adds a `frame` column on `Frames`; `thumb` is a computed column from that `frame`. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} import pixeltable as pxt import pixeltable.functions as pxtf TableModel = pxt.model_base() class Videos(TableModel, name='videos'): video: pxt.Video class Frames( TableModel, name='frames', base=Videos, iterator=pxtf.video.frame_iterator(Videos.video, fps=1), ): # iterator output thumb = frame.resize((256, 256)) # type: ignore[name-defined] ``` [Extract video frames](/howto/cookbooks/video/video-extract-frames) After `pxt schema update`, insert with `table.insert([...])` as in the snippets above, or POST after `pxt service update` ([Quickstart](/overview/quick-start)). Write `app.py`, run `pxt schema update`, then insert from Python or HTTP. Insert, compute, update, delete, query, uploads, background jobs. # Agentic Patterns Source: https://docs.pixeltable.com/howto/cookbooks/agents/agentic-patterns Open in Kaggle  Open in Colab  Download Notebook This documentation page is also available as an interactive notebook. You can launch the notebook in Kaggle or Colab, or download it for use with an IDE or local Jupyter installation, by clicking one of the above links. This notebook uses `pxt.create_table()` so you can run cells without a project. An app puts the same columns on a `TableModel` in `app.py` and creates the tables with `pxt schema update`. Two popular taxonomies describe the building blocks of agentic AI systems: * **Cognitive / reasoning-oriented** (Taxonomy 1): Reflection, Tool Use, ReAct, Planning, Multi-Agent — asks *“how does the agent think?”* * **Architectural / system-design-oriented** (Taxonomy 2): Prompt Chaining, Routing, Parallelization, Tool Use, Evaluator-Optimizer, Orchestrator-Worker — asks *“how do you wire LLM calls together?”* (See [OpenAI’s Practical Guide to Building Agents](https://cdn.openai.com/business-guides-and-resources/a-practical-guide-to-building-agents.pdf), [Anthropic’s multi-agent research system](https://www.anthropic.com/engineering/multi-agent-research-system), and [Pydantic AI’s multi-agent delegation](https://ai.pydantic.dev/multi-agent-applications/#agent-delegation).) Mapping them against each other reveals:
The cleanest framing: **six architectural patterns** that describe how you structure LLM calls, plus **two cross-cutting reasoning strategies** (ReAct and Planning) that can be layered inside any of them. This cookbook implements all eight in Pixeltable, where your agent *is* a table:
## Setup ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} %pip install -qU pixeltable openai ``` ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} import getpass import os if 'OPENAI_API_KEY' not in os.environ: os.environ['OPENAI_API_KEY'] = getpass.getpass('OpenAI API Key: ') ``` ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} import pixeltable as pxt from pixeltable.functions import openai pxt.drop_dir('agentic_patterns', force=True) pxt.create_dir('agentic_patterns') ```
  Created directory 'agentic\_patterns'.
  \
## Pattern 1: Prompt Chaining Break a complex task into sequential steps, where each step’s output feeds the next. **Imperative approach:** a chain of function calls or an explicit pipeline object. **Pixeltable approach:** each step is a computed column. The engine resolves dependencies automatically.
  input → step 1 (outline) → step 2 (draft) → step 3 (polish) → output
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Create a table with a single input column chain = pxt.create_table('agentic_patterns/chain', {'topic': pxt.String}) ```
  Created table 'chain'.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Step 1: generate an outline chain.add_computed_column( outline_response=openai.chat_completions( messages=[ { 'role': 'user', 'content': 'Create a 3-point outline for a short article about: ' + chain.topic, } ], model='gpt-4o-mini', ) ) chain.add_computed_column( outline=chain.outline_response.choices[0].message.content.astype( pxt.String ) ) ```
  Added 0 column values with 0 errors in 0.00 s
  Added 0 column values with 0 errors in 0.01 s
  No rows affected.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Step 2: write a draft from the outline chain.add_computed_column( draft_response=openai.chat_completions( messages=[ { 'role': 'user', 'content': 'Write a short article (2-3 paragraphs) based on this outline:\n\n' + chain.outline, } ], model='gpt-4o-mini', ) ) chain.add_computed_column( draft=chain.draft_response.choices[0].message.content.astype( pxt.String ) ) ```
  Added 0 column values with 0 errors in 0.01 s
  Added 0 column values with 0 errors in 0.01 s
  No rows affected.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Step 3: polish the draft chain.add_computed_column( polish_response=openai.chat_completions( messages=[ { 'role': 'user', 'content': 'Edit this article for clarity and conciseness. ' 'Return only the improved text:\n\n' + chain.draft, } ], model='gpt-4o-mini', ) ) chain.add_computed_column( final_article=chain.polish_response.choices[0].message.content.astype( pxt.String ) ) ```
  Added 0 column values with 0 errors in 0.01 s
  Added 0 column values with 0 errors in 0.01 s
  No rows affected.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Insert a topic — all three steps execute automatically chain.insert([{'topic': 'the benefits of declarative AI pipelines'}]) chain.select( chain.topic, chain.outline, chain.draft, chain.final_article ).collect() ```
  Inserted 1 row with 0 errors in 14.58 s (0.07 rows/s)
Every intermediate result (`outline`, `draft`, `final_article`) is persisted in the table. Inserting another topic reuses the same pipeline — no code changes needed. If the same topic is inserted again, cached results are returned instantly. ## Pattern 2: Routing Classify an input and route it to a specialized handler. This is the agent equivalent of a switch/case statement. **Imperative approach:** a triage agent that performs handoffs to specialized agents. **Pixeltable approach:** one computed column classifies; a UDF selects the prompt; a second LLM call generates the response.
  input → classify intent → select specialized prompt → generate response
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} router = pxt.create_table( 'agentic_patterns/router', {'query': pxt.String} ) ```
  Created table 'router'.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Step 1: classify the query intent router.add_computed_column( classify_response=openai.chat_completions( messages=[ { 'role': 'user', 'content': 'Classify this customer query into exactly one category: ' 'technical, billing, or general. Reply with the single word only.\n\n' 'Query: ' + router.query, } ], model='gpt-4o-mini', ) ) router.add_computed_column( intent=router.classify_response.choices[0].message.content.astype( pxt.String ) ) ```
  Added 0 column values with 0 errors in 0.01 s
  Added 0 column values with 0 errors in 0.01 s
  No rows affected.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Step 2: route to a specialized system prompt based on the classification @pxt.udf def route_prompt(intent: str, query: str) -> list[dict]: """Select a system prompt based on the classified intent.""" system_prompts = { 'technical': 'You are a senior technical support engineer. ' 'Provide precise, step-by-step troubleshooting guidance.', 'billing': 'You are a billing specialist. ' 'Be empathetic and clear about charges, refunds, and payment options.', 'general': 'You are a friendly customer service representative. ' 'Answer helpfully and concisely.', } # Default to general if classification is unexpected system = system_prompts.get( intent.strip().lower(), system_prompts['general'] ) return [ {'role': 'system', 'content': system}, {'role': 'user', 'content': query}, ] router.add_computed_column( routed_messages=route_prompt(router.intent, router.query) ) ```
  Added 0 column values with 0 errors in 0.01 s
  No rows affected.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Step 3: generate the specialized response router.add_computed_column( response_raw=openai.chat_completions( messages=router.routed_messages, model='gpt-4o-mini' ) ) router.add_computed_column( response=router.response_raw.choices[0].message.content.astype( pxt.String ) ) ```
  Added 0 column values with 0 errors in 0.00 s
  Added 0 column values with 0 errors in 0.01 s
  No rows affected.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Insert queries spanning different intents router.insert( [ { 'query': 'My API calls are returning 429 errors since this morning' }, {'query': 'I was charged twice for my subscription last month'}, {'query': 'What programming languages do you support?'}, ] ) router.select(router.query, router.intent, router.response).collect() ```
  Inserted 3 rows with 0 errors in 6.93 s (0.43 rows/s)
Each query was classified and then handled by a specialized system prompt. The `intent` column is inspectable for every row, making it easy to audit routing decisions. ## Pattern 3: Parallelization Run multiple independent LLM calls on the same input simultaneously, then combine the results. **Imperative approach:** `asyncio.gather` or thread pools. **Pixeltable approach:** add independent computed columns. The engine parallelizes them automatically because they share no dependencies.
           ┌→ sentiment  ─┐
  input  ──┼→ entities   ──┼→ merge → combined output
           └→ summary    ─┘
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} parallel = pxt.create_table( 'agentic_patterns/parallel', {'text': pxt.String} ) ```
  Created table 'parallel'.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Three independent LLM calls — Pixeltable runs them in parallel automatically parallel.add_computed_column( sentiment_raw=openai.chat_completions( messages=[ { 'role': 'user', 'content': 'Analyze the sentiment of this text. ' 'Reply with: positive, negative, or neutral.\n\n' + parallel.text, } ], model='gpt-4o-mini', ) ) parallel.add_computed_column( sentiment=parallel.sentiment_raw.choices[0].message.content.astype( pxt.String ) ) parallel.add_computed_column( entities_raw=openai.chat_completions( messages=[ { 'role': 'user', 'content': 'Extract all named entities (people, companies, locations) ' 'from this text. Return a comma-separated list.\n\n' + parallel.text, } ], model='gpt-4o-mini', ) ) parallel.add_computed_column( entities=parallel.entities_raw.choices[0].message.content.astype( pxt.String ) ) parallel.add_computed_column( summary_raw=openai.chat_completions( messages=[ { 'role': 'user', 'content': 'Summarize this text in one sentence.\n\n' + parallel.text, } ], model='gpt-4o-mini', ) ) parallel.add_computed_column( summary=parallel.summary_raw.choices[0].message.content.astype( pxt.String ) ) ```
  Added 0 column values with 0 errors in 0.01 s
  Added 0 column values with 0 errors in 0.01 s
  Added 0 column values with 0 errors in 0.00 s
  Added 0 column values with 0 errors in 0.01 s
  Added 0 column values with 0 errors in 0.00 s
  Added 0 column values with 0 errors in 0.01 s
  No rows affected.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Merge the parallel results into a single structured report @pxt.udf def merge_analysis(sentiment: str, entities: str, summary: str) -> dict: """Combine parallel analysis results into one report.""" return { 'sentiment': sentiment.strip(), 'entities': entities.strip(), 'summary': summary.strip(), } parallel.add_computed_column( report=merge_analysis( parallel.sentiment, parallel.entities, parallel.summary ) ) ```
  Added 0 column values with 0 errors in 0.01 s
  No rows affected.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} parallel.insert( [ { 'text': 'Apple announced record quarterly revenue of $124 billion, ' 'driven by strong iPhone sales in Europe and Asia. CEO Tim Cook ' "expressed optimism about the company's AI initiatives, while " 'some analysts remain cautious about increased R&D spending.' } ] ) parallel.select( parallel.text, parallel.sentiment, parallel.entities, parallel.summary ).collect() ``` The three LLM calls (`sentiment`, `entities`, `summary`) have no dependency on each other, so Pixeltable dispatches them concurrently. The `merge_analysis` UDF waits for all three before combining the results. No async code required. ## Pattern 4: Tool Use Give an LLM access to external functions it can call to gather information or take action. **Imperative approach:** `@function_tool` decorator, tool loop that re-prompts until the LLM stops requesting tools. **Pixeltable approach:** `pxt.tools()` bundles UDFs into tool definitions; `invoke_tools()` executes the LLM’s choices — both as computed columns.
  input → LLM (with tools) → invoke\_tools() → results
For a deeper walkthrough including MCP servers, see [Use tool calling with LLMs](/howto/cookbooks/agents/llm-tool-calling). ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Define tool functions as UDFs @pxt.udf def get_weather(city: str) -> str: """Get the current weather for a city.""" weather_data = { 'new york': 'Sunny, 72F', 'london': 'Cloudy, 58F', 'tokyo': 'Rainy, 65F', 'paris': 'Partly cloudy, 68F', } return weather_data.get( city.lower(), f'Weather data not available for {city}' ) @pxt.udf def get_stock_price(symbol: str) -> str: """Get the current stock price for a ticker symbol.""" prices = {'AAPL': '$178.50', 'GOOGL': '$141.25', 'MSFT': '$378.90'} return prices.get(symbol.upper(), f'Price not available for {symbol}') # Bundle into a Tools object tools = pxt.tools(get_weather, get_stock_price) ``` ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Create the tool-calling pipeline tool_agent = pxt.create_table( 'agentic_patterns/tool_agent', {'query': pxt.String} ) # LLM decides which tool(s) to call tool_agent.add_computed_column( response=openai.chat_completions( messages=[{'role': 'user', 'content': tool_agent.query}], model='gpt-4o-mini', tools=tools, ) ) # Execute the tool calls automatically tool_agent.add_computed_column( tool_output=openai.invoke_tools(tools, tool_agent.response) ) ```
  Created table 'tool\_agent'.
  Added 0 column values with 0 errors in 0.01 s
  Added 0 column values with 0 errors in 0.01 s
  No rows affected.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} tool_agent.insert( [ {'query': "What's the weather in Tokyo?"}, {'query': "What's Apple's stock price?"}, { 'query': "What's the weather in Paris and Microsoft's stock price?" }, ] ) for row in tool_agent.select( tool_agent.query, tool_agent.tool_output ).collect(): print(f'Query: {row["query"]}') for tool_name, results in (row['tool_output'] or {}).items(): if results: print(f' -> {tool_name}: {results}') print() ``` The LLM chose which tools to invoke (including multiple tools for the last query). `invoke_tools()` executed them and stored results. The full LLM response is also persisted in the `response` column for debugging. ## Pattern 5: Evaluator-Optimizer One LLM generates output, a second LLM evaluates it, and the results are used to decide whether to refine. This is the architectural cousin of the *Reflection* pattern from Taxonomy 1 — an agent critiques its own output and iteratively improves it. **Imperative approach:** a while-loop that re-prompts until a quality threshold is met (see [Pixelagent’s reflection example](https://github.com/pixeltable/pixelagent/tree/main/examples/reflection)). **Pixeltable approach:** chained computed columns — generate, evaluate, then conditionally refine. The evaluation score is stored alongside the content for analysis.
  input → generate → evaluate (score + feedback) → refine if needed → output
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} evaluator = pxt.create_table( 'agentic_patterns/evaluator', {'product_brief': pxt.String} ) ```
  Created table 'evaluator'.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Step 1: generate initial marketing copy evaluator.add_computed_column( gen_response=openai.chat_completions( messages=[ { 'role': 'user', 'content': 'Write a short marketing tagline (one sentence) for this product:\n\n' + evaluator.product_brief, } ], model='gpt-4o-mini', ) ) evaluator.add_computed_column( first_draft=evaluator.gen_response.choices[0].message.content.astype( pxt.String ) ) ```
  Added 0 column values with 0 errors in 0.00 s
  Added 0 column values with 0 errors in 0.01 s
  No rows affected.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Step 2: evaluate the draft with an LLM-as-judge evaluator.add_computed_column( eval_response=openai.chat_completions( messages=[ { 'role': 'user', 'content': 'Rate this marketing tagline on a scale of 1-10 for clarity, ' 'creativity, and persuasiveness. Then provide one sentence of feedback ' 'for improvement.\n\n' 'Tagline: ' + evaluator.first_draft + '\n\n' 'Reply in this exact format:\n' 'Score: \nFeedback: ', } ], model='gpt-4o-mini', ) ) evaluator.add_computed_column( evaluation=evaluator.eval_response.choices[0].message.content.astype( pxt.String ) ) ```
  Added 0 column values with 0 errors in 0.01 s
  Added 0 column values with 0 errors in 0.01 s
  No rows affected.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Step 3: refine using the feedback evaluator.add_computed_column( refine_response=openai.chat_completions( messages=[ { 'role': 'user', 'content': 'Improve this marketing tagline based on the feedback below. ' 'Return only the improved tagline.\n\n' 'Original: ' + evaluator.first_draft + '\n\n' 'Feedback: ' + evaluator.evaluation, } ], model='gpt-4o-mini', ) ) evaluator.add_computed_column( refined=evaluator.refine_response.choices[0].message.content.astype( pxt.String ) ) ```
  Added 0 column values with 0 errors in 0.01 s
  Added 0 column values with 0 errors in 0.01 s
  No rows affected.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} evaluator.insert( [ { 'product_brief': 'A noise-canceling headphone designed for open-plan offices, ' 'with 30-hour battery life and a built-in microphone for calls.' }, { 'product_brief': 'An AI-powered code review tool that catches bugs, suggests ' "improvements, and learns your team's coding style over time." }, ] ) evaluator.select( evaluator.product_brief, evaluator.first_draft, evaluator.evaluation, evaluator.refined, ).collect() ```
  Inserted 2 rows with 0 errors in 2.95 s (0.68 rows/s)
Both the first draft and the refined version are stored side-by-side with the evaluation. This makes it straightforward to compare outputs, audit the judge’s reasoning, or filter rows where the score fell below a threshold. ## Pattern 6: Orchestrator-Worker A central agent decomposes a task, delegates sub-tasks to specialized worker agents, and synthesizes the results. This is the architectural cousin of the *Multi-Agent* pattern from Taxonomy 1, and the same structure Anthropic uses in their [multi-agent research system](https://www.anthropic.com/engineering/multi-agent-research-system) — a lead agent coordinates parallel subagents, each with their own context and tools. **Imperative approach:** an orchestrator agent class that spawns worker agent instances and collects their outputs. **Pixeltable approach:** each worker is a table with computed columns, wrapped as a callable function via `pxt.udf(table, return_value=...)`. The orchestrator table calls these functions as computed columns.
  input → decompose → worker A (summarizer)  ─┐
                    → worker B (fact-checker) ─┼→ synthesize → output
For more on table UDFs, see [Use a table pipeline as a reusable function](/howto/cookbooks/agents/pattern-table-as-udf). ### Build worker agents as tables ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Worker A: summarizer summarizer_tbl = pxt.create_table( 'agentic_patterns/summarizer', {'text': pxt.String} ) summarizer_tbl.add_computed_column( response=openai.chat_completions( messages=[ { 'role': 'user', 'content': 'Summarize this text in 2-3 sentences:\n\n' + summarizer_tbl.text, } ], model='gpt-4o-mini', ) ) summarizer_tbl.add_computed_column( summary=summarizer_tbl.response.choices[0].message.content.astype( pxt.String ) ) # Wrap as a callable function summarize = pxt.udf(summarizer_tbl, return_value=summarizer_tbl.summary) ```
  Created table 'summarizer'.
  Added 0 column values with 0 errors in 0.10 s
  Added 0 column values with 0 errors in 0.06 s
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Worker B: fact-checker checker_tbl = pxt.create_table( 'agentic_patterns/checker', {'claim': pxt.String} ) checker_tbl.add_computed_column( response=openai.chat_completions( messages=[ { 'role': 'user', 'content': 'Assess whether this claim is plausible. ' 'Reply with: PLAUSIBLE or DUBIOUS, followed by a one-sentence explanation.\n\n' 'Claim: ' + checker_tbl.claim, } ], model='gpt-4o-mini', ) ) checker_tbl.add_computed_column( assessment=checker_tbl.response.choices[0].message.content.astype( pxt.String ) ) # Wrap as a callable function fact_check = pxt.udf(checker_tbl, return_value=checker_tbl.assessment) ```
  Created table 'checker'.
  Added 0 column values with 0 errors in 0.01 s
  Added 0 column values with 0 errors in 0.02 s
### Build the orchestrator ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Orchestrator table: delegates to workers, then synthesizes orchestrator = pxt.create_table( 'agentic_patterns/orchestrator', {'article': pxt.String} ) # Dispatch to worker A (summarizer) and worker B (fact-checker) in parallel orchestrator.add_computed_column( summary=summarize(text=orchestrator.article) ) orchestrator.add_computed_column( fact_check_result=fact_check(claim=orchestrator.article) ) ```
  Created table 'orchestrator'.
  Added 0 column values with 0 errors in 0.01 s
  Added 0 column values with 0 errors in 0.01 s
  No rows affected.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Synthesize worker outputs into a final briefing orchestrator.add_computed_column( synth_response=openai.chat_completions( messages=[ { 'role': 'user', 'content': 'Based on the summary and fact-check below, write a brief ' 'editorial note (2-3 sentences) about this article.\n\n' 'Summary: ' + orchestrator.summary + '\n\n' 'Fact-check: ' + orchestrator.fact_check_result, } ], model='gpt-4o-mini', ) ) orchestrator.add_computed_column( briefing=orchestrator.synth_response.choices[ 0 ].message.content.astype(pxt.String) ) ```
  Added 0 column values with 0 errors in 0.02 s
  Added 0 column values with 0 errors in 0.01 s
  No rows affected.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} orchestrator.insert( [ { 'article': 'A recent study published in Nature found that global sea levels ' 'rose by 4.5 mm per year over the last decade, nearly double the rate observed ' 'in the 1990s. Researchers attribute the acceleration primarily to ice sheet ' 'loss in Greenland and Antarctica, compounded by thermal expansion of ocean ' 'water. The findings suggest coastal cities may face significant flooding risks ' 'by 2050 without aggressive mitigation strategies.' } ] ) orchestrator.select( orchestrator.summary, orchestrator.fact_check_result, orchestrator.briefing, ).collect() ```
  Inserted 1 row with 0 errors in 4.69 s (0.21 rows/s)
The orchestrator table called two independent worker pipelines (`summarize` and `fact_check`), each backed by their own table with full intermediate-result persistence. The synthesis step consumed both outputs to produce the final briefing. Adding a new worker (e.g., a tone analyzer) requires only creating another table, wrapping it with `pxt.udf()`, and adding one more computed column to the orchestrator. ## Strategy A: ReAct ReAct is not a wiring pattern — it is a **reasoning strategy** that can be applied inside any of the six patterns above. The agent alternates between reasoning about the next step and acting on it (typically via tools), observing the result before deciding what to do next. **Imperative approach:** a while-loop that parses the LLM’s THOUGHT/ACTION output, calls tools, and feeds observations back (see [Pixelagent’s ReAct example](https://github.com/pixeltable/pixelagent/tree/main/examples/planning)). **Pixeltable approach:** the reasoning loop lives in a UDF that inserts rows into a tool-calling table and reads back results. The table stores every thought-action-observation triple for full observability.
  question → \[THOUGHT → ACTION → OBSERVATION] × N → final answer
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} import re # Define a tool for the ReAct agent @pxt.udf def lookup_population(country: str) -> str: """Look up the approximate population of a country.""" populations = { 'united states': '331 million', 'china': '1.4 billion', 'india': '1.4 billion', 'germany': '84 million', 'brazil': '214 million', 'japan': '125 million', } return populations.get( country.lower(), f'Population data not available for {country}' ) react_tools = pxt.tools(lookup_population) ``` ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Build a tool-calling table that the ReAct loop will insert into react_steps = pxt.create_table( 'agentic_patterns/react_steps', {'step': pxt.Int, 'prompt': pxt.String, 'system_prompt': pxt.String}, ) react_steps.add_computed_column( response=openai.chat_completions( messages=[ {'role': 'system', 'content': react_steps.system_prompt}, {'role': 'user', 'content': react_steps.prompt}, ], model='gpt-4o-mini', tools=react_tools, ) ) react_steps.add_computed_column( answer=react_steps.response.choices[0].message.content.astype( pxt.String | None ) ) react_steps.add_computed_column( tool_output=openai.invoke_tools(react_tools, react_steps.response) ) ```
  Created table 'react\_steps'.
  Added 0 column values with 0 errors in 0.00 s
  Added 0 column values with 0 errors in 0.01 s
  Added 0 column values with 0 errors in 0.00 s
  No rows affected.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # The ReAct loop: reason → act → observe, repeated until done REACT_SYSTEM = ( "You are a research assistant. Answer the user's question step by step.\n" 'Available tools: lookup_population\n\n' 'On each turn, respond in this exact format:\n' 'THOUGHT: \n' 'ACTION: \n\n' 'When ACTION is FINAL, include your final answer after it.\n' 'Current step: {step} of {max_steps}.' ) question = 'Which country has a larger population, Brazil or Germany?' max_steps = 4 history = [] for step in range(1, max_steps + 1): # Build prompt with accumulated observations prompt = question if history: prompt += '\n\nPrevious observations:\n' + '\n'.join(history) system = REACT_SYSTEM.format(step=step, max_steps=max_steps) react_steps.insert( [{'step': step, 'prompt': prompt, 'system_prompt': system}] ) # Read back the result for this step row = ( react_steps.where(react_steps.step == step) .select(react_steps.answer, react_steps.tool_output) .collect() ) answer_text = row['answer'][0] or '' tool_out = row['tool_output'][0] # Record observation from tool output (if any) if tool_out: history.append(f'Step {step} tool result: {tool_out}') # Check if the agent decided to finalize if 'FINAL' in answer_text.upper(): break print(f'Completed in {step} steps') for row in react_steps.select( react_steps.step, react_steps.answer, react_steps.tool_output ).collect(): print(f'Step {row["step"]}:') if row['answer']: print(f' {row["answer"][:200]}') for tool_name, results in (row['tool_output'] or {}).items(): if results: print(f' -> {tool_name}: {results}') print() ``` Every thought, action, and observation is persisted as a row in the `react_steps` table. The loop itself is plain Python; the LLM calls and tool execution happen declaratively via computed columns. This makes the reasoning trace fully queryable after the fact — useful for debugging or evaluation. ## Strategy B: Planning Planning is the second cross-cutting reasoning strategy. Instead of acting step-by-step (ReAct), the agent first generates a complete plan, then executes each step. This is especially effective for complex tasks where the structure of the solution can be determined upfront. **Imperative approach:** an LLM generates a plan as structured JSON, then a loop executes each step (see [Pixelagent’s planning example](https://github.com/pixeltable/pixelagent/tree/main/examples/planning)). **Pixeltable approach:** a prompt-chaining pipeline where the first column generates the plan and a UDF parses it into executable steps. Each step then feeds into subsequent computed columns.
  question → generate plan → execute step 1 → execute step 2 → ... → synthesize
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} import json as json_mod planner = pxt.create_table( 'agentic_patterns/planner', {'question': pxt.String} ) # Step 1: generate a plan as structured JSON planner.add_computed_column( plan_response=openai.chat_completions( messages=[ { 'role': 'user', 'content': 'Break this question into 2-3 research steps. ' 'Return ONLY a JSON object like {"steps": ["sub-question 1", "sub-question 2"]}. ' 'No other text.\n\n' 'Question: ' + planner.question, } ], model='gpt-4o-mini', ) ) planner.add_computed_column( plan_text=planner.plan_response.choices[0].message.content.astype( pxt.String ) ) ```
  Created table 'planner'.
  Added 0 column values with 0 errors in 0.00 s
  Added 0 column values with 0 errors in 0.01 s
  No rows affected.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Step 2: parse the plan and execute each sub-question, then synthesize @pxt.udf def execute_plan(plan_json: str, original_question: str) -> list[dict]: """Parse the plan JSON and return structured sub-questions.""" try: data = json_mod.loads(plan_json) # Handle both {"steps": [...]} and direct [...] steps = ( data if isinstance(data, list) else data.get('steps', data.get('questions', [])) ) return [ {'step': i + 1, 'sub_question': q} for i, q in enumerate(steps) ] except (json_mod.JSONDecodeError, TypeError): return [{'step': 1, 'sub_question': original_question}] planner.add_computed_column( plan_steps=execute_plan(planner.plan_text, planner.question) ) ```
  Added 0 column values with 0 errors in 0.01 s
  No rows affected.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Step 3: execute the plan — answer each sub-question, then synthesize @pxt.udf def format_plan_for_execution( plan_steps: list[dict], original_question: str ) -> str: """Format the plan steps into a single execution prompt.""" step_list = '\n'.join( f'{s["step"]}. {s["sub_question"]}' for s in plan_steps ) return ( f'Answer each of these research sub-questions briefly, ' f'then provide a final synthesis that answers the original question.\n\n' f'Original question: {original_question}\n\n' f'Sub-questions:\n{step_list}' ) planner.add_computed_column( exec_prompt=format_plan_for_execution( planner.plan_steps, planner.question ) ) planner.add_computed_column( exec_response=openai.chat_completions( messages=[{'role': 'user', 'content': planner.exec_prompt}], model='gpt-4o-mini', ) ) planner.add_computed_column( final_answer=planner.exec_response.choices[0].message.content.astype( pxt.String ) ) ```
  Added 0 column values with 0 errors in 0.01 s
  Added 0 column values with 0 errors in 0.01 s
  Added 0 column values with 0 errors in 0.01 s
  No rows affected.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} planner.insert( [ { 'question': 'What are the economic and environmental trade-offs of electric vehicles vs hydrogen fuel cells?' } ] ) row = planner.select( planner.question, planner.plan_text, planner.final_answer ).collect() print('Plan:', row['plan_text'][0]) print() print('Answer:', row['final_answer'][0][:500]) ``` The plan (stored in `plan_steps`) is fully inspectable. The execution step answers all sub-questions in a single LLM call, but this could also use parallelization (Pattern 3) to answer each sub-question independently and merge the results. Planning and ReAct compose naturally with any of the six architectural patterns. ## Choosing a Pattern ### Six architectural patterns
### Two cross-cutting reasoning strategies
Patterns compose naturally. An orchestrator-worker system might use routing in the orchestrator, tool use within a worker, and ReAct reasoning inside the tool-calling loop. Because each pattern is just a set of computed columns on a table, combining them requires no special glue code. ## See Also **Pixeltable cookbooks:** * [Use tool calling with LLMs](/howto/cookbooks/agents/llm-tool-calling) — deep dive into `pxt.tools()`, `invoke_tools()`, and MCP server integration * [Build an agent with persistent memory](/howto/cookbooks/agents/pattern-agent-memory) — embedding indexes for semantic memory recall * [Build a RAG pipeline](/howto/cookbooks/agents/pattern-rag-pipeline) — document chunking, embedding, and retrieval-augmented generation * [Look up structured data with retrieval UDFs](/howto/cookbooks/agents/pattern-data-lookup) — `pxt.retrieval_udf()` for key-based lookups * [Use a table pipeline as a reusable function](/howto/cookbooks/agents/pattern-table-as-udf) — `pxt.udf(table)` explained in depth **Pixelagent examples** (imperative implementations of the same patterns): * [Reflection loop](https://github.com/pixeltable/pixelagent/tree/main/examples/reflection) — main agent + critic agent with iterative refinement * [ReAct / Planning](https://github.com/pixeltable/pixelagent/tree/main/examples/planning) — step-by-step reasoning with tool calls * [Tool calling](https://github.com/pixeltable/pixelagent/tree/main/examples/tool-calling) — OpenAI, Anthropic, and Bedrock tool integration * [Memory](https://github.com/pixeltable/pixelagent/tree/main/examples/memory) — persistent and semantic memory management **External references:** * [OpenAI’s Practical Guide to Building Agents](https://cdn.openai.com/business-guides-and-resources/a-practical-guide-to-building-agents.pdf) — the six architectural patterns * [Anthropic: How we built our multi-agent research system](https://www.anthropic.com/engineering/multi-agent-research-system) — orchestrator-worker at scale * [Pydantic AI: Multi-agent applications](https://ai.pydantic.dev/multi-agent-applications/#agent-delegation) — agent delegation patterns # Use tool calling and MCP servers with LLMs Source: https://docs.pixeltable.com/howto/cookbooks/agents/llm-tool-calling Open in Kaggle  Open in Colab  Download Notebook This documentation page is also available as an interactive notebook. You can launch the notebook in Kaggle or Colab, or download it for use with an IDE or local Jupyter installation, by clicking one of the above links. Enable LLMs to call functions and tools, then execute the results automatically. ## Problem You want an LLM to decide which functions to call based on user queries—for agents, chatbots, or automated workflows.
## Solution **What’s in this recipe:** * Define tools as Python functions * Let LLMs decide which tool to call * Automatically execute tool calls with `invoke_tools` * Use MCP servers to load external tools You define tools with JSON schemas, pass them to the LLM, and use `invoke_tools` to execute the function calls. ### Setup ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} %pip install -qU pixeltable openai mcp ``` ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} import getpass import os if 'OPENAI_API_KEY' not in os.environ: os.environ['OPENAI_API_KEY'] = getpass.getpass('OpenAI API Key: ') ``` ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} import pixeltable as pxt from pixeltable.functions import openai # Create a fresh directory pxt.drop_dir('tools_demo', force=True) pxt.create_dir('tools_demo') ```
  Connected to Pixeltable database at: postgresql+psycopg://postgres:@/pixeltable?host=/Users/pjlb/.pixeltable/pgdata
  Created directory 'tools\_demo'.
  \
### Define tools as UDFs ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Define tool functions as Pixeltable UDFs @pxt.udf def get_weather(city: str) -> str: """Get the current weather for a city.""" # In production, call a real weather API weather_data = { 'new york': 'Sunny, 72°F', 'london': 'Cloudy, 58°F', 'tokyo': 'Rainy, 65°F', 'paris': 'Partly cloudy, 68°F', } return weather_data.get( city.lower(), f'Weather data not available for {city}' ) @pxt.udf def get_stock_price(symbol: str) -> str: """Get the current stock price for a symbol.""" # In production, call a real stock API prices = { 'AAPL': '$178.50', 'GOOGL': '$141.25', 'MSFT': '$378.90', 'AMZN': '$185.30', } return prices.get(symbol.upper(), f'Price not available for {symbol}') ``` ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Create a Tools object with our functions tools = pxt.tools(get_weather, get_stock_price) ``` ### Create tool-calling pipeline ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Create table for queries queries = pxt.create_table('tools_demo/queries', {'query': pxt.String}) ```
  Created table 'queries'.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Add LLM call with tools queries.add_computed_column( response=openai.chat_completions( messages=[{'role': 'user', 'content': queries.query}], model='gpt-4o-mini', tools=tools, # Pass tools to the LLM ) ) ```
  Added 0 column values with 0 errors in 0.00 s
  No rows affected.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Automatically execute tool calls and get results queries.add_computed_column( tool_results=openai.invoke_tools(tools, queries.response) ) ```
  Added 0 column values with 0 errors in 0.01 s
  No rows affected.
### Run tool-enabled queries ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Insert queries that require tool calls sample_queries = [ {'query': "What's the weather in Tokyo?"}, {'query': "What's the stock price of Apple?"}, { 'query': "What's the weather in Paris and the price of Microsoft stock?" }, ] queries.insert(sample_queries) ```
  Inserted 3 rows with 0 errors in 4.16 s (0.72 rows/s)
  3 rows inserted.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # View results queries.select(queries.query, queries.tool_results).collect() ```
## Using MCP Servers as Tools The [Model Context Protocol (MCP)](https://modelcontextprotocol.io/) is an open protocol that standardizes how applications provide context to LLMs. Pixeltable can connect to MCP servers and use their exposed tools as UDFs. ### Why MCP?
### Create an MCP Server First, create an MCP server with tools you want to expose. Save this as `mcp_server.py`: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} from mcp.server.fastmcp import FastMCP mcp = FastMCP('PixeltableDemo', stateless_http=True) @mcp.tool() def calculate_discount(price: float, discount_percent: float) -> float: """Calculate the discounted price.""" return price * (1 - discount_percent / 100) @mcp.tool() def check_inventory(product_id: str) -> str: """Check inventory status for a product.""" # In production, query your inventory database inventory = { 'SKU001': 'In stock (42 units)', 'SKU002': 'Low stock (3 units)', 'SKU003': 'Out of stock', } return inventory.get(product_id, f'Unknown product: {product_id}') if __name__ == '__main__': mcp.run(transport='streamable-http') ``` Run the server: `python mcp_server.py` (it will listen on `http://localhost:8000/mcp`) ### Connect to MCP Server and Use Tools ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Connect to the MCP server using pxt.mcp_udfs() # This creates a Pixeltable UDF for each tool exposed by the server # See: https://docs.pixeltable.com/platform/udfs-in-pixeltable mcp_tools = pxt.mcp_udfs('https://docs.pixeltable.com/mcp') # View available tools - each is now a callable Pixeltable function for tool in mcp_tools: print(f'- {tool.name}: {tool.comment()}') ```
  - SearchPixeltableDocumentation: Search across the Pixeltable Documentation knowledge base to find relevant information, code examples, API references, and guides. Use this tool when you need to answer questions about Pixeltable Documentation, find specific documentation, understand how features work, or locate implementation details. The search returns contextual content with titles and direct links to the documentation pages.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Bundle MCP tools for LLM use mcp_toolset = pxt.tools(*mcp_tools) # Create a table with MCP tool-calling pipeline mcp_queries = pxt.create_table( 'tools_demo/mcp_queries', {'query': pxt.String} ) # Add LLM call with MCP tools mcp_queries.add_computed_column( response=openai.chat_completions( messages=[{'role': 'user', 'content': mcp_queries.query}], model='gpt-4o-mini', tools=mcp_toolset, ) ) # Execute MCP tool calls mcp_queries.add_computed_column( tool_results=openai.invoke_tools(mcp_toolset, mcp_queries.response) ) # View the schema - note that mcp_toolset is stored as persistent metadata # Every subsequent insert will use these same tools automatically mcp_queries.describe() ```
  Created table 'mcp\_queries'.
  Added 0 column values with 0 errors in 0.00 s
  Added 0 column values with 0 errors in 0.01 s
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Test with e-commerce queries mcp_queries.insert( [ {'query': 'What is Pixeltable?'}, {'query': 'How to use OpenAI in Pixeltable?'}, ] ) mcp_queries.select(mcp_queries.query, mcp_queries.tool_results).collect() ```
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Extract the search result with a named column mcp_queries.select( search_result=mcp_queries.tool_results[ 'SearchPixeltableDocumentation' ][0] ).collect() ```
## Explanation **Tool calling flow:**
  Query → LLM decides tool → invoke\_tools executes → Results
**Key components:**
**MCP integration:**
  MCP Server → pxt.mcp\_udfs() → pxt.tools() → LLM tool calling
MCP servers expose tools via a standardized protocol. Pixeltable’s `mcp_udfs()` connects to any MCP server and returns the tools as callable UDFs that can be bundled with `pxt.tools()` for LLM use. **Supported providers:**
## See also * [Build a RAG pipeline](/howto/cookbooks/agents/pattern-rag-pipeline) - Retrieval-augmented generation * [Run local LLMs](/howto/providers/working-with-ollama) - Local model inference * [Multimodal MCP Servers](/use-cases/agentic-workflows) - Pixeltable’s MCP server collection * [Custom Functions](/platform/udfs-in-pixeltable) - More about UDFs and MCP integration # Build an agent with memory Source: https://docs.pixeltable.com/howto/cookbooks/agents/pattern-agent-memory Open in Kaggle  Open in Colab  Download Notebook This documentation page is also available as an interactive notebook. You can launch the notebook in Kaggle or Colab, or download it for use with an IDE or local Jupyter installation, by clicking one of the above links. Create an AI agent that remembers important information across conversations. ## Problem You want to build an AI agent that can store and recall important information—user preferences, key facts, or context from previous conversations.
## Solution **What’s in this recipe:** * Store memories with embeddings for semantic search * Retrieve relevant memories based on conversation context * Use `@pxt.query` for retrieval functions This pattern is inspired by [Pixelbot](https://github.com/pixeltable/pixelbot) and [Pixelmemory](https://github.com/pixeltable/pixelmemory). ### Setup ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} %pip install -qU pixeltable openai ``` ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} import getpass import os from datetime import datetime if 'OPENAI_API_KEY' not in os.environ: os.environ['OPENAI_API_KEY'] = getpass.getpass('OpenAI API Key: ') ``` ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} import pixeltable as pxt from pixeltable.functions.openai import chat_completions, embeddings # Create a fresh directory pxt.drop_dir('agent_demo', force=True) pxt.create_dir('agent_demo') ```
  Created directory 'agent\_demo'.
  \
### Create memory bank ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Create memory bank table memories = pxt.create_table( 'agent_demo/memories', { 'content': pxt.String, # The memory content 'category': pxt.String, # Optional category (preference, fact, etc.) 'created_at': pxt.Timestamp, # When the memory was stored }, ) ```
  Created table 'memories'.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Add embedding index for semantic search on content memories.add_embedding_index( column='content', string_embed=embeddings.using(model='text-embedding-3-small'), ) ``` ### Define retrieval function ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Define a query function to retrieve relevant memories @pxt.query def recall_memories(context: str, top_k: int = 3): """Retrieve memories relevant to the current context.""" sim = memories.content.similarity(string=context) return ( memories.where(sim > 0.5) .order_by(sim, asc=False) .limit(top_k) .select(content=memories.content, category=memories.category) ) ``` ### Store some memories ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Store some initial memories initial_memories = [ { 'content': 'User prefers Python for data analysis', 'category': 'preference', 'created_at': datetime.now(), }, { 'content': 'The project deadline is March 15, 2024', 'category': 'fact', 'created_at': datetime.now(), }, { 'content': 'User works at a startup in San Francisco', 'category': 'fact', 'created_at': datetime.now(), }, { 'content': 'Budget for the ML project is $50,000', 'category': 'fact', 'created_at': datetime.now(), }, { 'content': 'User prefers concise explanations over detailed ones', 'category': 'preference', 'created_at': datetime.now(), }, ] memories.insert(initial_memories) ```
  Inserting rows into \`memories\`: 5 rows \[00:00, 590.53 rows/s]
  Inserted 5 rows with 0 errors.
  5 rows inserted, 15 values computed.
### Create conversation table with memory retrieval ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Create conversation table conversations = pxt.create_table( 'agent_demo/conversations', {'user_message': pxt.String} ) ```
  Created table 'conversations'.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Add memory retrieval step conversations.add_computed_column( relevant_memories=recall_memories(conversations.user_message, top_k=3) ) ```
  Added 0 column values with 0 errors.
  No rows affected.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Build prompt with memories @pxt.udf def build_memory_prompt( user_message: str, relevant_memories: list[dict] ) -> str: memory_text = '\n'.join( [f'- {m["content"]}' for m in relevant_memories] ) return f"""You are a helpful assistant with access to the following memories about the user: {memory_text} Use these memories to personalize your response when relevant. User: {user_message} Assistant:""" conversations.add_computed_column( prompt=build_memory_prompt( conversations.user_message, conversations.relevant_memories ) ) ```
  Added 0 column values with 0 errors.
  No rows affected.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Generate response with memory context conversations.add_computed_column( response=chat_completions( messages=[{'role': 'user', 'content': conversations.prompt}], model='gpt-4o-mini', ) ) conversations.add_computed_column( assistant_reply=conversations.response.choices[0].message.content ) ```
  Added 0 column values with 0 errors.
  Added 0 column values with 0 errors.
  No rows affected.
### Chat with memory-aware agent ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Test the memory-aware agent test_messages = [ { 'user_message': 'What programming language should I use for this project?' }, {'user_message': 'When do I need to finish this?'}, {'user_message': 'How much can I spend on cloud resources?'}, ] conversations.insert(test_messages) ```
  Inserting rows into \`conversations\`: 3 rows \[00:00, 1047.88 rows/s]
  Inserted 3 rows with 0 errors.
  3 rows inserted, 18 values computed.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # View conversations with memory conversations.select( conversations.user_message, conversations.relevant_memories, conversations.assistant_reply, ).collect() ```
## Explanation **Memory-aware agent architecture:**
  User Message → Retrieve Memories → Build Prompt → LLM Response
                      ↓
              Memory Bank (with embeddings)
**Key components:**
**Adding new memories:** ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} memories.insert([{ 'content': 'New information to remember', 'category': 'fact', 'created_at': datetime.now() }]) ``` ## See also * [Build a RAG pipeline](/howto/cookbooks/agents/pattern-rag-pipeline) - Document retrieval * [Use tool calling](/howto/cookbooks/agents/llm-tool-calling) - Function calling with LLMs * [Pixelbot](https://github.com/pixeltable/pixelbot) - Full agent implementation # Look up structured data with retrieval UDFs Source: https://docs.pixeltable.com/howto/cookbooks/agents/pattern-data-lookup Open in Kaggle  Open in Colab  Download Notebook This documentation page is also available as an interactive notebook. You can launch the notebook in Kaggle or Colab, or download it for use with an IDE or local Jupyter installation, by clicking one of the above links. Create lookup functions that query tables by key—for customer records, product catalogs, or financial data. ## Problem You have structured data—customer records, product catalogs, financial data—and need to look up rows by key values. Common scenarios:
## Solution **What’s in this recipe:** * Create lookup functions from tables with `retrieval_udf` * Query by single or multiple keys * Use lookups in computed columns for data enrichment Use `pxt.retrieval_udf(table)` to automatically create a function that queries the table by its columns. ### Setup ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} %pip install -qU pixeltable ``` ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} import pixeltable as pxt # Create a fresh directory pxt.drop_dir('lookup_demo', force=True) pxt.create_dir('lookup_demo') ```
  Connected to Pixeltable database at: postgresql+psycopg://postgres:@/pixeltable?host=/Users/pjlb/.pixeltable/pgdata
  Created directory 'lookup\_demo'.
  \
### Create a product catalog table ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Create a product catalog products = pxt.create_table( 'lookup_demo/products', { 'sku': pxt.String, 'name': pxt.String, 'price': pxt.Float, 'category': pxt.String, }, ) products.insert( [ { 'sku': 'LAPTOP-001', 'name': 'MacBook Pro 14"', 'price': 1999.00, 'category': 'electronics', }, { 'sku': 'LAPTOP-002', 'name': 'ThinkPad X1', 'price': 1499.00, 'category': 'electronics', }, { 'sku': 'PHONE-001', 'name': 'iPhone 15 Pro', 'price': 999.00, 'category': 'electronics', }, { 'sku': 'CHAIR-001', 'name': 'Ergonomic Office Chair', 'price': 449.00, 'category': 'furniture', }, { 'sku': 'DESK-001', 'name': 'Standing Desk', 'price': 699.00, 'category': 'furniture', }, ] ) products.collect() ```
  Created table 'products'.
  Inserting rows into \`products\`: 5 rows \[00:00, 502.31 rows/s]
  Inserted 5 rows with 0 errors.
### Create a lookup function with retrieval\_udf ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Create a lookup function that searches by SKU get_product = pxt.retrieval_udf( products, name='get_product', description='Look up a product by its SKU code', parameters=['sku'], # Only use SKU as the lookup key limit=1, # Return at most 1 result ) # Check the function signature ``` ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Look up a product by SKU result = products.select(get_product(sku='LAPTOP-001')).limit(1).collect() ``` ### Look up by category (multiple results) ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Create a category lookup (returns multiple products) get_by_category = pxt.retrieval_udf( products, name='get_by_category', description='Get all products in a category', parameters=['category'], limit=10, # Return up to 10 products ) # Find all electronics products.select(get_by_category(category='electronics')).limit( 1 ).collect() ```
### Use lookups for data enrichment ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Create an orders table orders = pxt.create_table( 'lookup_demo/orders', { 'order_id': pxt.String, 'product_sku': pxt.String, 'quantity': pxt.Int, }, ) orders.insert( [ { 'order_id': 'ORD-001', 'product_sku': 'LAPTOP-001', 'quantity': 2, }, { 'order_id': 'ORD-002', 'product_sku': 'PHONE-001', 'quantity': 1, }, { 'order_id': 'ORD-003', 'product_sku': 'CHAIR-001', 'quantity': 4, }, ] ) ```
  Created table 'orders'.
  Inserting rows into \`orders\`: 3 rows \[00:00, 1186.28 rows/s]
  Inserted 3 rows with 0 errors.
  3 rows inserted, 6 values computed.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Add a computed column that enriches orders with product details orders.add_computed_column( product_info=get_product(sku=orders.product_sku) ) # View enriched orders orders.select( orders.order_id, orders.product_sku, orders.quantity, orders.product_info, ).collect() ```
  Added 3 column values with 0 errors.
## Explanation **`retrieval_udf` parameters:**
**Use cases:**
**Tips:** * Use `limit=1` for unique key lookups * Specify only needed columns in `parameters` for cleaner APIs * Add descriptions for LLM tool integration ## See also * [Use tool calling with LLMs](/howto/cookbooks/agents/llm-tool-calling) - Use retrieval UDFs as LLM tools * [Build a RAG pipeline](/howto/cookbooks/agents/pattern-rag-pipeline) - Semantic search with `@pxt.query` # LLM as judge Source: https://docs.pixeltable.com/howto/cookbooks/agents/pattern-llm-judge Score model output with a second model, as a chain of computed columns Scoring generated text by hand does not scale, and a score computed outside the table drifts from the row it describes. Declare the judge as columns on the same table: the answer, the judge prompt, the verdict, and the numeric score each become a computed column, so inserting a test case runs the whole evaluation and the score is stored beside the answer it grades. ## The table Each row is one test case: a prompt, and the criteria the answer is judged against. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} import pixeltable as pxt import pixeltable.functions as pxtf from pixeltable.functions import openai pxt.create_dir('evals', if_exists='ignore') runs = pxt.create_table( 'evals.runs', {'prompt': pxt.String, 'criteria': pxt.String}, if_exists='ignore', ) ``` ## The answer under test ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} runs.add_computed_column( response=openai.chat_completions( messages=[{'role': 'user', 'content': runs.prompt}], model='gpt-4o-mini' ) ) runs.add_computed_column(answer=runs.response.choices[0].message.content) ``` ## The judge prompt Build it with `pxtf.string.format`, not Python's `str.format`. `template.format(prompt=runs.prompt)` does not do what it looks like. Python formats the expression object itself, so every row gets the same string containing the column's name. `pxtf.string.format` returns an expression, which is evaluated per row. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} JUDGE_TEMPLATE = """You are grading an AI response against a set of criteria. Prompt: {prompt} Criteria: {criteria} Response: {response} Reply on exactly two lines: Score: <1-10> Explanation: """ runs.add_computed_column( judge_prompt=pxtf.string.format( JUDGE_TEMPLATE, prompt=runs.prompt, criteria=runs.criteria, response=runs.answer ) ) ``` ## The verdict and the score The judge returns prose. A UDF pulls the number out so you can sort and aggregate on it. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} runs.add_computed_column( judge_response=openai.chat_completions( messages=[ {'role': 'system', 'content': 'You grade AI responses. Be strict and terse.'}, {'role': 'user', 'content': runs.judge_prompt}, ], model='gpt-4o-mini', ) ) runs.add_computed_column(verdict=runs.judge_response.choices[0].message.content) @pxt.udf def extract_score(verdict: str) -> float | None: """The first `Score:` line, or None when the judge did not follow the format.""" for line in verdict.splitlines(): if line.strip().startswith('Score:'): try: return float(line.split(':', 1)[1].strip()) except ValueError: return None return None runs.add_computed_column(score=extract_score(runs.verdict)) ``` Returning `None` rather than `0.0` on a malformed verdict matters: a zero is indistinguishable from a genuinely bad answer, and it drags any average you compute. ## Run it ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} runs.insert([ { 'prompt': 'Write a haiku about dogs.', 'criteria': 'Follows 5-7-5. Is about dogs. Uses concrete imagery.', }, { 'prompt': 'Explain quantum computing to a 10-year-old.', 'criteria': 'Age-appropriate words. Uses an analogy. Under 100 words.', }, ]) runs.order_by(runs.score).select(runs.prompt, runs.score, runs.verdict).collect() ``` Both `openai.chat_completions()` calls need `OPENAI_API_KEY`. Inserting a row runs the answer, the judge prompt, the verdict, and the score in order, because each column depends on the one above it. A UDF cannot be defined in the global namespace of a plain Python script. In a notebook or a REPL the definition above works as written; in a script, put `extract_score` in an importable module and import it. ## Notes * Add a criterion and only the affected columns recompute; the answers already generated are not called again. * To grade the same answers under two judges, add a second `judge_response` column with a different model rather than a second table. * To grade answers your application already produced, put these columns on that table instead of a separate one. The judge does not need its own copy of the data. * `pxt.create_table()` here is the notebook and test form. An application declares the same columns on a `TableModel` in `app.py` and creates them with `pxt schema update`. # Build a RAG pipeline Source: https://docs.pixeltable.com/howto/cookbooks/agents/pattern-rag-pipeline Open in Kaggle  Open in Colab  Download Notebook This documentation page is also available as an interactive notebook. You can launch the notebook in Kaggle or Colab, or download it for use with an IDE or local Jupyter installation, by clicking one of the above links. Create a retrieval-augmented generation system that answers questions using your documents as context. ## Problem You want an LLM to answer questions using your specific documents—not just its training data. You need to retrieve relevant context and include it in the prompt.
## Solution **What’s in this recipe:** * Embed and index documents for retrieval * Create a query function that retrieves context * Generate answers grounded in your documents You build a pipeline that: (1) embeds documents, (2) finds relevant chunks for a query, and (3) generates an answer using those chunks as context. ### Setup ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} %pip install -qU pixeltable openai ``` ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} import getpass import os if 'OPENAI_API_KEY' not in os.environ: os.environ['OPENAI_API_KEY'] = getpass.getpass('OpenAI API Key: ') ``` ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} import pixeltable as pxt from pixeltable.functions.openai import chat_completions, embeddings # Create a fresh directory pxt.drop_dir('rag_demo', force=True) pxt.create_dir('rag_demo') ```
  Connected to Pixeltable database at: postgresql+psycopg://postgres:@/pixeltable?host=/Users/pjlb/.pixeltable/pgdata
  Created directory 'rag\_demo'.
  \
### Step 1: create document store with embeddings ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Create table for document chunks chunks = pxt.create_table( 'rag_demo/chunks', {'doc_id': pxt.String, 'chunk_text': pxt.String} ) ```
  Created table 'chunks'.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Add embedding index for semantic search chunks.add_embedding_index( column='chunk_text', string_embed=embeddings.using(model='text-embedding-3-small'), ) ``` ### Step 2: load documents ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Sample knowledge base (in production, load from files/database) documents = [ { 'doc_id': 'password-reset', 'chunk_text': 'To reset your password, go to the login page and click "Forgot Password". Enter your email address and you will receive a reset link within 5 minutes. The link expires after 24 hours.', }, { 'doc_id': 'password-reset', 'chunk_text': 'Password requirements: minimum 8 characters, at least one uppercase letter, one number, and one special character. Passwords expire every 90 days for security.', }, { 'doc_id': 'account-settings', 'chunk_text': 'To update your profile, navigate to Settings > Account. You can change your display name, email address, and notification preferences. Changes take effect immediately.', }, { 'doc_id': 'billing', 'chunk_text': 'Billing occurs on the first of each month. You can view invoices under Settings > Billing. To change your payment method, click "Update Payment" and enter your new card details.', }, { 'doc_id': 'api-access', 'chunk_text': 'API keys can be generated in Settings > Developer. Each key has configurable permissions. Rate limits are 1000 requests per minute for standard plans, 10000 for enterprise.', }, ] chunks.insert(documents) ```
  Inserting rows into \`chunks\`: 5 rows \[00:00, 345.31 rows/s]
  Inserted 5 rows with 0 errors.
  5 rows inserted, 15 values computed.
### Step 3: create the RAG query function ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Define a query function that retrieves context @pxt.query def retrieve_context(query: str, top_k: int = 3): """Retrieve the most relevant chunks for a query.""" sim = chunks.chunk_text.similarity(string=query) return ( chunks.where(sim > 0.5) .order_by(sim, asc=False) .limit(top_k) .select(doc_id=chunks.doc_id, text=chunks.chunk_text) ) ``` ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # View retrieved context for a query query = 'What are the key features?' context_chunks = retrieve_context(query) context_chunks ```
  retrieve\_context('What are the key features?')
### Step 4: generate answers with context ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Create a table for questions/answers qa = pxt.create_table('rag_demo/qa', {'question': pxt.String}) ```
  Created table 'qa'.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Add retrieval step qa.add_computed_column(context=retrieve_context(qa.question, top_k=3)) ```
  Added 0 column values with 0 errors.
  No rows affected.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Build the RAG prompt @pxt.udf def build_rag_prompt(question: str, context: list[dict]) -> str: context_text = '\n\n'.join( [f'[{c["doc_id"]}]: {c["text"]}' for c in context] ) return f"""Answer the question based only on the provided context. If the context doesn't contain the answer, say "I don't have information about that." Context: {context_text} Question: {question} Answer:""" qa.add_computed_column(prompt=build_rag_prompt(qa.question, qa.context)) ```
  Added 0 column values with 0 errors.
  No rows affected.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Generate answer qa.add_computed_column( response=chat_completions( messages=[{'role': 'user', 'content': qa.prompt}], model='gpt-4o-mini', ) ) qa.add_computed_column(answer=qa.response.choices[0].message.content) ```
  Added 0 column values with 0 errors.
  Added 0 column values with 0 errors.
  No rows affected.
### Ask questions ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Insert questions questions = [ {'question': 'How do I reset my password?'}, {'question': 'What are the API rate limits?'}, {'question': 'When am I billed?'}, ] qa.insert(questions) ```
  Inserting rows into \`qa\`: 3 rows \[00:00, 872.12 rows/s]
  Inserted 3 rows with 0 errors.
  3 rows inserted, 18 values computed.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # View answers qa.select(qa.question, qa.answer).collect() ```
## Explanation **RAG pipeline flow:**
  Question → Embed → Retrieve similar chunks → Build prompt with context → Generate answer
**Key components:**
**Scaling tips:** * Use `doc-chunk-for-rag` recipe to split long documents * Adjust `top_k` to balance context size vs. relevance * Consider metadata filtering for large knowledge bases ## See also * [Chunk documents for RAG](/howto/cookbooks/text/doc-chunk-for-rag) - Split documents into chunks * [Create text embeddings](/howto/cookbooks/search/embed-text-openai) - Embedding fundamentals * [Semantic text search](/howto/cookbooks/search/search-semantic-text) - Search patterns # Use a table pipeline as a reusable function Source: https://docs.pixeltable.com/howto/cookbooks/agents/pattern-table-as-udf Open in Kaggle  Open in Colab  Download Notebook This documentation page is also available as an interactive notebook. You can launch the notebook in Kaggle or Colab, or download it for use with an IDE or local Jupyter installation, by clicking one of the above links. Convert a table with computed columns into a callable function for multi-agent workflows and pipeline composition. ## Problem You have a table that runs a complex pipeline—LLM calls, tool use, post-processing—and you want to reuse that entire pipeline from other tables. Copy-pasting computed column definitions is error-prone and hard to maintain.
## Solution **What’s in this recipe:** * Create an “agent” table with computed columns * Convert the table to a callable UDF with `pxt.udf(table, return_value=...)` * Use the table UDF in other tables’ computed columns You wrap an entire table pipeline as a function. When you call this function from another table, it inserts a row into the agent table, runs all computed columns, and returns the specified output column. ### Setup ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} %pip install -qU pixeltable openai ``` ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} import getpass import os if 'OPENAI_API_KEY' not in os.environ: os.environ['OPENAI_API_KEY'] = getpass.getpass('OpenAI API Key: ') ``` ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} import pixeltable as pxt from pixeltable.functions.openai import chat_completions # Create a fresh directory pxt.drop_dir('table_udf_demo', force=True) pxt.create_dir('table_udf_demo') ```
  Connected to Pixeltable database at: postgresql+psycopg://postgres:@/pixeltable?host=/Users/pjlb/.pixeltable/pgdata
  Created directory 'table\_udf\_demo'.
  \
### Create an agent table with computed columns You create a table that encapsulates a complete pipeline. This example builds a summarization agent: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Create the agent table with input column summarizer = pxt.create_table( 'table_udf_demo/summarizer', {'text': pxt.String} ) ```
  Created table 'summarizer'.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Add the LLM call as a computed column summarizer.add_computed_column( response=chat_completions( messages=[ { 'role': 'user', 'content': 'Summarize this in one sentence:\n\n' + summarizer.text, } ], model='gpt-4o-mini', ) ) ```
  Added 0 column values with 0 errors.
  No rows affected.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Extract the summary text summarizer.add_computed_column( summary=summarizer.response.choices[0].message.content ) ```
  Added 0 column values with 0 errors.
  No rows affected.
### Convert the table to a UDF You use `pxt.udf(table, return_value=...)` to convert the table into a callable function. The `return_value` specifies which column to return: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Convert the summarizer table into a callable UDF summarize = pxt.udf(summarizer, return_value=summarizer.summary) ``` ### Use the table UDF in another table You can now use `summarize()` as a computed column in any other table: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Create a table that uses the summarizer articles = pxt.create_table( 'table_udf_demo/articles', {'title': pxt.String, 'content': pxt.String}, ) ```
  Created table 'articles'.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Add the table UDF as a computed column articles.add_computed_column(summary=summarize(text=articles.content)) ```
  Added 0 column values with 0 errors.
  No rows affected.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Insert articles - summaries are generated automatically articles.insert( [ { 'title': 'Climate Report', 'content': 'Global temperatures rose by 1.2 degrees Celsius above pre-industrial levels last year, marking the hottest year on record. Scientists attribute this to continued greenhouse gas emissions and a strong El Nino pattern. The report calls for immediate action to reduce carbon emissions.', }, { 'title': 'Tech Merger', 'content': 'Two major semiconductor companies announced a merger valued at $50 billion. The combined entity will control 30% of the global chip market. Regulators in multiple countries will review the deal over the next 18 months.', }, ] ) ```
  Inserting rows into \`articles\`: 2 rows \[00:00, 196.58 rows/s]
  Inserted 2 rows with 0 errors.
  2 rows inserted, 6 values computed.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # View results articles.select(articles.title, articles.summary).collect() ```
## Explanation **How table UDFs work:**
  Consumer table row → Table UDF called → Agent table inserts row →
  Computed columns run → Return value extracted → Consumer gets result
**When to use table UDFs vs `@pxt.query`:**
**Key benefits:** * **Encapsulation**: Hide complex pipeline details behind a simple function call * **Reusability**: Use the same agent from multiple consumer tables * **Persistence**: All intermediate results are stored in the agent table for debugging * **Composition**: Chain agents together for multi-stage workflows ## See also * [Look up structured data](/howto/cookbooks/agents/pattern-data-lookup) - Simple key-based lookups with `retrieval_udf` * [Build a RAG pipeline](/howto/cookbooks/agents/pattern-rag-pipeline) - Retrieval with `@pxt.query` * [Use tool calling with LLMs](/howto/cookbooks/agents/llm-tool-calling) - Add tools to agent tables # Extract audio from video Source: https://docs.pixeltable.com/howto/cookbooks/audio/audio-extract-from-video Open in Kaggle  Open in Colab  Download Notebook This documentation page is also available as an interactive notebook. You can launch the notebook in Kaggle or Colab, or download it for use with an IDE or local Jupyter installation, by clicking one of the above links. Extract the audio track from video files for transcription, analysis, or processing. ## Problem You have video files but need to work with just the audio track—for transcription, speaker analysis, or audio processing. Extracting audio manually with ffmpeg is tedious and doesn’t integrate with your data pipeline.
## Solution **What’s in this recipe:** * Extract audio from video as a computed column * Choose audio format (mp3, wav, flac) * Chain with transcription for automatic video-to-text You use the `extract_audio` function to create an audio column from video. This integrates seamlessly with transcription and other audio processing. ### Setup ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} %pip install -qU pixeltable 'numpy<2.4' ``` ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} import pixeltable as pxt from pixeltable.functions.video import extract_audio # Create a fresh directory pxt.drop_dir('audio_extract_demo', force=True) pxt.create_dir('audio_extract_demo') ```
  Connected to Pixeltable database at: postgresql+psycopg://postgres:@/pixeltable?host=/Users/pjlb/.pixeltable/pgdata
  Created directory 'audio\_extract\_demo'.
  \
### Extract audio from video ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Create table for videos videos = pxt.create_table( 'audio_extract_demo/videos', {'title': pxt.String, 'video': pxt.Video} ) ```
  Created table 'videos'.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Add computed column to extract audio as MP3 videos.add_computed_column( audio=extract_audio(videos.video, format='mp3') ) ```
  Added 0 column values with 0 errors.
  No rows affected.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Insert a sample video (from multimedia-commons with audio) video_url = 's3://multimedia-commons/data/videos/mp4/ffe/ffb/ffeffbef41bbc269810b2a1a888de.mp4' videos.insert([{'title': 'Sample Video', 'video': video_url}]) ```
  Inserting rows into \`videos\`: 1 rows \[00:00, 207.52 rows/s]
  Inserted 1 row with 0 errors.
  1 row inserted, 4 values computed.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # View results videos.select(videos.title, videos.audio).collect() ```
### Chain with transcription Add transcription as a follow-up computed column: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Install whisper for transcription %pip install -qU openai-whisper ``` ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} from pixeltable.functions import whisper # Add transcription of the extracted audio videos.add_computed_column( transcription=whisper.transcribe(videos.audio, model='base.en') ) ```
  Added 1 column value with 0 errors.
  1 row updated, 1 value computed.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Extract the transcript text videos.add_computed_column(transcript=videos.transcription.text) ```
  Added 1 column value with 0 errors.
  1 row updated, 1 value computed.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # View the full pipeline results videos.select(videos.title, videos.transcript).collect() ```
## Explanation **Audio format options:**
**Pipeline flow:**
  Video → extract\_audio → Audio → whisper.transcribe → Transcript
Each step is a computed column. When you insert a new video: 1. Audio is extracted automatically 2. Whisper transcribes the audio 3. All results are cached for future queries ## See also * [Transcribe audio](/howto/cookbooks/audio/audio-transcribe) - Audio-only transcription * [Summarize podcasts](/howto/cookbooks/audio/audio-summarize-podcast) - Transcribe and summarize * [Extract video frames](/howto/cookbooks/video/video-extract-frames) - Work with video frames # Generate Podcast Chapters Source: https://docs.pixeltable.com/howto/cookbooks/audio/audio-podcast-chapters Open in Kaggle  Open in Colab  Download Notebook This documentation page is also available as an interactive notebook. You can launch the notebook in Kaggle or Colab, or download it for use with an IDE or local Jupyter installation, by clicking one of the above links. Generate YouTube-style chapter timestamps for podcast episodes. WhisperX detects where speech starts and stops, then an LLM summarizes each segment into a chapter title. The end result looks like this:
  0:00 - Experiencing self vs remembering self
  0:10 - Whether memories are the primary source of happiness
  0:36 - Controlling how we remember experiences
## Problem You have podcast episodes and want to generate chapter markers — the kind you see in YouTube descriptions or podcast apps. Each chapter needs a timestamp and a short description of what’s being discussed. ## Solution **What’s in this recipe:** 1. **WhisperX** transcribes the audio and detects speech boundaries via VAD (Voice Activity Detection) 2. `json.list_iterator` creates a **view** with one row per speech segment 3. **GPT-4o-mini** generates a chapter title for each segment as a computed column 4. The result is formatted as YouTube-style timestamps ### Setup ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} %pip install -qU pixeltable whisperx openai ``` ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} import getpass import os if 'OPENAI_API_KEY' not in os.environ: os.environ['OPENAI_API_KEY'] = getpass.getpass('OpenAI API Key: ') ``` ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} import pixeltable as pxt import pixeltable.functions as pxtf pxt.drop_dir('podcast_demo', force=True, if_not_exists='ignore') pxt.create_dir('podcast_demo') ``` ### Load a podcast episode ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} episodes = pxt.create_table( 'podcast_demo/episodes', {'title': pxt.String, 'audio': pxt.Audio} ) ```
  Created table 'episodes'.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} episodes.insert( [ { 'title': 'Lex Fridman Podcast Excerpt', 'audio': 'https://raw.githubusercontent.com/pixeltable/pixeltable/release/docs/resources/audio-transcription-demo/Lex-Fridman-Podcast-430-Excerpt-0.mp4', } ] ) ```
  Inserted 1 row with 0 errors in 0.82 s (1.22 rows/s)
  1 row inserted.
View the data: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} episodes.collect() ```
And the table schema so far: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} episodes ```
### Step 1: Transcribe with WhisperX WhisperX does two things at once: it runs Voice Activity Detection (VAD) to find where speech occurs, then transcribes each speech region. The output is a list of segments — each with a start time, end time, and the text that was spoken. These segments are the raw material for our chapter markers. Each segment boundary corresponds to a natural pause or transition in the conversation. > **Note:** You may see verbose warnings from `torchcodec` or `pyannote` > when this cell runs — they’re harmless and can be ignored. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} episodes.add_computed_column( transcription=pxtf.whisperx.transcribe( episodes.audio, model='tiny.en' ) ) ```
  Added 1 column value with 0 errors in 9.81 s (0.10 rows/s)
  1 row updated.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} episodes.select( first_segment=episodes.transcription.segments[0] ).collect() ```
Each segment is a dict with `start`, `end`, and `text` fields. We can use the `['*']` path expression to extract a field from every segment at once — this returns a list of values: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} episodes.select( segment_starts=episodes.transcription.segments['*'].start, segment_ends=episodes.transcription.segments['*'].end, segment_text=episodes.transcription.segments['*'].text, ).collect() ```
That’s useful for peeking at the data, but the result is still one row with parallel lists — not one row per segment. Here’s what a single segment looks like as a proper row: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} episodes.select( start=episodes.transcription.segments[0].start, end=episodes.transcription.segments[0].end, text=episodes.transcription.segments[0].text, ).collect() ```
We want every segment as its own row like this — not just the first one. That’s what `list_iterator` does. ### Step 2: Create a segments view with `list_iterator` To work with individual segments as rows, we create a **view** using `pxtf.json.list_iterator`. This iterator takes parallel lists and zips them into one row per element — like converting columns of arrays into a proper table. The keyword argument names (`start`, `end`, `text`) become the column names in the view. Each argument is a Pixeltable expression that evaluates to a JSON list. **Why `astype`?** WhisperX returns an untyped `dict`, so Pixeltable doesn’t know what types are inside the segments. `list_iterator` requires typed JSON so it can define the view’s schema. We use `astype` to declare that `.start` and `.end` are lists of floats, and `.text` is a list of strings: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} segments = pxt.create_view( 'podcast_demo/segments', episodes, iterator=pxtf.json.list_iterator( start=episodes.transcription.segments['*'].start.astype( pxt.Json[[float]] ), end=episodes.transcription.segments['*'].end.astype( pxt.Json[[float]] ), text=episodes.transcription.segments['*'].text.astype( pxt.Json[[str]] ), ), ) ``` Here is the schema of this view - you can see the four columns added by the `list_iterator`, and the other columns that came from the `episodes` table we started with: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} segments ```
One row per segment, with typed columns. From here we can add computed columns that operate on individual segments — no manual JSON wrangling needed. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} segments.select(segments.start, segments.end, segments.text).collect() ```
### Step 3: Generate chapter titles Each segment now has its own row. We add a computed column that sends the segment text to GPT-4o-mini for a short chapter title: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} segments.add_computed_column( title_response=pxtf.openai.chat_completions( messages=[ { 'role': 'user', 'content': pxtf.string.format( 'Write a short chapter title (5-10 words) for this podcast segment. ' 'Return only the title, no quotes or extra punctuation.\n\n{0}', segments.text, ), } ], model='gpt-4o-mini', ), if_exists='replace', ) segments.add_computed_column( chapter_title=segments.title_response.choices[0].message.content, if_exists='replace', ) ```
  Added 3 column values with 0 errors in 2.64 s (1.14 rows/s)
  Added 3 column values with 0 errors in 0.02 s (142.43 rows/s)
  3 rows updated.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} segments.select(segments.text, segments.chapter_title).collect() ```
### Step 4: YouTube-style timestamps Format each segment’s start time with its LLM-generated title: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf def format_timestamp(start: float, chapter_title: str) -> str: mins, secs = divmod(int(start), 60) title = chapter_title.strip('"') return f'{mins}:{secs:02d} - {title}' segments.add_computed_column( timestamp=format_timestamp(segments.start, segments.chapter_title), if_exists='replace', ) ```
  Added 3 column values with 0 errors in 0.04 s (69.92 rows/s)
  3 rows updated.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} segments.select(segments.timestamp).order_by(segments.start).collect() ```
Here is a formatted version you can copy/paste directly into your YouTube video description field: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} print('\n'.join(segments.order_by(segments.start).collect()['timestamp'])) ```
  0:00 - Living in the Moment vs. Reflecting on the Past
  0:10 - The Power of Memory in Shaping Happiness
  0:36 - Evolving Memories for Lasting Happiness
### Step 5: Listen to each segment The segments view inherits the `audio` column from the base `episodes` table. We can add a computed column that slices out each segment’s audio clip using its `start` and `end` times — useful for spot-checking the transcription. In Jupyter, the `audio_clip` column renders as an inline audio player you can click to listen: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} import os import subprocess import tempfile @pxt.udf def slice_audio(audio: pxt.Audio, start: float, end: float) -> pxt.Audio: """Extract a time range from an audio file using ffmpeg.""" fd, output_path = tempfile.mkstemp(suffix='.mp4') os.close(fd) subprocess.run( [ 'ffmpeg', '-y', '-i', str(audio), '-ss', str(start), '-to', str(end), '-c', 'copy', output_path, ], capture_output=True, check=True, ) return output_path segments.add_computed_column( audio_clip=slice_audio(segments.audio, segments.start, segments.end), if_exists='replace', ) ```
  Added 3 column values with 0 errors in 0.28 s (10.85 rows/s)
  3 rows updated.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} segments.select(segments.timestamp, segments.audio_clip).order_by( segments.start ).collect() ```
## Explanation **Pipeline:**
  Audio → WhisperX (VAD + transcription) → list\_iterator view (1 row per segment) → GPT-4o-mini per row → timestamps
The `episodes` table holds the raw audio and transcription. The `segments` view fans out the transcription’s segment list into individual rows using `json.list_iterator`. Each segment row then gets its own LLM call and timestamp — all as computed columns. Insert a new episode and the entire pipeline runs automatically: transcription, segment extraction, chapter titling, and timestamp formatting. **How WhisperX finds the chapter boundaries:** WhisperX uses PyAnnote VAD to detect where speech occurs in the audio. Pauses, silence, and transitions between speakers create natural segment boundaries. These boundaries become the chapter start times. **Why `list_iterator`?** Without `list_iterator`, you’d write custom UDFs to extract segments from the JSON, batch them into a single LLM prompt, and parse the response back apart. The view-based approach is more idiomatic — each segment is its own row, and computed columns operate on one row at a time. **Trade-offs:** * One LLM call per segment instead of one batched call — fine for short podcasts, but consider batching for episodes with 50+ segments * This approach requires running a full transcription model just to find where the pauses are * For longer episodes, WhisperX’s `chunk_size` parameter controls how the audio is batched internally * The chapter titles depend on LLM quality — `gpt-4o-mini` is fast and cheap, use `gpt-4o` for higher quality ## See also * [`json.list_iterator`](/sdk/latest/json#iterator-list_iterator) — Iterator for flattening JSON lists into view rows * [Transcribe audio](/howto/cookbooks/audio/audio-transcribe) — Fixed-interval splitting with local Whisper * [Summarize podcasts](/howto/cookbooks/audio/audio-summarize-podcast) — Transcription + LLM summarization * [Video scene detection](/howto/cookbooks/video/video-scene-detection) — Content-aware detection for video # Summarize podcasts and audio Source: https://docs.pixeltable.com/howto/cookbooks/audio/audio-summarize-podcast Open in Kaggle  Open in Colab  Download Notebook This documentation page is also available as an interactive notebook. You can launch the notebook in Kaggle or Colab, or download it for use with an IDE or local Jupyter installation, by clicking one of the above links. Transcribe audio files and generate summaries automatically using Whisper and LLMs. ## Problem You have podcast episodes, meeting recordings, or interviews that need both transcription and summarization. Doing this manually is time-consuming and doesn’t scale.
## Solution **What’s in this recipe:** * Transcribe audio with Whisper (runs locally) * Generate summaries with an LLM * Chain transcription → summarization automatically You create a pipeline where audio is transcribed first, then the transcript is summarized. Both steps run automatically when you insert new audio files. ### Setup ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} %pip install -qU pixeltable openai-whisper openai ``` ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} import getpass import os if 'OPENAI_API_KEY' not in os.environ: os.environ['OPENAI_API_KEY'] = getpass.getpass('OpenAI API Key: ') ``` ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} import pixeltable as pxt from pixeltable.functions import openai, whisper # Create a fresh directory pxt.drop_dir('podcast_demo', force=True) pxt.create_dir('podcast_demo') ```
  Created directory 'podcast\_demo'.
  \
### Create the pipeline Create a table with audio input, then add computed columns for transcription and summarization: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Create table for audio files podcasts = pxt.create_table( 'podcast_demo/episodes', {'title': pxt.String, 'audio': pxt.Audio} ) ```
  Created table 'episodes'.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Step 1: Transcribe with local Whisper (uses GPU if available) podcasts.add_computed_column( transcription=whisper.transcribe(podcasts.audio, model='base.en') ) ```
  Added 0 column values with 0 errors.
  No rows affected.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Extract the text from the transcription result podcasts.add_computed_column(transcript_text=podcasts.transcription.text) ```
  Added 0 column values with 0 errors.
  No rows affected.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Step 2: Summarize the transcript with OpenAI summary_prompt = ( """Summarize this transcript in 2-3 sentences, then list 3 key points. Transcript: """ + podcasts.transcript_text ) podcasts.add_computed_column( summary_response=openai.chat_completions( messages=[{'role': 'user', 'content': summary_prompt}], model='gpt-4o-mini', ) ) ```
  Added 0 column values with 0 errors.
  No rows affected.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Extract summary text from response podcasts.add_computed_column( summary=podcasts.summary_response.choices[0].message.content ) ```
  Added 0 column values with 0 errors.
  No rows affected.
### Process audio files Insert audio files and watch the pipeline run automatically: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Insert sample audio audio_url = 'https://github.com/pixeltable/pixeltable/raw/main/docs/resources/10-minute%20tour%20of%20Pixeltable.mp3' podcasts.insert([{'title': 'Pixeltable Tour', 'audio': audio_url}]) ```
  Inserting rows into \`episodes\`: 1 rows \[00:00, 185.18 rows/s]
  Inserted 1 row with 0 errors.
  1 row inserted, 8 values computed.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # View transcript podcasts.select(podcasts.title, podcasts.transcript_text).collect() ```
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # View summary podcasts.select(podcasts.title, podcasts.summary).collect() ```
## Explanation **Pipeline architecture:**
  Audio → Whisper transcription → Transcript text → LLM summarization → Summary
Each step is a computed column that depends on the previous one. When you insert a new audio file, all steps run automatically in sequence. **Whisper model options:**
For production with varied audio quality, use `small.en` or larger. ## See also * [Transcribe audio](/howto/cookbooks/audio/audio-transcribe) - Basic audio transcription * [Summarize text](/howto/cookbooks/text/text-summarize) - Text summarization patterns # Convert text to speech Source: https://docs.pixeltable.com/howto/cookbooks/audio/audio-text-to-speech Open in Kaggle  Open in Colab  Download Notebook This documentation page is also available as an interactive notebook. You can launch the notebook in Kaggle or Colab, or download it for use with an IDE or local Jupyter installation, by clicking one of the above links. Generate natural-sounding audio from text using OpenAI’s text-to-speech models. ## Problem You need to convert text content into spoken audio—for accessibility, content repurposing, or voice applications.
## Solution **What’s in this recipe:** * Generate speech with OpenAI TTS * Choose from multiple voice options * Store text and audio together You add a computed column that converts text to audio. The audio is cached and only regenerated when the source text changes. ### Setup ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} %pip install -qU pixeltable openai ``` ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} import getpass import os if 'OPENAI_API_KEY' not in os.environ: os.environ['OPENAI_API_KEY'] = getpass.getpass('OpenAI API Key: ') ``` ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} import pixeltable as pxt from pixeltable.functions.openai import speech # Create a fresh directory pxt.drop_dir('tts_demo', force=True) pxt.create_dir('tts_demo') ```
  Connected to Pixeltable database at: postgresql+psycopg://postgres:@/pixeltable?host=/Users/pjlb/.pixeltable/pgdata
  Created directory 'tts\_demo'.
  \
### Create text-to-speech pipeline ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Create table for articles articles = pxt.create_table( 'tts_demo/articles', {'title': pxt.String, 'content': pxt.String} ) ```
  Created table 'articles'.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Add audio generation column articles.add_computed_column( audio=speech(articles.content, model='tts-1', voice='alloy') ) ```
  Added 0 column values with 0 errors.
  No rows affected.
### Generate audio ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Insert sample articles sample_articles = [ { 'title': 'Welcome to AI', 'content': 'Artificial intelligence is transforming how we work and live. From smart assistants to autonomous vehicles, AI is becoming part of our daily lives.', }, { 'title': 'Getting Started', 'content': 'To begin your journey with machine learning, start by understanding the basics of data preparation and model training.', }, ] articles.insert(sample_articles) ```
  Inserting rows into \`articles\`: 2 rows \[00:00, 423.90 rows/s]
  Inserted 2 rows with 0 errors.
  2 rows inserted, 6 values computed.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # View articles with generated audio articles.select( articles.title, articles.content, articles.audio ).collect() ```
## Explanation **OpenAI TTS models:**
**Voice options:**
**Tips:** * Use `tts-1` for drafts and real-time applications * Use `tts-1-hd` for final production audio * Audio is cached—no regeneration on queries ## See also * [Transcribe audio](/howto/cookbooks/audio/audio-transcribe) - Convert audio to text * [Summarize podcasts](/howto/cookbooks/audio/audio-summarize-podcast) - Transcribe and summarize audio # Transcribe audio files with Whisper Source: https://docs.pixeltable.com/howto/cookbooks/audio/audio-transcribe Open in Kaggle  Open in Colab  Download Notebook This documentation page is also available as an interactive notebook. You can launch the notebook in Kaggle or Colab, or download it for use with an IDE or local Jupyter installation, by clicking one of the above links. Convert speech to text locally using OpenAI’s open-source Whisper model—no API key needed. This notebook uses `pxt.create_table()` so you can run cells without a project. An app puts the same columns on a `TableModel` in `app.py` and creates the tables with `pxt schema update`. ## Problem You have audio or video files that need transcription. Long files are memory-intensive to process at once, so you need to split them into manageable segments.
## Solution **What’s in this recipe:** * Transcribe audio files locally with Whisper (no API key) * Automatically segment long files * Extract and transcribe audio from videos You create a view with `audio_splitter` to break long files into segments, then add a computed column for transcription. Whisper runs locally on your machine—no API calls needed. ### Setup ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} %pip install -qU pixeltable openai-whisper ``` ### Load audio files ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} import pixeltable as pxt from pixeltable.functions import whisper from pixeltable.functions.audio import audio_splitter # Create a fresh directory pxt.drop_dir('audio_demo', force=True) pxt.create_dir('audio_demo') ```
  Connected to Pixeltable database at: postgresql+psycopg://postgres:@/pixeltable?host=/Users/asiegel/.pixeltable/pgdata
  Converting metadata from version 45 to 46
  Created directory 'audio\_demo'.
  \
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Create table for audio files audio = pxt.create_table('audio_demo/files', {'audio': pxt.Audio}) ```
  Created table 'files'.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Insert a sample audio file (video files also work - audio is extracted automatically) audio.insert( [ { 'audio': 'https://raw.githubusercontent.com/pixeltable/pixeltable/main/docs/resources/audio-transcription-demo/Lex-Fridman-Podcast-430-Excerpt-0.mp4' } ] ) ```
  Inserted 1 row with 0 errors in 1.05 s (0.95 rows/s)
  1 row inserted.
### Split into segments Create a view that splits audio into 30-second segments with overlap: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Split audio into segments for transcription segments = pxt.create_view( 'audio_demo/segments', audio, iterator=audio_splitter( audio.audio, duration=30.0, # 30-second segments overlap=2.0, # 2-second overlap for context min_segment_duration=5.0, # Drop segments shorter than 5 seconds ), ) ``` ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # View the segments segments.select(segments.segment_start, segments.segment_end).collect() ```
### Transcribe with Whisper Add a computed column that transcribes each segment: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Add transcription column (runs locally - no API key needed) segments.add_computed_column( transcription=whisper.transcribe( audio=segments.audio_segment, model='base.en', # Options: tiny.en, base.en, small.en, medium.en, large ) ) ```
  Added 2 column values with 0 errors in 3.35 s (0.60 rows/s)
  2 rows updated.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Extract just the text segments.add_computed_column(text=segments.transcription.text) ```
  Added 2 column values with 0 errors in 0.06 s (31.82 rows/s)
  2 rows updated.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # View transcriptions with timestamps segments.select( segments.segment_start, segments.segment_end, segments.text ).collect() ```
## Explanation **Whisper models:**
Models ending in `.en` are English-only and faster. Remove `.en` for multilingual support. **audio\_splitter parameters:** Exactly one of `duration` or `max_size` must be specified.
**Tips:** ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Byte-budget splits (e.g. 24 MB Whisper upload limit) audio_splitter(audio.audio, max_size=24 * 1024 * 1024) # Silence-aware duration splits audio_splitter( audio.audio, duration=30.0, min_silence_len=0.3, silence_thresh=-40.0, trim_leading_silence=True, ) ``` Full API: [audio\_splitter](/sdk/latest/audio#iterator-audio_splitter). **Video files work too:** When you insert a video file, Pixeltable automatically extracts the audio track. ## See also * [Iterators documentation](/platform/iterators) * [Whisper library](https://github.com/openai/whisper) # Create custom aggregate functions (UDAs) Source: https://docs.pixeltable.com/howto/cookbooks/core/custom-aggregates-uda Open in Kaggle  Open in Colab  Download Notebook This documentation page is also available as an interactive notebook. You can launch the notebook in Kaggle or Colab, or download it for use with an IDE or local Jupyter installation, by clicking one of the above links. Build reusable aggregation logic for group-by queries and analytics. ## Problem You need aggregations beyond the built-in `sum`, `count`, `mean`, `min`, `max` — such as collecting values into a list, concatenating strings, or computing custom statistics. ## Solution **What’s in this recipe:** * Define a UDA (User-Defined Aggregate) with the `@pxt.uda` decorator * Use UDAs in `group_by` queries * Create UDAs with multiple inputs ### Setup ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} %pip install -qU pixeltable ``` ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} import pixeltable as pxt pxt.drop_dir('uda_demo', force=True) pxt.create_dir('uda_demo') ```
  Connected to Pixeltable database at: postgresql+psycopg://postgres:@/pixeltable?host=/Users/pjlb/.pixeltable/pgdata
  Created directory 'uda\_demo'.
  \
### Create sample data ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} sales = pxt.create_table( 'uda_demo/sales', { 'region': pxt.String, 'product': pxt.String, 'amount': pxt.Float, 'quantity': pxt.Int, }, ) sales.insert( [ { 'region': 'North', 'product': 'Widget', 'amount': 100.0, 'quantity': 5, }, { 'region': 'North', 'product': 'Gadget', 'amount': 250.0, 'quantity': 2, }, { 'region': 'North', 'product': 'Widget', 'amount': 150.0, 'quantity': 8, }, { 'region': 'South', 'product': 'Widget', 'amount': 200.0, 'quantity': 10, }, { 'region': 'South', 'product': 'Gadget', 'amount': 175.0, 'quantity': 3, }, { 'region': 'East', 'product': 'Widget', 'amount': 125.0, 'quantity': 6, }, ] ) sales.collect() ```
  Created table 'sales'.

  Inserting rows into \`sales\`: 0 rows \[00:00, ? rows/s]
  Inserting rows into \`sales\`: 6 rows \[00:00, 609.56 rows/s]
  Inserted 6 rows with 0 errors.
### Variance UDA (not built-in) ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # A UDA is a class that inherits from pxt.Aggregator # It must implement: __init__, update, and value @pxt.uda class variance(pxt.Aggregator): """Compute population variance using Welford's online algorithm.""" def __init__(self): self.count = 0 self.mean = 0.0 self.m2 = 0.0 # Sum of squared differences from mean def update(self, val: float) -> None: if val is not None: self.count += 1 delta = val - self.mean self.mean += delta / self.count delta2 = val - self.mean self.m2 += delta * delta2 def value(self) -> float: if self.count < 1: return 0.0 return self.m2 / self.count # Population variance ``` ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Use like any built-in aggregate sales.select(variance(sales.amount)).collect() ```
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Use in group_by queries sales.group_by(sales.region).select( sales.region, amount_variance=variance(sales.amount) ).collect() ```
### String concatenation UDA ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.uda class string_agg(pxt.Aggregator): """Concatenate strings with a comma separator.""" def __init__(self): self.values = [] def update(self, val: str) -> None: if val is not None: self.values.append(val) def value(self) -> str: return ', '.join(self.values) ``` ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # List all products sold in each region sales.group_by(sales.region).select( sales.region, products=string_agg(sales.product) ).collect() ```
### Collect values into a list ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.uda class collect_list(pxt.Aggregator): """Collect all values into a list.""" def __init__(self): self.items = [] def update(self, val: float) -> None: if val is not None: self.items.append(val) def value(self) -> list[float]: return self.items ``` ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Get all amounts per region as a list sales.group_by(sales.region).select( sales.region, amounts=collect_list(sales.amount) ).collect() ```
### Weighted average UDA ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.uda class weighted_avg(pxt.Aggregator): """Compute weighted average: sum(value * weight) / sum(weight).""" def __init__(self): self.weighted_sum = 0.0 self.weight_sum = 0.0 def update(self, value: float, weight: float) -> None: if value is not None and weight is not None: self.weighted_sum += value * weight self.weight_sum += weight def value(self) -> float: if self.weight_sum == 0: return 0.0 return self.weighted_sum / self.weight_sum ``` ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Compute quantity-weighted average price per region sales.group_by(sales.region).select( sales.region, avg_price=weighted_avg(sales.amount, sales.quantity) ).collect() ```
### Mode UDA (most frequent value) ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} from collections import Counter @pxt.uda class mode(pxt.Aggregator): """Find the most frequent value in a group.""" def __init__(self): self.counts = Counter() def update(self, val: str) -> None: if val is not None: self.counts[val] += 1 def value(self) -> str: if not self.counts: return None return self.counts.most_common(1)[0][0] ``` ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Find most common product per region sales.group_by(sales.region).select( sales.region, top_product=mode(sales.product) ).collect() ```
## Explanation **UDA structure:** ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.uda class my_aggregate(pxt.Aggregator): def __init__(self): # Initialize state self.state = initial_value def update(self, val: InputType) -> None: # Called for each row # Update internal state with val def value(self) -> OutputType: # Called at the end return self.state ``` **Key points:** * Always handle `None` values in `update()` * Multiple parameters in `update()` enable multi-column aggregations (like `weighted_avg`) * Return type annotation on `value()` determines output column type ## See also * [UDFs in Pixeltable](../../../platform/udfs-in-pixeltable) - Complete guide to custom functions * [Join tables](/howto/cookbooks/core/query-join-tables) - Combine data before aggregating # Custom Iterators Source: https://docs.pixeltable.com/howto/cookbooks/core/custom-iterators Open in Kaggle  Open in Colab  Download Notebook This documentation page is also available as an interactive notebook. You can launch the notebook in Kaggle or Colab, or download it for use with an IDE or local Jupyter installation, by clicking one of the above links. An **iterator** in Pixeltable is a function that expands a single input row into multiple output rows. Built-in Pixeltable iterators include [frame\_iterator](/sdk/latest/video#iterator-frame_iterator), which iterates over the frames of a video; [tile\_iterator](/sdk/latest/image#iterator-tile_iterator), which iterates over tiles of an image; and [document\_splitter](/sdk/latest/document#iterator-document_splitter), which iterates over chunks (such as sentences or pages) of a document. These and other examples are discussed in the [Iterators](/platform/iterators) platform tutorial. As with UDFs, Pixeltable provides a way for users to define their own iterators from arbitrary Python code. Recall that custom UDFs are created by decorating a Python function with the `@pxt.udf` decorator. Similarly, custom iterators are created by decorating a Python generator function with `@pxt.iterator`. Custom iterators are a relatively advanced Pixeltable feature. This guide will make the most sense if you’re already familiar with Pixeltable’s built-in iterators, as well as the pxt.udf decorator. If you haven’t encountered those concepts yet, it’s recommended to first read the Iterators and UDFs tutorial sections. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} %pip install -qU pixeltable ``` ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} import pixeltable as pxt pxt.create_dir('iterators_demo', if_exists='replace_force') ```
  Connected to Pixeltable database at: postgresql+psycopg://postgres:@/pixeltable?host=/Users/asiegel/.pixeltable/pgdata
  Created directory 'iterators\_demo'.
  \
In this tutorial, we’ll be creating an iterator that takes an image as input, and produces multiple images as output. The output images will be variations of the input with different characteristics. To start, we’ll create a base table to store our source images. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} t = pxt.create_table('iterators_demo/images', {'image': pxt.Image}) images = [ 'https://raw.githubusercontent.com/pixeltable/pixeltable/release/docs/resources/images/000000000108.jpg', 'https://raw.githubusercontent.com/pixeltable/pixeltable/release/docs/resources/images/000000000632.jpg', ] t.insert({'image': image} for image in images) t.head() ```
Now let’s define a custom iterator. Our iterator is going to turn each image into `n` different grayscale images of varying brightness. Creating a functioning iterator is as simple as defining a Python generator function (a function that `yield`s its output) and then decorating it with `@pxt.iterator`. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} from PIL.Image import Image from PIL.ImageEnhance import Brightness from typing import Iterator, TypedDict class GrayscaleOutput(TypedDict): brightness: float grayscale_image: Image @pxt.iterator def grayscale_iterator( image: Image, *, n: int ) -> Iterator[GrayscaleOutput]: grayscale_image = image.convert('L') enhancer = Brightness(grayscale_image) for brightness in [0.5 * (i + 1) for i in range(n)]: enhanced_image = enhancer.enhance(brightness) yield { 'grayscale_image': enhanced_image, 'brightness': brightness, } ``` Notice that before defining our iterator, we first introduced a `TypedDict` class describing the content of the iterator’s output. Unlike UDFs, iterators can (and usually do) return multiple outputs. They will *always* `yield` dictionaries, and you *must* annotate the return type with a suitable `TypedDict`. This is how Pixeltable knows what types to assign to the iterator’s output columns. Defining a TypedDict for your iterator is not optional. Remember that Pixeltable is a database system, and everything must be typed! Now let’s see our iterator in action! We’ll create a view on top of the `images` table and collect the results. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} v = pxt.create_view( 'iterators_demo/grayscale', t, iterator=grayscale_iterator(t.image, n=3), ) v.head() ```
The iterator view has the columns `brightness` and `grayscale_image`, which were defined in `GrayscaleOutput`. In addition, Pixeltable added a third column `pos`. *Every* iterator will automatically output a `pos` column, regardless of what shows up in the iterator’s `TypedDict`. The `pos` column simply indicates the integer position of that row in the original iteration order. If we look at the schema of our new view, we can see that `pos` always has type `Int`. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} v ```
In addition, a column for the original input image is included for reference. (Of course, the input image is *not* copied `n` times; Pixeltable materializes it in the view by joining against the base table.) ## Parameterizing Iterators Iterators often contain complex functionality; `document_splitter`, for example, has 10 optional parameters to tune its behavior. Like UDFs, iterators can involve any number of parameters. To illustrate this, let’s add an optional `colorize` parameter to our iterator. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} from PIL import ImageOps @pxt.iterator def grayscale_iterator( image: Image, *, n: int, colorize: str | None = None ) -> Iterator[GrayscaleOutput]: grayscale_image = image.convert('L') if colorize is not None: grayscale_image = ImageOps.colorize( grayscale_image, black='black', white=colorize ) enhancer = Brightness(grayscale_image) for brightness in [0.5 * (i + 1) for i in range(n)]: enhanced_image = enhancer.enhance(brightness) yield { 'grayscale_image': enhanced_image, 'brightness': brightness, } ``` ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} v = pxt.create_view( 'iterators_demo/grayscale', t, iterator=grayscale_iterator(t.image, n=3, colorize='red'), if_exists='replace', ) v.head() ```
## Validation Often it’s desirable to validate an iterator’s inputs as a sanity check. Suppose we want to check that the `colorize` input is a valid PIL color name. That’s already being done, in a sense: when `ImageOps.colorize` is called in our iterator code, it will raise an exception if the color name is not valid. The problem is that the iterator code isn’t executed until our workflow actually runs. There’s nothing stopping us from *instantiating* instances of `grayscale_iterator` with broken inputs. To appreciate this distinction, let’s set up an empty table with no rows, and define an invalid iterator view on it. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} t = pxt.create_table( 'iterators_demo/images', {'image': pxt.Image}, if_exists='replace_force', ) ```
  Created table 'images'.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} v = pxt.create_view( 'iterators_demo/grayscale', t, iterator=grayscale_iterator( t.image, n=3, colorize='invalid_color_name' ), ) ``` The view gets created without any errors, because nothing has actually run yet! Only when we go to insert data do we see an exception. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} t.insert({'image': image} for image in images) ```
  ValueError: unknown color specifier: 'invalid\_color\_name'
  \[0;31m---------------------------------------------------------------------------\[0m
  \[0;31mValueError\[0m                                Traceback (most recent call last)
  Cell \[0;32mIn\[10], line 1\[0m
  \[0;32m----> 1\[0m \[43mt\[49m\[38;5;241;43m.\[39;49m\[43minsert\[49m\[43m(\[49m\[43m\{\[49m\[38;5;124;43m'\[39;49m\[38;5;124;43mimage\[39;49m\[38;5;124;43m'\[39;49m\[43m:\[49m\[43m \[49m\[43mimage\[49m\[43m}\[49m\[43m \[49m\[38;5;28;43;01mfor\[39;49;00m\[43m \[49m\[43mimage\[49m\[43m \[49m\[38;5;129;43;01min\[39;49;00m\[43m \[49m\[43mimages\[49m\[43m)\[49m

  File \[0;32m\~/Dropbox/workspace/pixeltable/pixeltable/pixeltable/catalog/insertable\_table.py:174\[0m, in \[0;36mInsertableTable.insert\[0;34m(self, source, source\_format, schema\_overrides, on\_error, print\_stats, \*\*kwargs)\[0m
  \[1;32m    171\[0m data\_source\[38;5;241m.\[39madd\_table\_info(table)
  \[1;32m    172\[0m data\_source\[38;5;241m.\[39mprepare\_for\_insert\_into\_table()
  \[0;32m--> 174\[0m \[38;5;28;01mreturn\[39;00m \[43mtable\[49m\[38;5;241;43m.\[39;49m\[43minsert\_table\_data\_source\[49m\[43m(\[49m
  \[1;32m    175\[0m \[43m    \[49m\[43mdata\_source\[49m\[38;5;241;43m=\[39;49m\[43mdata\_source\[49m\[43m,\[49m\[43m \[49m\[43mfail\_on\_exception\[49m\[38;5;241;43m=\[39;49m\[43mfail\_on\_exception\[49m\[43m,\[49m\[43m \[49m\[43mprint\_stats\[49m\[38;5;241;43m=\[39;49m\[43mprint\_stats\[49m
  \[1;32m    176\[0m \[43m\[49m\[43m)\[49m

  File \[0;32m\~/Dropbox/workspace/pixeltable/pixeltable/pixeltable/catalog/insertable\_table.py:195\[0m, in \[0;36mInsertableTable.insert\_table\_data\_source\[0;34m(self, data\_source, fail\_on\_exception, print\_stats)\[0m
  \[1;32m    193\[0m         status \[38;5;241m=\[39m pxt\[38;5;241m.\[39mUpdateStatus()
  \[1;32m    194\[0m         \[38;5;28;01mfor\[39;00m row\_batch \[38;5;129;01min\[39;00m data\_source\[38;5;241m.\[39mvalid\_row\_batch():
  \[0;32m--> 195\[0m             status \[38;5;241m+\[39m\[38;5;241m=\[39m \[38;5;28;43mself\[39;49m\[38;5;241;43m.\[39;49m\[43m\_tbl\_version\[49m\[38;5;241;43m.\[39;49m\[43mget\[49m\[43m(\[49m\[43m)\[49m\[38;5;241;43m.\[39;49m\[43minsert\[49m\[43m(\[49m
  \[1;32m    196\[0m \[43m                \[49m\[43mrows\[49m\[38;5;241;43m=\[39;49m\[43mrow\_batch\[49m\[43m,\[49m\[43m \[49m\[43mquery\[49m\[38;5;241;43m=\[39;49m\[38;5;28;43;01mNone\[39;49;00m\[43m,\[49m\[43m \[49m\[43mprint\_stats\[49m\[38;5;241;43m=\[39;49m\[43mprint\_stats\[49m\[43m,\[49m\[43m \[49m\[43mfail\_on\_exception\[49m\[38;5;241;43m=\[39;49m\[43mfail\_on\_exception\[49m
  \[1;32m    197\[0m \[43m            \[49m\[43m)\[49m
  \[1;32m    199\[0m Env\[38;5;241m.\[39mget()\[38;5;241m.\[39mconsole\_logger\[38;5;241m.\[39minfo(status\[38;5;241m.\[39minsert\_msg(start\_ts))
  \[1;32m    201\[0m FileCache\[38;5;241m.\[39mget()\[38;5;241m.\[39memit\_eviction\_warnings()

  File \[0;32m\~/Dropbox/workspace/pixeltable/pixeltable/pixeltable/catalog/table\_version.py:1183\[0m, in \[0;36mTableVersion.insert\[0;34m(self, rows, query, print\_stats, fail\_on\_exception)\[0m
  \[1;32m   1180\[0m         \[38;5;28;01myield\[39;00m rowid
  \[1;32m   1182\[0m \[38;5;28;01mwith\[39;00m Env\[38;5;241m.\[39mget()\[38;5;241m.\[39mreport\_progress():
  \[0;32m-> 1183\[0m     result \[38;5;241m=\[39m \[38;5;28;43mself\[39;49m\[38;5;241;43m.\[39;49m\[43m\_insert\[49m\[43m(\[49m
  \[1;32m   1184\[0m \[43m        \[49m\[43mplan\[49m\[43m,\[49m\[43m \[49m\[43mtime\[49m\[38;5;241;43m.\[39;49m\[43mtime\[49m\[43m(\[49m\[43m)\[49m\[43m,\[49m\[43m \[49m\[43mprint\_stats\[49m\[38;5;241;43m=\[39;49m\[43mprint\_stats\[49m\[43m,\[49m\[43m \[49m\[43mrowids\[49m\[38;5;241;43m=\[39;49m\[43mrowids\[49m\[43m(\[49m\[43m)\[49m\[43m,\[49m\[43m \[49m\[43mabort\_on\_exc\[49m\[38;5;241;43m=\[39;49m\[43mfail\_on\_exception\[49m
  \[1;32m   1185\[0m \[43m    \[49m\[43m)\[49m
  \[1;32m   1186\[0m     \[38;5;28;01mreturn\[39;00m result

  File \[0;32m\~/Dropbox/workspace/pixeltable/pixeltable/pixeltable/catalog/table\_version.py:1214\[0m, in \[0;36mTableVersion.*insert\[0;34m(self, exec\_plan, timestamp, rowids, print\_stats, abort\_on\_exc)\[0m
  \[1;32m   1211\[0m     \[38;5;28;01mfrom\[39;00m \[38;5;21;01mpixeltable\[39;00m\[38;5;21;01m.\[39;00m\[38;5;21;01mplan\[39;00m \[38;5;28;01mimport\[39;00m Planner
  \[1;32m   1213\[0m     view\_plan, * \[38;5;241m=\[39m Planner\[38;5;241m.\[39mcreate\_view\_load\_plan(view\[38;5;241m.\[39mget()\[38;5;241m.\[39mpath, propagates\_insert\[38;5;241m=\[39m\[38;5;28;01mTrue\[39;00m)
  \[0;32m-> 1214\[0m     status \[38;5;241m=\[39m \[43mview\[49m\[38;5;241;43m.\[39;49m\[43mget\[49m\[43m(\[49m\[43m)\[49m\[38;5;241;43m.\[39;49m\[43m\_insert\[49m\[43m(\[49m\[43mview\_plan\[49m\[43m,\[49m\[43m \[49m\[43mtimestamp\[49m\[43m,\[49m\[43m \[49m\[43mprint\_stats\[49m\[38;5;241;43m=\[39;49m\[43mprint\_stats\[49m\[43m)\[49m
  \[1;32m   1215\[0m     result \[38;5;241m+\[39m\[38;5;241m=\[39m status\[38;5;241m.\[39mto\_cascade()
  \[1;32m   1217\[0m \[38;5;66;03m# Use the net status after all propagations\[39;00m

  File \[0;32m\~/Dropbox/workspace/pixeltable/pixeltable/pixeltable/catalog/table\_version.py:1201\[0m, in \[0;36mTableVersion.\_insert\[0;34m(self, exec\_plan, timestamp, rowids, print\_stats, abort\_on\_exc)\[0m
  \[1;32m   1199\[0m \[38;5;28mself\[39m\[38;5;241m.\[39mbump\_version(timestamp, bump\_schema\_version\[38;5;241m=\[39m\[38;5;28;01mFalse\[39;00m)
  \[1;32m   1200\[0m exec\_plan\[38;5;241m.\[39mctx\[38;5;241m.\[39mtitle \[38;5;241m=\[39m \[38;5;28mself\[39m\[38;5;241m.\[39mdisplay\_str()
  \[0;32m-> 1201\[0m cols\_with\_excs, row\_counts \[38;5;241m=\[39m \[38;5;28;43mself\[39;49m\[38;5;241;43m.\[39;49m\[43mstore\_tbl\[49m\[38;5;241;43m.\[39;49m\[43minsert\_rows\[49m\[43m(\[49m
  \[1;32m   1202\[0m \[43m    \[49m\[43mexec\_plan\[49m\[43m,\[49m\[43m \[49m\[43mv\_min\[49m\[38;5;241;43m=\[39;49m\[38;5;28;43mself\[39;49m\[38;5;241;43m.\[39;49m\[43mversion\[49m\[43m,\[49m\[43m \[49m\[43mrowids\[49m\[38;5;241;43m=\[39;49m\[43mrowids\[49m\[43m,\[49m\[43m \[49m\[43mabort\_on\_exc\[49m\[38;5;241;43m=\[39;49m\[43mabort\_on\_exc\[49m
  \[1;32m   1203\[0m \[43m\[49m\[43m)\[49m
  \[1;32m   1204\[0m result \[38;5;241m=\[39m UpdateStatus(
  \[1;32m   1205\[0m     cols\_with\_excs\[38;5;241m=\[39m\[\[38;5;124mf\[39m\[38;5;124m'\[39m\[38;5;132;01m\{\[39;00m\[38;5;28mself\[39m\[38;5;241m.\[39mname\[38;5;132;01m}\[39;00m\[38;5;124m.\[39m\[38;5;132;01m\{\[39;00m\[38;5;28mself\[39m\[38;5;241m.\[39mcols\_by\_id\[cid]\[38;5;241m.\[39mname\[38;5;132;01m}\[39;00m\[38;5;124m'\[39m \[38;5;28;01mfor\[39;00m cid \[38;5;129;01min\[39;00m cols\_with\_excs],
  \[1;32m   1206\[0m     row\_count\_stats\[38;5;241m=\[39mrow\_counts,
  \[1;32m   1207\[0m )
  \[1;32m   1209\[0m \[38;5;66;03m# update views\[39;00m

  File \[0;32m\~/Dropbox/workspace/pixeltable/pixeltable/pixeltable/store.py:425\[0m, in \[0;36mStoreBase.insert\_rows\[0;34m(self, exec\_plan, v\_min, rowids, abort\_on\_exc)\[0m
  \[1;32m    420\[0m \[38;5;28;01mwith\[39;00m exec\_plan:
  \[1;32m    421\[0m     progress\_reporter \[38;5;241m=\[39m exec\_plan\[38;5;241m.\[39mctx\[38;5;241m.\[39madd\_progress\_reporter(
  \[1;32m    422\[0m         \[38;5;124mf\[39m\[38;5;124m'\[39m\[38;5;124mRows written (table \[39m\[38;5;132;01m\{\[39;00m\[38;5;28mself\[39m\[38;5;241m.\[39mtbl\_version\[38;5;241m.\[39mget()\[38;5;241m.\[39mname\[38;5;132;01m!r}\[39;00m\[38;5;124m)\[39m\[38;5;124m'\[39m, \[38;5;124m'\[39m\[38;5;124mrows\[39m\[38;5;124m'\[39m
  \[1;32m    423\[0m     )
  \[0;32m--> 425\[0m     \[38;5;28;01mfor\[39;00m row\_batch \[38;5;129;01min\[39;00m exec\_plan:
  \[1;32m    426\[0m         num\_rows \[38;5;241m+\[39m\[38;5;241m=\[39m \[38;5;28mlen\[39m(row\_batch)
  \[1;32m    427\[0m         batch\_table\_rows: \[38;5;28mlist\[39m\[\[38;5;28mtuple\[39m\[Any]] \[38;5;241m=\[39m \[]

  File \[0;32m\~/Dropbox/workspace/pixeltable/pixeltable/pixeltable/exec/exec\_node.py:63\[0m, in \[0;36mExecNode.\_\_iter\_\_\[0;34m(self)\[0m
  \[1;32m     61\[0m \[38;5;28;01mtry\[39;00m:
  \[1;32m     62\[0m     \[38;5;28;01mwhile\[39;00m \[38;5;28;01mTrue\[39;00m:
  \[0;32m---> 63\[0m         batch: DataRowBatch \[38;5;241m=\[39m \[43mloop\[49m\[38;5;241;43m.\[39;49m\[43mrun\_until\_complete\[49m\[43m(\[49m\[38;5;28;43maiter\[39;49m\[38;5;241;43m.\[39;49m\[38;5;21;43m\_\_anext\_\_\[39;49m\[43m(\[49m\[43m)\[49m\[43m)\[49m
  \[1;32m     64\[0m         \[38;5;28;01myield\[39;00m batch
  \[1;32m     65\[0m \[38;5;28;01mexcept\[39;00m \[38;5;167;01mStopAsyncIteration\[39;00m:

  File \[0;32m/opt/miniconda3/envs/pxt/lib/python3.10/site-packages/nest\_asyncio.py:99\[0m, in \[0;36m\_patch\_loop.\.run\_until\_complete\[0;34m(self, future)\[0m
  \[1;32m     96\[0m \[38;5;28;01mif\[39;00m \[38;5;129;01mnot\[39;00m f\[38;5;241m.\[39mdone():
  \[1;32m     97\[0m     \[38;5;28;01mraise\[39;00m \[38;5;167;01mRuntimeError\[39;00m(
  \[1;32m     98\[0m         \[38;5;124m'\[39m\[38;5;124mEvent loop stopped before Future completed.\[39m\[38;5;124m'\[39m)
  \[0;32m---> 99\[0m \[38;5;28;01mreturn\[39;00m \[43mf\[49m\[38;5;241;43m.\[39;49m\[43mresult\[49m\[43m(\[49m\[43m)\[49m

  File \[0;32m/opt/miniconda3/envs/pxt/lib/python3.10/asyncio/futures.py:201\[0m, in \[0;36mFuture.result\[0;34m(self)\[0m
  \[1;32m    199\[0m \[38;5;28mself\[39m\[38;5;241m.\[39m\_\_log\_traceback \[38;5;241m=\[39m \[38;5;28;01mFalse\[39;00m
  \[1;32m    200\[0m \[38;5;28;01mif\[39;00m \[38;5;28mself\[39m\[38;5;241m.\[39m\_exception \[38;5;129;01mis\[39;00m \[38;5;129;01mnot\[39;00m \[38;5;28;01mNone\[39;00m:
  \[0;32m--> 201\[0m     \[38;5;28;01mraise\[39;00m \[38;5;28mself\[39m\[38;5;241m.\[39m\_exception\[38;5;241m.\[39mwith\_traceback(\[38;5;28mself\[39m\[38;5;241m.\[39m\_exception\_tb)
  \[1;32m    202\[0m \[38;5;28;01mreturn\[39;00m \[38;5;28mself\[39m\[38;5;241m.\[39m\_result

  File \[0;32m/opt/miniconda3/envs/pxt/lib/python3.10/asyncio/tasks.py:232\[0m, in \[0;36mTask.\_\_step\[0;34m(***failed resolving arguments***)\[0m
  \[1;32m    228\[0m \[38;5;28;01mtry\[39;00m:
  \[1;32m    229\[0m     \[38;5;28;01mif\[39;00m exc \[38;5;129;01mis\[39;00m \[38;5;28;01mNone\[39;00m:
  \[1;32m    230\[0m         \[38;5;66;03m# We use the \`send\` method directly, because coroutines\[39;00m
  \[1;32m    231\[0m         \[38;5;66;03m# don't have \`**iter**\` and \`**next**\` methods.\[39;00m
  \[0;32m--> 232\[0m         result \[38;5;241m=\[39m \[43mcoro\[49m\[38;5;241;43m.\[39;49m\[43msend\[49m\[43m(\[49m\[38;5;28;43;01mNone\[39;49;00m\[43m)\[49m
  \[1;32m    233\[0m     \[38;5;28;01melse\[39;00m:
  \[1;32m    234\[0m         result \[38;5;241m=\[39m coro\[38;5;241m.\[39mthrow(exc)

  File \[0;32m\~/Dropbox/workspace/pixeltable/pixeltable/pixeltable/exec/object\_store\_save\_node.py:128\[0m, in \[0;36mObjectStoreSaveNode.\_\_aiter\_\_\[0;34m(self)\[0m
  \[1;32m    125\[0m \[38;5;28;01mwhile\[39;00m \[38;5;28;01mTrue\[39;00m:
  \[1;32m    126\[0m     \[38;5;66;03m# Create work to fill the queue to the high water mark ... ?without overrunning the in-flight row limit.\[39;00m
  \[1;32m    127\[0m     \[38;5;28;01mwhile\[39;00m \[38;5;129;01mnot\[39;00m \[38;5;28mself\[39m\[38;5;241m.\[39minput\_finished \[38;5;129;01mand\[39;00m \[38;5;28mself\[39m\[38;5;241m.\[39mqueued\_work \[38;5;241m\<\[39m \[38;5;28mself\[39m\[38;5;241m.\[39mQUEUE\_DEPTH\_HIGH\_WATER:
  \[0;32m--> 128\[0m         input\_batch \[38;5;241m=\[39m \[38;5;28;01mawait\[39;00m \[38;5;28mself\[39m\[38;5;241m.\[39mget\_input\_batch(input\_iter)
  \[1;32m    129\[0m         \[38;5;28;01mif\[39;00m input\_batch \[38;5;129;01mis\[39;00m \[38;5;129;01mnot\[39;00m \[38;5;28;01mNone\[39;00m:
  \[1;32m    130\[0m             \[38;5;28mself\[39m\[38;5;241m.\[39m\_\_process\_input\_batch(input\_batch, executor)

  File \[0;32m\~/Dropbox/workspace/pixeltable/pixeltable/pixeltable/exec/object\_store\_save\_node.py:114\[0m, in \[0;36mObjectStoreSaveNode.get\_input\_batch\[0;34m(self, input\_iter)\[0m
  \[1;32m    112\[0m \[38;5;250m\[39m\[38;5;124;03m"""Get the next batch of input rows, or None if there are no more rows"""\[39;00m
  \[1;32m    113\[0m \[38;5;28;01mtry\[39;00m:
  \[0;32m--> 114\[0m     input\_batch \[38;5;241m=\[39m \[38;5;28;01mawait\[39;00m anext(input\_iter)
  \[1;32m    115\[0m     \[38;5;28;01mif\[39;00m input\_batch \[38;5;129;01mis\[39;00m \[38;5;28;01mNone\[39;00m:
  \[1;32m    116\[0m         \[38;5;28mself\[39m\[38;5;241m.\[39minput\_finished \[38;5;241m=\[39m \[38;5;28;01mTrue\[39;00m

  File \[0;32m\~/Dropbox/workspace/pixeltable/pixeltable/pixeltable/exec/expr\_eval/expr\_eval\_node.py:298\[0m, in \[0;36mExprEvalNode.\_\_aiter\_\_\[0;34m(self)\[0m
  \[1;32m    296\[0m         \[38;5;28;01mraise\[39;00m \[38;5;28mself\[39m\[38;5;241m.\[39merror \[38;5;28;01mfrom\[39;00m \[38;5;21;01mself\[39;00m\[38;5;21;01m.\[39;00m\[38;5;21;01merror\[39;00m\[38;5;21;01m.\[39;00m\[38;5;21;01mexc\[39;00m
  \[1;32m    297\[0m     \[38;5;28;01melse\[39;00m:
  \[0;32m--> 298\[0m         \[38;5;28;01mraise\[39;00m \[38;5;28mself\[39m\[38;5;241m.\[39merror
  \[1;32m    299\[0m \[38;5;28;01mif\[39;00m completed\_aw \[38;5;129;01min\[39;00m done:
  \[1;32m    300\[0m     \[38;5;28mself\[39m\[38;5;241m.\[39m\_log\_state(\[38;5;124m'\[39m\[38;5;124mcompleted\_aw done\[39m\[38;5;124m'\[39m)

  File \[0;32m\~/Dropbox/workspace/pixeltable/pixeltable/pixeltable/exec/expr\_eval/expr\_eval\_node.py:124\[0m, in \[0;36mExprEvalNode.\_fetch\_input\_batch\[0;34m(self)\[0m
  \[1;32m    122\[0m \[38;5;28;01massert\[39;00m \[38;5;129;01mnot\[39;00m \[38;5;28mself\[39m\[38;5;241m.\[39minput\_complete
  \[1;32m    123\[0m \[38;5;28;01mtry\[39;00m:
  \[0;32m--> 124\[0m     batch \[38;5;241m=\[39m \[38;5;28;01mawait\[39;00m anext(\[38;5;28mself\[39m\[38;5;241m.\[39minput\_iter)
  \[1;32m    125\[0m     \[38;5;28;01mif\[39;00m \[38;5;28mself\[39m\[38;5;241m.\[39mprogress\_reporter \[38;5;129;01mis\[39;00m \[38;5;129;01mnot\[39;00m \[38;5;28;01mNone\[39;00m:
  \[1;32m    126\[0m         \[38;5;66;03m# make sure our progress reporter shows up before we run anything long\[39;00m
  \[1;32m    127\[0m         \[38;5;28mself\[39m\[38;5;241m.\[39mprogress\_reporter\[38;5;241m.\[39mupdate(\[38;5;241m0\[39m)

  File \[0;32m\~/Dropbox/workspace/pixeltable/pixeltable/pixeltable/exec/component\_iteration\_node.py:56\[0m, in \[0;36mComponentIterationNode.\_\_aiter\_\_\[0;34m(self)\[0m
  \[1;32m     54\[0m \[38;5;28;01mif\[39;00m \[38;5;28mself\[39m\[38;5;241m.\[39m\_\_non\_nullable\_args\_specified(iterator\_args):
  \[1;32m     55\[0m     iterator \[38;5;241m=\[39m \[38;5;28mself\[39m\[38;5;241m.\[39mview\[38;5;241m.\[39mget()\[38;5;241m.\[39miterator\_call\[38;5;241m.\[39meval(iterator\_args)
  \[0;32m---> 56\[0m     \[38;5;28;01mfor\[39;00m pos, component\_dict \[38;5;129;01min\[39;00m \[38;5;28menumerate\[39m(iterator):
  \[1;32m     57\[0m         output\_row \[38;5;241m=\[39m \[38;5;28mself\[39m\[38;5;241m.\[39mrow\_builder\[38;5;241m.\[39mmake\_row()
  \[1;32m     58\[0m         input\_row\[38;5;241m.\[39mcopy(output\_row)

  Cell \[0;32mIn\[6], line 10\[0m, in \[0;36mgrayscale\_iterator\[0;34m(image, n, colorize)\[0m
  \[1;32m      8\[0m grayscale\_image \[38;5;241m=\[39m image\[38;5;241m.\[39mconvert(\[38;5;124m'\[39m\[38;5;124mL\[39m\[38;5;124m'\[39m)
  \[1;32m      9\[0m \[38;5;28;01mif\[39;00m colorize \[38;5;129;01mis\[39;00m \[38;5;129;01mnot\[39;00m \[38;5;28;01mNone\[39;00m:
  \[0;32m---> 10\[0m     grayscale\_image \[38;5;241m=\[39m \[43mImageOps\[49m\[38;5;241;43m.\[39;49m\[43mcolorize\[49m\[43m(\[49m
  \[1;32m     11\[0m \[43m        \[49m\[43mgrayscale\_image\[49m\[43m,\[49m\[43m \[49m\[43mblack\[49m\[38;5;241;43m=\[39;49m\[38;5;124;43m'\[39;49m\[38;5;124;43mblack\[39;49m\[38;5;124;43m'\[39;49m\[43m,\[49m\[43m \[49m\[43mwhite\[49m\[38;5;241;43m=\[39;49m\[43mcolorize\[49m
  \[1;32m     12\[0m \[43m    \[49m\[43m)\[49m
  \[1;32m     13\[0m enhancer \[38;5;241m=\[39m Brightness(grayscale\_image)
  \[1;32m     14\[0m \[38;5;28;01mfor\[39;00m brightness \[38;5;129;01min\[39;00m \[\[38;5;241m0.5\[39m \[38;5;241m\*\[39m (i \[38;5;241m+\[39m \[38;5;241m1\[39m) \[38;5;28;01mfor\[39;00m i \[38;5;129;01min\[39;00m \[38;5;28mrange\[39m(n)]:

  File \[0;32m/opt/miniconda3/envs/pxt/lib/python3.10/site-packages/PIL/ImageOps.py:207\[0m, in \[0;36mcolorize\[0;34m(image, black, white, mid, blackpoint, whitepoint, midpoint)\[0m
  \[1;32m    205\[0m \[38;5;66;03m# Define colors from arguments\[39;00m
  \[1;32m    206\[0m rgb\_black \[38;5;241m=\[39m cast(Sequence\[\[38;5;28mint\[39m], \_color(black, \[38;5;124m"\[39m\[38;5;124mRGB\[39m\[38;5;124m"\[39m))
  \[0;32m--> 207\[0m rgb\_white \[38;5;241m=\[39m cast(Sequence\[\[38;5;28mint\[39m], \[43m\_color\[49m\[43m(\[49m\[43mwhite\[49m\[43m,\[49m\[43m \[49m\[38;5;124;43m"\[39;49m\[38;5;124;43mRGB\[39;49m\[38;5;124;43m"\[39;49m\[43m)\[49m)
  \[1;32m    208\[0m rgb\_mid \[38;5;241m=\[39m cast(Sequence\[\[38;5;28mint\[39m], \_color(mid, \[38;5;124m"\[39m\[38;5;124mRGB\[39m\[38;5;124m"\[39m)) \[38;5;28;01mif\[39;00m mid \[38;5;129;01mis\[39;00m \[38;5;129;01mnot\[39;00m \[38;5;28;01mNone\[39;00m \[38;5;28;01melse\[39;00m \[38;5;28;01mNone\[39;00m
  \[1;32m    210\[0m \[38;5;66;03m# Empty lists for the mapping\[39;00m

  File \[0;32m/opt/miniconda3/envs/pxt/lib/python3.10/site-packages/PIL/ImageOps.py:48\[0m, in \[0;36m\_color\[0;34m(color, mode)\[0m
  \[1;32m     45\[0m \[38;5;28;01mif\[39;00m \[38;5;28misinstance\[39m(color, \[38;5;28mstr\[39m):
  \[1;32m     46\[0m     \[38;5;28;01mfrom\[39;00m \[38;5;21;01m.\[39;00m \[38;5;28;01mimport\[39;00m ImageColor
  \[0;32m---> 48\[0m     color \[38;5;241m=\[39m \[43mImageColor\[49m\[38;5;241;43m.\[39;49m\[43mgetcolor\[49m\[43m(\[49m\[43mcolor\[49m\[43m,\[49m\[43m \[49m\[43mmode\[49m\[43m)\[49m
  \[1;32m     49\[0m \[38;5;28;01mreturn\[39;00m color

  File \[0;32m/opt/miniconda3/envs/pxt/lib/python3.10/site-packages/PIL/ImageColor.py:144\[0m, in \[0;36mgetcolor\[0;34m(color, mode)\[0m
  \[1;32m    130\[0m \[38;5;250m\[39m\[38;5;124;03m"""\[39;00m
  \[1;32m    131\[0m \[38;5;124;03mSame as :py:func:\`\~PIL.ImageColor.getrgb\` for most modes. However, if\[39;00m
  \[1;32m    132\[0m \[38;5;124;03m\`\`mode\`\` is HSV, converts the RGB value to a HSV value, or if \`\`mode\`\` is\[39;00m
  \[0;32m   (...)\[0m
  \[1;32m    141\[0m \[38;5;124;03m:return: \`\`graylevel, (graylevel, alpha) or (red, green, blue\[, alpha])\`\`\[39;00m
  \[1;32m    142\[0m \[38;5;124;03m"""\[39;00m
  \[1;32m    143\[0m \[38;5;66;03m# same as getrgb, but converts the result to the given mode\[39;00m
  \[0;32m--> 144\[0m rgb, alpha \[38;5;241m=\[39m \[43mgetrgb\[49m\[43m(\[49m\[43mcolor\[49m\[43m)\[49m, \[38;5;241m255\[39m
  \[1;32m    145\[0m \[38;5;28;01mif\[39;00m \[38;5;28mlen\[39m(rgb) \[38;5;241m==\[39m \[38;5;241m4\[39m:
  \[1;32m    146\[0m     alpha \[38;5;241m=\[39m rgb\[\[38;5;241m3\[39m]

  File \[0;32m/opt/miniconda3/envs/pxt/lib/python3.10/site-packages/PIL/ImageColor.py:125\[0m, in \[0;36mgetrgb\[0;34m(color)\[0m
  \[1;32m    123\[0m     \[38;5;28;01mreturn\[39;00m \[38;5;28mint\[39m(m\[38;5;241m.\[39mgroup(\[38;5;241m1\[39m)), \[38;5;28mint\[39m(m\[38;5;241m.\[39mgroup(\[38;5;241m2\[39m)), \[38;5;28mint\[39m(m\[38;5;241m.\[39mgroup(\[38;5;241m3\[39m)), \[38;5;28mint\[39m(m\[38;5;241m.\[39mgroup(\[38;5;241m4\[39m))
  \[1;32m    124\[0m msg \[38;5;241m=\[39m \[38;5;124mf\[39m\[38;5;124m"\[39m\[38;5;124munknown color specifier: \[39m\[38;5;132;01m\{\[39;00m\[38;5;28mrepr\[39m(color)\[38;5;132;01m}\[39;00m\[38;5;124m"\[39m
  \[0;32m--> 125\[0m \[38;5;28;01mraise\[39;00m \[38;5;167;01mValueError\[39;00m(msg)

  \[0;31mValueError\[0m: unknown color specifier: 'invalid\_color\_name'
It’s more useful to do *fail-fast validation*, in which the arguments get checked at the time the iterator is first instantiated. This can be done in Pixeltable with the `@validate` decorator. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} from PIL import ImageColor @grayscale_iterator.validate def _(bound_args: dict): color = bound_args.get('colorize') if color is not None: try: ImageColor.getrgb(color) except ValueError as exc: raise ValueError(f'Invalid color name: {color}') from exc ``` Now if we try to create an invalid instance, we get an error right away. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} t = pxt.create_table( 'iterators_demo/images', {'input': pxt.Image}, if_exists='replace_force', ) ```
  Created table 'images'.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} v = pxt.create_view( 'iterators_demo/grayscale', t, iterator=grayscale_iterator( t.input, n=3, colorize='invalid_color_name' ), ) ```
  ValueError: Invalid color name: invalid\_color\_name
  \[0;31m---------------------------------------------------------------------------\[0m
  \[0;31mValueError\[0m                                Traceback (most recent call last)
  Cell \[0;32mIn\[11], line 9\[0m, in \[0;36m\_\[0;34m(bound\_args)\[0m
  \[1;32m      8\[0m \[38;5;28;01mtry\[39;00m:
  \[0;32m----> 9\[0m     \[43mImageColor\[49m\[38;5;241;43m.\[39;49m\[43mgetrgb\[49m\[43m(\[49m\[43mcolor\[49m\[43m)\[49m
  \[1;32m     10\[0m \[38;5;28;01mexcept\[39;00m \[38;5;167;01mValueError\[39;00m \[38;5;28;01mas\[39;00m exc:

  File \[0;32m/opt/miniconda3/envs/pxt/lib/python3.10/site-packages/PIL/ImageColor.py:125\[0m, in \[0;36mgetrgb\[0;34m(color)\[0m
  \[1;32m    124\[0m msg \[38;5;241m=\[39m \[38;5;124mf\[39m\[38;5;124m"\[39m\[38;5;124munknown color specifier: \[39m\[38;5;132;01m\{\[39;00m\[38;5;28mrepr\[39m(color)\[38;5;132;01m}\[39;00m\[38;5;124m"\[39m
  \[0;32m--> 125\[0m \[38;5;28;01mraise\[39;00m \[38;5;167;01mValueError\[39;00m(msg)

  \[0;31mValueError\[0m: unknown color specifier: 'invalid\_color\_name'

  The above exception was the direct cause of the following exception:

  \[0;31mValueError\[0m                                Traceback (most recent call last)
  Cell \[0;32mIn\[13], line 4\[0m
  \[1;32m      1\[0m v \[38;5;241m=\[39m pxt\[38;5;241m.\[39mcreate\_view(
  \[1;32m      2\[0m     \[38;5;124m'\[39m\[38;5;124miterators\_demo/grayscale\[39m\[38;5;124m'\[39m,
  \[1;32m      3\[0m     t,
  \[0;32m----> 4\[0m     iterator\[38;5;241m=\[39m\[43mgrayscale\_iterator\[49m\[43m(\[49m
  \[1;32m      5\[0m \[43m        \[49m\[43mt\[49m\[38;5;241;43m.\[39;49m\[43minput\[49m\[43m,\[49m\[43m \[49m\[43mn\[49m\[38;5;241;43m=\[39;49m\[38;5;241;43m3\[39;49m\[43m,\[49m\[43m \[49m\[43mcolorize\[49m\[38;5;241;43m=\[39;49m\[38;5;124;43m'\[39;49m\[38;5;124;43minvalid\_color\_name\[39;49m\[38;5;124;43m'\[39;49m
  \[1;32m      6\[0m \[43m    \[49m\[43m)\[49m,
  \[1;32m      7\[0m )

  File \[0;32m\~/Dropbox/workspace/pixeltable/pixeltable/pixeltable/func/iterator.py:233\[0m, in \[0;36mGeneratingFunction.\_\_call\_\_\[0;34m(self, \*args, \*\*kwargs)\[0m
  \[1;32m    231\[0m \[38;5;66;03m# Run custom iterator validation on whatever args are bound to literals at this stage\[39;00m
  \[1;32m    232\[0m \[38;5;28;01mif\[39;00m \[38;5;28mself\[39m\[38;5;241m.\[39m\_validate \[38;5;129;01mis\[39;00m \[38;5;129;01mnot\[39;00m \[38;5;28;01mNone\[39;00m:
  \[0;32m--> 233\[0m     \[38;5;28;43mself\[39;49m\[38;5;241;43m.\[39;49m\[43m\_validate\[49m\[43m(\[49m\[43mliteral\_args\[49m\[43m)\[49m
  \[1;32m    235\[0m output\_schema \[38;5;241m=\[39m \[38;5;28mself\[39m\[38;5;241m.\[39mcall\_output\_schema(literal\_args)
  \[1;32m    237\[0m outputs \[38;5;241m=\[39m \{
  \[1;32m    238\[0m     name: IteratorOutput(orig\_name\[38;5;241m=\[39mname, is\_stored\[38;5;241m=\[39m(name \[38;5;129;01mnot\[39;00m \[38;5;129;01min\[39;00m \[38;5;28mself\[39m\[38;5;241m.\[39munstored\_cols), col\_type\[38;5;241m=\[39mcol\_type)
  \[1;32m    239\[0m     \[38;5;28;01mfor\[39;00m name, col\_type \[38;5;129;01min\[39;00m output\_schema\[38;5;241m.\[39mitems()
  \[1;32m    240\[0m }

  Cell \[0;32mIn\[11], line 11\[0m, in \[0;36m\_\[0;34m(bound\_args)\[0m
  \[1;32m      9\[0m     ImageColor\[38;5;241m.\[39mgetrgb(color)
  \[1;32m     10\[0m \[38;5;28;01mexcept\[39;00m \[38;5;167;01mValueError\[39;00m \[38;5;28;01mas\[39;00m exc:
  \[0;32m---> 11\[0m     \[38;5;28;01mraise\[39;00m \[38;5;167;01mValueError\[39;00m(\[38;5;124mf\[39m\[38;5;124m'\[39m\[38;5;124mInvalid color name: \[39m\[38;5;132;01m\{\[39;00mcolor\[38;5;132;01m}\[39;00m\[38;5;124m'\[39m) \[38;5;28;01mfrom\[39;00m \[38;5;21;01mexc\[39;00m

  \[0;31mValueError\[0m: Invalid color name: invalid\_color\_name
The input to `validate()`, `bound_args`, is a dictionary that contains all *constant* arguments for a particular instance of the iterator. In the above example, it contains `colorize` (because it’s equal to the constant value `'invalid_color_name'`), but not `image` (which depends dynamically on the data in the `t.input` column). `validate()` will actually be called twice: once when the iterator is instantiated, with just the constant arguments present in `bound_args`; and again when the iterator is evaluated on each row, this time with *all* arguments present. ## Class-Based Iterators For complex iterators that need to maintain a lot of state or provide fine-grained control over their iteration mechanism, it can be convenient to define a class rather than a generator function. This can be done by writing a subclass of `PxtIterator` and decorating the class, rather than decorating a function. Here’s what `grayscale_iterator` looks like if written as a class; it is functionally identical to the earlier implementation. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.iterator class grayscale_iterator(pxt.PxtIterator[GrayscaleOutput]): # The parameters of __init__() determine the iterator arguments def __init__( self, image: Image, *, n: int, colorize: str | None = None ): self.image = image self.n = n self.colorize = colorize self.idx = 0 grayscale_image = self.image.convert('L') if self.colorize is not None: grayscale_image = ImageOps.colorize( grayscale_image, black='black', white=self.colorize ) self.enhancer = Brightness(grayscale_image) # Every class-based iterator *must* implement a __next__() method # whose return type is a `TypedDict`. def __next__(self) -> GrayscaleOutput: if self.idx >= self.n: raise StopIteration brightness = 0.5 * (self.idx + 1) enhanced_image = self.enhancer.enhance(brightness) self.idx += 1 return { 'grayscale_image': enhanced_image, 'brightness': brightness, } # When defining a class-based iterator, validate() can optionally be specified # as a @classmethod rather than a standalone decorated function. @classmethod def validate(cls, bound_args: dict): color = bound_args.get('colorize') if color is not None: try: ImageColor.getrgb(color) except ValueError as exc: raise ValueError(f'Invalid color name: {color}') from exc ``` ## Unstored Columns That’s all you need to know to implement fully functional iterators. But sometimes, depending on the nature of the outputs, a little extra work will help make them more performant. In our example, every input image gets turned into `n` output images. Moreover, recreating those output images doesn’t involve a lot of computation: it’s just a simple color mask. If we store every output image as a separate file, then when `n` is large we’ll be using up a lot of storage without much benefit. Even at `n=3`, the outputs will consume 3x the storage as the inputs (maybe a little less since they’re monochrome now, but you get the idea). Just as with computed columns, Pixeltable provides an option for iterator outputs to be *unstored* - meaning the outputs won’t be saved to disk, and they’ll instead be dynamically regenerated each time a client queries them. Unstored columns don’t provide much benefit for scalar columns (integers or strings, say), where the storage footprint is small; or for expensive computations (such as generative model outputs), where we actually *do* want to persist the output. But for simple image operations, they can be a lifesaver. In the Pixeltable library, frame\_iterator and tile\_iterator both use an unstored column for the output images. In the case of frame\_iterator, the output is potentially *huge*, because video data is highly compressed, as compared to individually stored frame images. To mark an iterator output as unstored, use the `unstored_cols` decorator parameter. There is one important caveat: * If you use unstored columns, you *must* implement your iterator as a class-based iterator; and * You *must* implement a `seek()` method in your class, as in the example below. This is to ensure Pixeltable has efficient random access to the iterator outputs, to facilitate downstream queries against the iterator view. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Mark `grayscale_image` as an unstored column. @pxt.iterator(unstored_cols=['grayscale_image']) class grayscale_iterator(pxt.PxtIterator[GrayscaleOutput]): def __init__( self, image: Image, *, n: int, colorize: str | None = None ): self.image = image self.n = n self.colorize = colorize self.idx = 0 grayscale_image = self.image.convert('L') if self.colorize is not None: grayscale_image = ImageOps.colorize( grayscale_image, black='black', white=self.colorize ) self.enhancer = Brightness(grayscale_image) def __next__(self) -> GrayscaleOutput: if self.idx >= self.n: raise StopIteration brightness = 0.5 * (self.idx + 1) enhanced_image = self.enhancer.enhance(brightness) self.idx += 1 return { 'grayscale_image': enhanced_image, 'brightness': brightness, } # seek() will always receive the `pos` of the row being sought. It # will also receive the previously stored values of any *stored* # output columns in the target row, as keyword arguments. def seek(self, pos: int, **kwargs): assert 0 <= pos < self.n # 'brightness' is a stored column, so it should always be # present. We don't need it to implement seek(), but for # purposes of illustration let's check that it's here. assert 'brightness' in kwargs self.idx = pos # Reset the iterator to the sought position. # When defining a class-based iterator, validate() can optionally # be a @classmethod rather than a standalone decorated function. @classmethod def validate(cls, bound_args: dict): color = bound_args.get('colorize') if color is not None: try: ImageColor.getrgb(color) except ValueError as exc: raise ValueError(f'Invalid color name: {color}') from exc ``` There it is: a complete, performant implementation of `grayscale_iterator`. Let’s check one more time that it all works as expected. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} t = pxt.create_table( 'iterators_demo/images', {'image': pxt.Image}, if_exists='replace_force', ) t.insert({'image': image} for image in images) ```
  Inserted 2 rows with 0 errors in 0.03 s (75.79 rows/s)
  2 rows inserted.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} v = pxt.create_view( 'iterators_demo/grayscale', t, iterator=grayscale_iterator(t.image, n=3), ) v.head() ```
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Check that we have random access to arbitrary rows in the view. v.where(v.pos == 2).collect() ```
# Split data into multiple rows with iterators Source: https://docs.pixeltable.com/howto/cookbooks/core/data-split-rows Open in Kaggle  Open in Colab  Download Notebook This documentation page is also available as an interactive notebook. You can launch the notebook in Kaggle or Colab, or download it for use with an IDE or local Jupyter installation, by clicking one of the above links. Transform a single document, video, image, or audio file into multiple rows for granular processing. **What’s in this recipe:** * Split documents into text chunks for RAG * Extract frames or segments from videos * Tile images for high-resolution analysis * Chunk audio files for transcription ## Problem You have documents, videos, or text that you need to break into smaller pieces for processing. A PDF needs to be split into chunks for retrieval-augmented generation. A video needs individual frames for analysis. Text needs to be divided into sentences or sliding windows. You need a way to transform one source row into multiple output rows automatically. ## Solution You create views with iterator functions that split source data into multiple rows. Pixeltable provides built-in iterators for documents, videos, images, audio, and strings. ### Setup ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} %pip install -qU pixeltable spacy tiktoken click !python -m spacy download en_core_web_sm -q ``` ### Split documents into chunks Use `document_splitter` to break documents (PDF, HTML, Markdown, TXT) into text chunks. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} import pixeltable as pxt from pixeltable.functions.document import document_splitter pxt.drop_dir('split_demo', force=True) pxt.create_dir('split_demo') docs = pxt.create_table('split_demo/docs', {'doc': pxt.Document}) docs.insert( [ { 'doc': 'https://raw.githubusercontent.com/pixeltable/pixeltable/main/docs/resources/rag-demo/Jefferson-Amazon.pdf' } ] ) ```
  Inserted 1 row with 0 errors in 0.13 s (7.68 rows/s)
  1 row inserted.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} chunks = pxt.create_view( 'split_demo/doc_chunks', docs, iterator=document_splitter( docs.doc, separators='sentence,token_limit', limit=300 ), ) chunks.select(chunks.text).limit(3).collect() ```
**Available separators:** * `heading` — Split on HTML/Markdown headings * `sentence` — Split on sentence boundaries (requires spacy) * `token_limit` — Split by token count (requires tiktoken) * `char_limit` — Split by character count * `page` — Split by page (PDF only) [SDK Reference: document\_splitter](/sdk/latest/document) ### Extract frames from videos Use `frame_iterator` to extract frames at specified intervals. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} from pixeltable.functions.video import frame_iterator videos = pxt.create_table('split_demo/videos', {'video': pxt.Video}) videos.insert( [ { 'video': 'https://github.com/pixeltable/pixeltable/raw/main/docs/resources/bangkok.mp4' } ] ) ```
  Inserted 1 row with 0 errors in 1.28 s (0.78 rows/s)
  1 row inserted.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} frames = pxt.create_view( 'split_demo/frames', videos, iterator=frame_iterator(videos.video, fps=1.0), ) frames.select(frames.frame, frames.frame_attrs).limit(3).collect() ```
**frame\_iterator options:** * `fps` — Frames per second to extract * `num_frames` — Extract exact number of frames (evenly spaced) * `keyframes_only` — Extract only keyframes [SDK Reference: frame\_iterator](/sdk/latest/video) ### Split videos into segments Use `video_splitter` to divide videos into smaller clips. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} from pixeltable.functions.video import video_splitter segments = pxt.create_view( 'split_demo/segments', videos, iterator=video_splitter( videos.video, duration=5.0, min_segment_duration=1.0 ), ) segments.select( segments.segment_start, segments.segment_end, segments.video_segment ).limit(3).collect() ```
**video\_splitter options:** * `duration` — Duration of each segment in seconds * `overlap` — Overlap between segments in seconds * `min_segment_duration` — Drop last segment if shorter than this [SDK Reference: video\_splitter](/sdk/latest/video) ### Split strings into sentences Use `string_splitter` to divide text into sentences. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} from pixeltable.functions.string import string_splitter texts = pxt.create_table('split_demo/texts', {'content': pxt.String}) texts.insert( [ { 'content': 'AI data infrastructure simplifies ML workflows. Declarative pipelines update incrementally. This makes development faster and more maintainable.' } ] ) ```
  Inserted 1 row with 0 errors in 0.03 s (38.38 rows/s)
  1 row inserted.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} sentences = pxt.create_view( 'split_demo/sentences', texts, iterator=string_splitter(texts.content, separators='sentence'), ) sentences.select(sentences.text).collect() ```
[SDK Reference: string\_splitter](/sdk/latest/string) ### Tile images for analysis Use `tile_iterator` to divide large images into a grid of smaller tiles. This is useful for processing high-resolution images that are too large to analyze at once, or for running object detection on different regions. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} from pixeltable.functions.image import tile_iterator images = pxt.create_table('split_demo/images', {'image': pxt.Image}) images.insert( [ { 'image': 'https://raw.githubusercontent.com/pixeltable/pixeltable/main/docs/resources/pixeltable-logo-large.png' } ] ) ```
  Inserted 1 row with 0 errors in 0.09 s (11.69 rows/s)
  1 row inserted.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} tiles = pxt.create_view( 'split_demo/tiles', images, iterator=tile_iterator(images.image, tile_size=(100, 100)), ) ``` **tile\_iterator options:** * `tile_size` — Size of each tile as `(width, height)` * `overlap` — Overlap between adjacent tiles as `(width, height)` [SDK Reference: tile\_iterator](/sdk/latest/image) ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} tiles.select(tiles.tile_coord, tiles.tile).sample(n=4).collect() ```
### Split audio into chunks Use `audio_splitter` to divide audio files into time-based segments for transcription or analysis. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} from pixeltable.functions.audio import audio_splitter audio = pxt.create_table('split_demo/audio', {'audio': pxt.Audio}) audio.insert( [ { 'audio': 'https://raw.githubusercontent.com/pixeltable/pixeltable/main/docs/resources/10-minute%20tour%20of%20Pixeltable.mp3' } ] ) ```
  Inserted 1 row with 0 errors in 0.67 s (1.50 rows/s)
  1 row inserted.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} audio_segments = pxt.create_view( 'split_demo/audio_chunks', audio, iterator=audio_splitter(audio.audio, duration=30.0, overlap=2.0), ) audio_segments.select( audio_segments.segment_start, audio_segments.segment_end ).limit(5).collect() ```
**audio\_splitter options:** * `duration` — Duration of each chunk in seconds * `overlap` — Overlap between chunks in seconds * `min_segment_duration` — Drop last chunk if shorter than this [SDK Reference: audio\_splitter](/sdk/latest/audio) ## See also * [Split documents for RAG](/howto/cookbooks/text/doc-chunk-for-rag) * [Extract frames from videos](/howto/cookbooks/video/video-extract-frames) * [Transcribe audio files](/howto/cookbooks/audio/audio-transcribe) # Get fast feedback on transformations Source: https://docs.pixeltable.com/howto/cookbooks/core/dev-iterative-workflow Open in Kaggle  Open in Colab  Download Notebook This documentation page is also available as an interactive notebook. You can launch the notebook in Kaggle or Colab, or download it for use with an IDE or local Jupyter installation, by clicking one of the above links. ## Problem You need to iterate on transformation logic before running it on your entire dataset—especially for expensive operations like API calls or model inference. ## Solution **What’s in this recipe:** * Test transformations on sample rows before applying to your full dataset * Save expressions as variables to guarantee consistent logic * Apply the iterate-then-add workflow with built-in functions, expressions, and custom UDFs * Annotate columns with comments and custom metadata using `ColumnSpec` You test transformation logic on sample rows before processing your entire dataset using the iterate-then-add workflow. This lets you validate logic on a few rows before committing to your full table. You use `.select()` with `.collect()` to preview transformations—nothing is stored in your table. If you want to collect only the first few rows, use `.head(n)` instead of `.collect()`. Once you’re satisfied with the results, use `.add_computed_column()` with the same expression to persist the transformation across your full table. This workflow applies to any data type in Pixeltable: images, videos, audio files, documents, and structured tabular data. This recipe uses text data and shows three examples: 1. Testing built-in functions on sample data 2. Saving expressions as variables to ensure consistency 3. Iterating with custom user-defined functions (UDFs) ### Setup ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} %pip install -qU pixeltable ``` ### Create sample data ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} import pixeltable as pxt # Create a fresh directory (drop existing if present) pxt.drop_dir('demo_project', force=True) pxt.create_dir('demo_project') ```
  Connected to Pixeltable database at: postgresql+psycopg://postgres:@/pixeltable?host=/Users/pjlb/.pixeltable/pgdata
  Created directory 'demo\_project'.
  \
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} t = pxt.create_table('demo_project/lyrics', {'text': pxt.String}) ```
  Created table 'lyrics'.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} t.insert( [ {'text': 'Tumble out of bed and I stumble to the kitchen'}, {'text': 'Pour myself a cup of ambition'}, {'text': 'And yawn and stretch and try to come to life'}, {'text': "Jump in the shower and the blood starts pumpin'"}, {'text': "Out on the street, the traffic starts jumpin'"}, {'text': 'With folks like me on the job from nine to five'}, ] ) ```
  Inserted 6 rows with 0 errors in 0.01 s (916.65 rows/s)
  6 rows inserted.
### Example 1: built-in functions Iterate with built-in functions, then add to the table. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Test uppercase transformation on subset t.select(t.text, uppercase=t.text.upper()).head(2) ```
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Confirm the transformation was only in memory—table unchanged t.head(2) ```
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Apply to all rows (same expression) t.add_computed_column(uppercase=t.text.upper()) ```
  Added 6 column values with 0 errors in 0.04 s (158.08 rows/s)
  6 rows updated.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # View text with uppercase column t.collect() ```
### Example 2: save and reuse expressions Save an expression as a variable to guarantee the same logic in both iterate and add steps. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Define the expression once - no duplication char_count_expr = t.text.len() # Iterate: Test on subset t.select(t.text, char_count=char_count_expr).head(2) ```
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Confirm the transformation was only in memory—table unchanged t.head(2) ```
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Add: Use the SAME expression to persist t.add_computed_column(char_count=char_count_expr) ```
  Added 6 column values with 0 errors in 0.02 s (348.64 rows/s)
  6 rows updated.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # View text with char_count column t.collect() ```
This pattern works with any expression: * Built-in functions: `resize_expr = t.image.resize((224, 224))` * UDFs: `watermark_expr = add_watermark(t.image, '© 2024')` * Chained operations: `processed_expr = t.image.resize((224, 224)).rotate(90)` Benefits: * Write the expression once, use it twice * No copy-paste—reuse the same logic * Easy to iterate: change in one place, test again ### Example 3: custom UDF Iterate with a user-defined function, then add to the table. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Define a custom transformation @pxt.udf def word_count(text: str) -> int: return len(text.split()) ``` ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Iterate: Test UDF on subset t.select(t.text, word_count=word_count(t.text)).head(2) ```
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Confirm the transformation was only in memory—table unchanged t.head(2) ```
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Add: Apply to all rows (same expression) t.add_computed_column(word_count=word_count(t.text)) ```
  Added 6 column values with 0 errors in 0.02 s (312.11 rows/s)
  6 rows updated.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # View text with word_count column t.collect() ```
### Example 4: annotate columns with metadata Use `ColumnSpec` to attach a comment or custom metadata when adding columns. Comments appear in `describe()` output, while `custom_metadata` stores arbitrary data (tags, version info, config) that you can retrieve with `get_metadata()`. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} from pixeltable.types import ColumnSpec # Add a column with a comment and custom metadata t.add_column( source=ColumnSpec( type=(pxt.String | None), comment='Original source URL or file path', custom_metadata={'added_by': 'data_team', 'version': 2}, ) ) t.describe() ```
## Explanation **How the iterate-then-add workflow works:** Queries and computed columns serve different purposes. Queries let you test transformations on sample rows without storing anything. Once you’re satisfied with the results, you use the exact same expression with `.add_computed_column()` to persist it across your entire table. This workflow is especially valuable for expensive operations—API calls, model inference, complex image processing—where you want to validate logic before processing your full dataset. Test on 2-3 rows to catch errors early, then commit once. **To customize this workflow:** * **Sample size**: Use `.head(n)` to collect only the first n rows—`.head(1)` for single-row testing, `.head(10)` for broader validation, or `.collect()` to collect all rows * **Save expressions**: Store transformations as variables (Example 2) to guarantee identical logic in both iterate and add steps * **Chain transformations**: Test multiple operations together—`.select(t.text.upper().split())` works just like single operations * **Use with any data type**: This pattern works with images, videos, audio, documents—not just text. For multimodal data, visual inspection during iteration is especially valuable **The Pixeltable workflow:** In traditional databases, `.select()` just picks which columns to view. In Pixeltable, `.select()` also lets you compute new transformations on the fly—define new columns without storing them. This makes `.select()` perfect for testing transformations before you commit them. When you use `.select()`, you’re creating a query. Queries are temporary operations that retrieve and transform data from tables—they don’t store anything. Queries use lazy evaluation, meaning they don’t execute until you call `.collect()`. You must use `.collect()` to execute the query and return results. `.head(n)` is a convenience method that collects only the first n rows instead of all rows. Use `.head(n)` when iterating to get fast feedback without processing your entire dataset. Nothing is stored in your table when you run queries. You can test different approaches quickly without affecting your data. You can store query results in a Python variable to work with them in your session. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Store query results as a variable (in memory only) results = t.select( t.text, uppercase=t.text.upper() # Label the transformed column ).head(3) ``` These results are stored in memory and will not persist across sessions—only `.add_computed_column()` persists data to your table. Once you’re satisfied, `.add_computed_column()` uses the same expression but adds it as a persistent column in your table. Now the transformation runs on all rows and results are stored permanently. ## See also * [Transform images with PIL operations](/howto/cookbooks/images/img-pil-transforms) * [Convert RGB images to grayscale](/howto/cookbooks/images/img-rgb-to-grayscale) * [Apply filters to images](/howto/cookbooks/images/img-apply-filters) # Your Backend for Multimodal AI Applications Source: https://docs.pixeltable.com/howto/cookbooks/core/multimodal_backend Open in Kaggle  Open in Colab  Download Notebook This documentation page is also available as an interactive notebook. You can launch the notebook in Kaggle or Colab, or download it for use with an IDE or local Jupyter installation, by clicking one of the above links. ### Why Pixeltable This notebook uses `pxt.create_table()` so you can run cells without a project. An app puts the same columns on a `TableModel` in `app.py` and creates the tables with `pxt schema update`. Every multimodal AI app needs the same five things: store media, run models, index embeddings, serve endpoints, version everything. Most teams glue together 5-8 services (Postgres + Pinecone + S3 + Airflow + LangChain + …) and spend more time on infrastructure than on the product. **Pixeltable is a single system that handles all five.** One `pip install`, one Python API, one place to store, transform, index, retrieve, serve, version, observe, and debug.
**For developers and vibe coders:** Pixeltable’s declarative API means AI assistants generate correct, production-grade code. No glue logic, no orchestrator configs, no serialization code. Experimenting on multimodal data (extract a frame, run a model, draw bounding boxes) is one expression, not a pipeline. Install the [Pixeltable Skill](https://github.com/pixeltable/pixeltable-skill) and prompt. **For teams evaluating infrastructure:** Transaction integrity, async execution, parallelization, caching, retries, and observability are built in. One system to operate, monitor, and maintain. Schema changes are one line. Model upgrades are zero-downtime. Extensible via `@pxt.udf`, `@pxt.uda`, `@pxt.query`. [20+ AI providers](/integrations/frameworks) built in. [Skill](https://github.com/pixeltable/pixeltable-skill) | [MCP Server](https://github.com/pixeltable/mcp-server-pixeltable-developer) \| [Starter Kit](https://github.com/pixeltable/pixeltable-starter-kit) \| `llms.txt`: docs.pixeltable.com/llms.txt ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} %pip install -qU pixeltable google-genai 'fastapi[standard]' %pip install -qU torch torchvision transformers # optional, for object detection ```
  Note: you may need to restart the kernel to use updated packages.
  Note: you may need to restart the kernel to use updated packages.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} import getpass import logging import os import warnings warnings.filterwarnings('ignore') logging.getLogger('asyncio').setLevel(logging.CRITICAL) logging.getLogger('huggingface_hub').setLevel(logging.CRITICAL) if ( 'GEMINI_API_KEY' not in os.environ and 'GOOGLE_API_KEY' not in os.environ ): os.environ['GEMINI_API_KEY'] = getpass.getpass('Gemini API Key: ') import pixeltable as pxt from pixeltable.functions import gemini BASE_URL = 'https://raw.githubusercontent.com/pixeltable/pixeltable/release/docs/resources' ```

1. Store: Multimodal Tables

Video, audio, images, and documents are first-class column types. `pip install pixeltable` is all you need. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} from pixeltable.functions.uuid import uuid7 pxt.drop_dir('demo', force=True, if_not_exists='ignore') pxt.create_dir('demo') videos = pxt.create_table( 'demo/videos', {'id': uuid7(), 'video': pxt.Video, 'title': pxt.String}, primary_key='id', ) videos ```
  Created directory 'demo'.
  Created table 'videos'.

2. Orchestrate: AI as Computed Columns

Add a computed column; Pixeltable calls Gemini on every insert, caches results, retries failures, keeps embeddings in sync. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} videos.add_computed_column( response=gemini.generate_content( [videos.video, 'Describe this video in detail.'], model='gemini-3-flash-preview', ) ) videos.add_computed_column( description=videos.response.candidates[0] .content.parts[0] .text.astype(pxt.String) ) videos.add_embedding_index( 'description', embedding=gemini.embed_content.using( model='gemini-embedding-2-preview' ), ) ```
  Added 0 column values with 0 errors in 0.01 s
  Added 0 column values with 0 errors in 0.03 s

3. Insert: One Call Triggers the Full Pipeline

`insert()` downloads videos, runs Gemini, extracts text, computes embeddings. Open the **Dashboard** to watch in real time. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} videos.insert( [ { 'video': f'{BASE_URL}/bangkok.mp4', 'title': 'Bangkok Street Tour', }, { 'video': f'{BASE_URL}/The-Pursuit-of-Happiness-Video-Extract.mp4', 'title': 'The Pursuit of Happiness', }, ] ) ```
  Inserted 2 rows with 0 errors in 22.85 s (0.09 rows/s)
  2 rows inserted.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} videos = pxt.get_table('demo/videos') videos.select(videos.title, videos.description).collect() ```

4. Retrieve: Semantic Search

Embedding index stays in sync automatically. No separate vector DB. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} sim = videos.description.similarity(string='street food') # Filter + rank in one expression videos.where(videos.description != None).order_by(sim, asc=False).limit( 5 ).select(videos.title, videos.description, sim).collect() ```

5. Experiment on Media Data

Extract a frame, run DETR object detection, draw bounding boxes, all in one expression. Change `timestamp` and re-run to explore different frames. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} from pixeltable.functions import huggingface from pixeltable.functions.vision import bboxes_draw frame = videos.video.extract_frame(timestamp=2.0) detections = huggingface.detr_for_object_detection( frame, model_id='facebook/detr-resnet-50' ) videos.select( videos.title, annotated=bboxes_draw( frame, boxes=detections.boxes, labels=detections.label_text, fill=True, fill_alpha=0.15, width=2, font_size=14, ), ).collect() ```
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} videos.describe() ```

6. Serve: Queries Become API Endpoints

`@pxt.query` becomes an HTTP endpoint via `FastAPIRouter`. In production, declare the service in an application file and run `pxt service update`. See [HTTP Serving](/howto/deployment/serving). ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} import fastapi from fastapi.testclient import TestClient from pixeltable.serving import FastAPIRouter @pxt.query def search_videos(query_text: str, limit: int = 5): sim = videos.description.similarity(string=query_text) return ( videos.order_by(sim, asc=False) .limit(limit) .select(videos.title, videos.description, sim) ) app = fastapi.FastAPI() router = FastAPIRouter() router.add_query_route(path='/search', query=search_videos) router.add_insert_route( videos, path='/ingest', inputs=['video', 'title'], outputs=['id', 'title', 'description'], ) router.add_delete_route(videos, path='/delete') app.include_router(router) client = TestClient(app) ``` ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} resp = client.post( '/search', json={'query_text': 'street food', 'limit': 2} ) resp.json() ```
  \{'rows': \[\{'title': 'The Pursuit of Happiness',
     'description': 'In this clip from the movie "The Pursuit of Happyness," Chris Gardner (played by Will Smith) has just finished an interview for a competitive internship at a brokerage firm. Despite his disheveled appearance—wearing a grey work jacket and looking tired—he is approached by Jay Twistle, a senior manager at the firm.\n\n**Detailed Scene Breakdown:**\n\n\* **The Approach:** The scene opens with Chris looking down, appearing stressed or emotional. A voice calls out "Chris...", and he turns to see Mr. Twistle walking toward him with a wide, congratulatory smile. They are in a professional office lobby with people moving in the background and a reception desk visible.\n\* **The Interaction:** Mr. Twistle expresses his admiration, saying, "I don\'t know how you did it dressed as a garbage man, but you really pulled it off in there." This refers to Chris\'s impressive performance during the interview despite his unconventional attire (having come straight from a night in a jail cell due to unpaid parking tickets).\n\* **Building Rapport:** Chris politely thanks him, addressing him as "Mr. Twistle." In a sign of newfound respect and a positive result, Twistle insists, "Hey, now you can call me Jay. We\'ll talk to you soon." He gives Chris a friendly pat on the shoulder before walking away.\n\* **Gardner\'s Reaction:** Chris is left standing in the hallway, a look of immense relief and quiet triumph washing over his face. The scene highlights a pivotal moment where his intelligence and determination overcame his difficult circumstances.\n\nThe video features the "Binge Society" logo in the top left corner and copyright information at the bottom for Columbia Pictures Industries, Inc. and GH One LLC from 2006.',
     'similarity': 0.4487778141613705},
    \{'title': 'Bangkok Street Tour',
     'description': "The video is a static, high-angle shot overlooking a busy multi-lane city street in what appears to be Bangkok, Thailand, indicated by the presence of tuk-tuks and brightly colored taxis. The scene captures the constant flow of traffic throughout the entire clip.\n\nIn the foreground on the left, a blue hatchback and a traditional three-wheeled tuk-tuk with a pink delivery bag on its back are either stationary or moving very slowly. Throughout the video, various vehicles, including white sedans, silver SUVs, motorcycles, and the city's signature pink and green-yellow taxis, navigate the lanes. \n\nThe road is divided by a narrow median with small green bushes. Traffic moves in both directions, with vehicles heading away from the camera and towards it. On the left side of the street, large multi-story buildings feature several prominent billboards, one of which displays a woman’s face. On the right, a row of trees lines the sidewalk, behind which several large, white-roofed structures with pink accents are visible. In the background, a pedestrian overpass crosses the busy road, and taller city buildings can be seen in the distance under a bright, overcast sky. The overall atmosphere is one of a typical, bustling urban afternoon.",
     'similarity': 0.13178936719516832}]}

7. Version: Automatic History

Every insert, update, and delete is versioned. `history()` returns the full changelog. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} videos.history() ```

8. Agents: Tool Calling and Memory as Computed Columns

An agent is just more computed columns. Define tools from `@pxt.query` functions, wire tool calling and context assembly as a chain of columns, and every insert triggers the full reasoning pipeline. Memory is a table with an embedding index. This pattern scales to production. See the [Starter Kit](https://github.com/pixeltable/pixeltable-starter-kit) for a complete implementation with documents, images, video, and cross-modal search. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} from pixeltable.functions.anthropic import invoke_tools, messages # 1. Define tools from existing @pxt.query functions tools = pxt.tools(search_videos) # 2. Memory: chat history with embedding index chat = pxt.create_table( 'demo/chat', { 'role': pxt.String, 'content': pxt.String, 'conversation_id': pxt.String, }, if_exists='ignore', ) chat.add_embedding_index( 'content', string_embed=gemini.embed_content.using( model='gemini-embedding-2-preview' ), if_exists='ignore', ) # 3. Agent pipeline: each step is a computed column agent = pxt.create_table( 'demo/agent', {'prompt': pxt.String}, if_exists='ignore' ) # Step 1: LLM decides which tools to call agent.add_computed_column( response=messages( model='claude-sonnet-4-20250514', messages=[{'role': 'user', 'content': agent.prompt}], tools=tools, tool_choice=tools.choice(required=True), max_tokens=4096, ), if_exists='ignore', ) # Step 2: Pixeltable executes the tool calls (runs search_videos) agent.add_computed_column( tool_output=invoke_tools(tools, agent.response), if_exists='ignore' ) # In production, add more steps: assemble context, call LLM again with results. # See the Starter Kit for the full multi-step agent pipeline. agent.describe() ```
  Created table 'chat'.
  Created table 'agent'.
  Added 0 column values with 0 errors in 0.01 s
## Bonus: Cloud Storage (Optional) Free managed bucket with Pixeltable Cloud. Set two config values; computed media flows to cloud. See [Deploy to Pixeltable Cloud](/howto/deployment/cloud). ```toml theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # ~/.pixeltable/config.toml [pixeltable] api_key = "your-pixeltable-cloud-api-key" output_media_dest = "pxtfs://yourorg:yourdb/home" ``` ## Summary
### Links * [10-Minute Tour](/overview/ten-minute-tour) * [Starter Kit](https://github.com/pixeltable/pixeltable-starter-kit) (FastAPI + React reference app) * [Cookbooks](/use-cases/multimodal-backend) (50+ recipes) * [Docs](/) * [GitHub](https://github.com/pixeltable/pixeltable) (Apache 2.0) # Join tables to combine data Source: https://docs.pixeltable.com/howto/cookbooks/core/query-join-tables Open in Kaggle  Open in Colab  Download Notebook This documentation page is also available as an interactive notebook. You can launch the notebook in Kaggle or Colab, or download it for use with an IDE or local Jupyter installation, by clicking one of the above links. Combine data from multiple tables using inner, left, and cross joins. ## Problem You have related data in separate tables and need to combine them for analysis—customers with orders, products with inventory, or media with metadata.
## Solution **What’s in this recipe:** * Inner join to match rows from both tables * Left join to keep all rows from the first table * Cross join for Cartesian product (all combinations) * Join with filtering, aggregation, and saving results * Paginate results with `limit()` and `offset` Use `table1.join(table2, on=..., how=...)` to combine tables based on matching columns. ### Setup ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} %pip install -qU pixeltable ``` ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} import pixeltable as pxt import pixeltable.functions as pxtf # Create a fresh directory pxt.drop_dir('join_demo', force=True) pxt.create_dir('join_demo') ```
  Connected to Pixeltable database at: postgresql+psycopg://postgres:@/pixeltable?host=/Users/pjlb/.pixeltable/pgdata
  Created directory 'join\_demo'.
  \
### Create sample tables ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Create a customers table customers = pxt.create_table( 'join_demo/customers', {'customer_id': pxt.Int, 'name': pxt.String, 'email': pxt.String}, ) customers.insert( [ {'customer_id': 1, 'name': 'Alice', 'email': 'alice@example.com'}, {'customer_id': 2, 'name': 'Bob', 'email': 'bob@example.com'}, { 'customer_id': 3, 'name': 'Charlie', 'email': 'charlie@example.com', }, ] ) customers.collect() ```
  Created table 'customers'.
  Inserted 3 rows with 0 errors in 0.01 s (385.68 rows/s)
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Create an orders table orders = pxt.create_table( 'join_demo/orders', { 'order_id': pxt.Int, 'customer_id': pxt.Int, 'product': pxt.String, 'amount': pxt.Float, }, ) orders.insert( [ { 'order_id': 101, 'customer_id': 1, 'product': 'Laptop', 'amount': 999.00, }, { 'order_id': 102, 'customer_id': 1, 'product': 'Mouse', 'amount': 29.00, }, { 'order_id': 103, 'customer_id': 2, 'product': 'Keyboard', 'amount': 79.00, }, { 'order_id': 104, 'customer_id': 4, 'product': 'Monitor', 'amount': 299.00, }, # No matching customer ] ) orders.collect() ```
  Created table 'orders'.
  Inserted 4 rows with 0 errors in 0.01 s (657.81 rows/s)
### Inner join (matching rows only) ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Inner join: only rows that match in both tables customers.join( orders, on=customers.customer_id == orders.customer_id, how='inner' ).select(customers.name, orders.product, orders.amount).collect() ```
### Left join (keep all from first table) ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Left join: all customers, with order data where available # Charlie has no orders, so product/amount will be null customers.join( orders, on=customers.customer_id == orders.customer_id, how='left' ).select(customers.name, orders.product, orders.amount).collect() ```
### Join with filtering ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Combine join with where clause to filter results customers.join( orders, on=customers.customer_id == orders.customer_id, how='inner' ).where(orders.amount > 50).select( customers.name, customers.email, orders.product, orders.amount ).collect() ```
### Join with aggregation ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Join and aggregate: total spending per customer customers.join( orders, on=customers.customer_id == orders.customer_id, how='inner' ).group_by(customers.name).select( customers.name, total_spent=pxtf.sum(orders.amount), order_count=pxtf.count(orders.order_id), ).collect() ```
### Cross join (all combinations) ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Cross join: every customer paired with every product (no 'on' condition) products = pxt.create_table( 'join_demo/products', {'product': pxt.String, 'price': pxt.Float} ) products.insert( [ {'product': 'Widget', 'price': 19.99}, {'product': 'Gadget', 'price': 29.99}, ] ) customers.join(products, how='cross').select( customers.name, products.product, products.price ).collect() ```
  Created table 'products'.
  Inserted 2 rows with 0 errors in 0.00 s (422.52 rows/s)
### Save join results to a new table ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Build a join query and collect as DataFrame customer_orders_df = ( customers.join( orders, on=customers.customer_id == orders.customer_id, how='inner', ) .select( name=customers.name, email=customers.email, product=orders.product, amount=orders.amount, ) .collect() .to_pandas() ) customer_orders_df ```
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Create a new table from the DataFrame orders_report = pxt.create_table( 'join_demo/orders_report', source=customer_orders_df ) orders_report.collect() ```
  Created table 'orders\_report'.
  Inserted 3 rows with 0 errors in 0.01 s (500.32 rows/s)
### Paginate results with limit and offset Use `limit(n, offset=k)` to retrieve results in pages. This is useful for displaying results incrementally or building paginated APIs. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Page 1: first 2 rows orders.order_by(orders.order_id).limit(2).collect() ```
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Page 2: next 2 rows (skip the first 2) orders.order_by(orders.order_id).limit(2, offset=2).collect() ```
## Explanation **Join types:**
**Join syntax:** ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Simple: join on column by name t1.join(t2, on=t1.id) # Explicit predicate t1.join(t2, on=t1.customer_id == t2.customer_id) # Composite key t1.join(t2, on=(t1.pk1 == t2.pk1) & (t1.pk2 == t2.pk2)) ``` **Aggregation functions:** ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} from pixeltable.functions import sum, count, mean, min, max # Use as functions, not methods total=sum(t.amount) num_rows=count(t.id) ``` **Saving join results:** ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Collect as DataFrame, then create table df = query.select(name=t.col, ...).collect().to_pandas() new_table = pxt.create_table('path', source=df) ``` **Pagination:** ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # limit(n) returns at most n rows # limit(n, offset=k) skips the first k rows, then returns n query.order_by(t.id).limit(10) # rows 0-9 query.order_by(t.id).limit(10, offset=10) # rows 10-19 ``` **Tips:** * Use explicit predicates (`t1.col == t2.col`) for clarity * Chain `.where()` after join to filter results * Chain `.group_by()` for aggregations * Use `'left'` join when the first table is your “main” table * Use named columns in `.select(name=col)` for clean column names * Always use `.order_by()` with pagination to get deterministic page ordering ## See also * [Look up structured data](/howto/cookbooks/agents/pattern-data-lookup) - Use retrieval UDFs for lookups * [Sample data for training](/howto/cookbooks/data/data-sampling) - Sample from joined results # Time Zones Source: https://docs.pixeltable.com/howto/cookbooks/core/time-zones Open in Kaggle  Open in Colab  Download Notebook This documentation page is also available as an interactive notebook. You can launch the notebook in Kaggle or Colab, or download it for use with an IDE or local Jupyter installation, by clicking one of the above links. Because typical use cases involve datasets that span multiple time zones, Pixeltable strives to be precise in how it handles time zone arithmetic for datetimes. Timestamps are always stored in the Pixeltable database in UTC, to ensure consistency across datasets and deployments. Time zone considerations therefore apply during insertion and retrieval of timestamp data. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} %pip install -qU pixeltable ``` ### The default time zone Every Pixeltable deployment has a **default time zone**. The default time zone can be configured either by setting the `PIXELTABLE_TIME_ZONE` environment variable, or by adding a `time-zone` entry to the `[pixeltable]` section in `$PIXELTABLE_HOME/config.toml`. It must be a valid [IANA Time Zone](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones). (See the [Pixeltable Configuration](/platform/configuration) guide for more details on configuration options.) ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} import os os.environ['PIXELTABLE_TIME_ZONE'] = 'America/Los_Angeles' ``` If no time zone is configured, then Pixeltable will fall back on the system time zone of the host on which it is running. **Because system time zone is deployment-dependent, it is recommended that production deployments configure a default time zone explicitly.** As outlined in the [Python datetime documentation](https://docs.python.org/3/library/datetime.html), a Python `datetime` object may be either **naive** (no time zone) or **aware** (equipped with an explicit time zone). Pixeltable will always interpret naive `datetime` objects as belonging to the configured default time zone. ### Insertion and retrieval When a `datetime` is inserted into the database, it will be converted to UTC and stored as an absolute timestamp. If the `datetime` has an explicit time zone, Pixeltable will use that time zone for the conversion; otherwise, Pixeltable will use the default time zone. When a `datetime` is retrieved, it will always be retrieved in the default time zone. To query in a different time zone, it is necessary to do an explicit conversion; we’ll give an example of this in a moment. Let’s first walk through a few examples that illustrate the default behavior. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} import pixeltable as pxt pxt.drop_dir('tz_demo', force=True) pxt.create_dir('tz_demo') t = pxt.create_table( 'tz_demo/example', {'dt': pxt.Timestamp, 'note': pxt.String} ) ```
  Connected to Pixeltable database at: postgresql+psycopg://postgres:@/pixeltable?host=/Users/pjlb/.pixeltable/pgdata
  Created directory 'tz\_demo'.
  Created table 'example'.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} from datetime import datetime, timezone from zoneinfo import ZoneInfo naive_dt = datetime(2024, 8, 9, 23, 0, 0) explicit_dt = datetime( 2024, 8, 9, 23, 0, 0, tzinfo=ZoneInfo('America/Los_Angeles') ) other_dt = datetime( 2024, 8, 9, 23, 0, 0, tzinfo=ZoneInfo('America/New_York') ) t.insert( [ {'dt': naive_dt, 'note': 'No time zone specified (uses default)'}, { 'dt': explicit_dt, 'note': 'Time zone America/Los_Angeles was specified explicitly', }, { 'dt': other_dt, 'note': 'Time zone America/New_York was specified explicitly', }, ] ) ```
  Inserting rows into \`example\`: 0 rows \[00:00, ? rows/s]
  Inserting rows into \`example\`: 3 rows \[00:00, 433.04 rows/s]
  Inserted 3 rows with 0 errors.
  3 rows inserted, 3 values computed.
On retrieval, all timestamps are normalized to the default time zone, regardless of how they were specified during insertion. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} t.collect() ```
To represent timestamps in a different time zone, use the `astimezone` method. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} t.select( t.dt, dt_new_york=t.dt.astimezone('America/New_York'), note=t.note ).collect() ```
### Timestamp methods and properties The Pixeltable API exposes all the standard `datetime` methods and properties from the Python library. Because retrieval uses the default time zone, they are all relative to the default time zone unless `astimezone` is used. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} t.select( t.dt, day_default=t.dt.day, day_eastern=t.dt.astimezone('America/New_York').day, ).collect() ```
Observe that the first two timestamps map to different dates depending on the time zone, as expected. # Track changes and revert to previous versions Source: https://docs.pixeltable.com/howto/cookbooks/core/version-control-history Open in Kaggle  Open in Colab  Download Notebook This documentation page is also available as an interactive notebook. You can launch the notebook in Kaggle or Colab, or download it for use with an IDE or local Jupyter installation, by clicking one of the above links. Undo mistakes, audit changes, and create point-in-time snapshots of your data. ## Problem You need to track what changed in your data pipeline, undo accidental modifications, or preserve a specific state for reproducibility. ## Solution **What’s in this recipe:** * View version history with `history()` and `get_versions()` * Access specific versions with `pxt.get_table('table:N')` * Undo changes with `revert()` * Create point-in-time snapshots with `pxt.create_snapshot()` ### Setup ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} %pip install -qU pixeltable ``` ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} import pixeltable as pxt pxt.drop_dir('version_demo', force=True) pxt.create_dir('version_demo') ```
  Connected to Pixeltable database at: postgresql+psycopg://postgres:@/pixeltable?host=/Users/pjlb/.pixeltable/pgdata
  Created directory 'version\_demo'.
  \
### Create a table and make some changes Every data or schema change creates a new version. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Create table (version 0) products = pxt.create_table( 'version_demo/products', {'name': pxt.String, 'price': pxt.Float, 'category': pxt.String}, ) ```
  Created table 'products'.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Insert data (version 1) products.insert( [ {'name': 'Widget', 'price': 9.99, 'category': 'Tools'}, {'name': 'Gadget', 'price': 24.99, 'category': 'Electronics'}, {'name': 'Gizmo', 'price': 14.99, 'category': 'Electronics'}, ] ) ```
  Inserting rows into \`products\`: 0 rows \[00:00, ? rows/s]
  Inserting rows into \`products\`: 3 rows \[00:00, 432.95 rows/s]
  Inserted 3 rows with 0 errors.
  3 rows inserted, 6 values computed.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Add a computed column (version 2 - schema change) products.add_computed_column(price_with_tax=products.price * 1.08) ```
  Added 3 column values with 0 errors.
  3 rows updated, 6 values computed.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Update some data (version 3) products.update({'price': 19.99}, where=products.name == 'Widget') ```
  Inserting rows into \`products\`: 0 rows \[00:00, ? rows/s]
  Inserting rows into \`products\`: 1 rows \[00:00, 297.47 rows/s]
  1 row updated, 3 values computed.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Insert more data (version 4) products.insert( [{'name': 'Thingamajig', 'price': 49.99, 'category': 'Tools'}] ) ```
  Inserting rows into \`products\`: 0 rows \[00:00, ? rows/s]
  Inserting rows into \`products\`: 1 rows \[00:00, 661.46 rows/s]
  Inserted 1 row with 0 errors.
  1 row inserted, 3 values computed.
### View version history Use `history()` for a human-readable summary of all changes. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # View full history (most recent first) products.history() ```
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # View only the last 3 versions products.history(n=3) ```
### Programmatic access to version metadata Use `get_versions()` to access version data programmatically. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Get version metadata as a list of dictionaries versions = products.get_versions() # Access specific version info latest = versions[0] latest['version'], latest['change_type'], latest['inserts'] ```
  (4, 'data', 1)
### Access a specific version Use `pxt.get_table('table_name:version')` to get a read-only handle to a specific version: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Get the table at version 1 (after initial insert, before computed column) products_v1 = pxt.get_table('version_demo/products:1') # This is a read-only view of the data at that point in time products_v1.collect() ```
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Compare data at version 2 (after computed column added) vs version 1 # Note: version 1 doesn't have the price_with_tax column yet products_v2 = pxt.get_table('version_demo/products:2') products_v2.collect() ```
### Revert to previous version Use `revert()` to undo the most recent change. This is irreversible. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Current state: 4 products products.count() ```
  4
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Revert the last insert (removes Thingamajig) products.revert() products.count() ```
  3
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # History now shows version 4 was reverted products.history() ```
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Can revert multiple times (back to before the update) products.revert() # Check the Widget price is back to original products.where(products.name == 'Widget').select( products.name, products.price ).collect() ```
### Create point-in-time snapshots Snapshots freeze a table’s state for reproducibility. Unlike `revert()`, snapshots preserve the data indefinitely. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Create a snapshot of the current state snapshot_v1 = pxt.create_snapshot('version_demo/products_v1', products) snapshot_v1.collect() ```
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Now make changes to the original table products.insert( [{'name': 'Doohickey', 'price': 99.99, 'category': 'Premium'}] ) products.update({'price': 29.99}, where=products.name == 'Gadget') products.collect() ```
  Inserting rows into \`products\`: 0 rows \[00:00, ? rows/s]
  Inserting rows into \`products\`: 1 rows \[00:00, 535.67 rows/s]
  Inserted 1 row with 0 errors.

  Inserting rows into \`products\`: 0 rows \[00:00, ? rows/s]
  Inserting rows into \`products\`: 1 rows \[00:00, 558.05 rows/s]
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Snapshot remains unchanged - still shows original data snapshot_v1.collect() ```
## Explanation **What creates a new version:** * `insert()` - adding rows * `update()` - modifying rows * `delete()` - removing rows * `add_column()` / `add_computed_column()` - schema changes * `drop_column()` - schema changes * `rename_column()` - schema changes **Version history methods:** * `history()` - Human-readable DataFrame showing all changes * `get_versions()` - List of dictionaries for programmatic access **Accessing specific versions:** * `pxt.get_table('table_name:N')` - Get read-only handle to version N * Useful for comparing data across versions, auditing changes, or recovering specific values * Version handles are read-only—you cannot modify historical versions **Reverting:** * `revert()` undoes the most recent version * Can call multiple times to go back further * Cannot revert past version 0 * Cannot revert if a snapshot references that version **Snapshots vs revert:** * Snapshots are persistent, named, point-in-time copies * `revert()` permanently removes the latest version * Use snapshots when you need to preserve state for reproducibility * Use `revert()` to undo mistakes ## See also * [Iterative development](/howto/cookbooks/core/dev-iterative-workflow) - Fast feedback during development # Configure API keys for AI services Source: https://docs.pixeltable.com/howto/cookbooks/core/workflow-api-keys Open in Kaggle  Open in Colab  Download Notebook This documentation page is also available as an interactive notebook. You can launch the notebook in Kaggle or Colab, or download it for use with an IDE or local Jupyter installation, by clicking one of the above links. Set up API credentials for OpenAI, Anthropic, and other AI providers so Pixeltable can access them. ## Problem You need to call AI services (OpenAI, Anthropic, Gemini, etc.) from your data pipeline. These services require API keys, but you don’t want to hardcode credentials in your notebooks or scripts.
## Solution **What’s in this recipe:** * Set API keys using environment variables * Store keys in a config file for all projects * Use `getpass` for one-time session keys You configure API keys using one of three methods, depending on your needs. Pixeltable automatically discovers credentials from environment variables or config files—no code changes needed.
### Setup ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} %pip install -qU pixeltable ``` ### Option 1: environment variables **Use when:** CI/CD pipelines, Docker containers, production deployments Set the environment variable in your shell before running Python: ```bash theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # In your terminal (temporary, current session only) export OPENAI_API_KEY="sk-..." # Or add to ~/.bashrc or ~/.zshrc (permanent) echo 'export OPENAI_API_KEY="sk-..."' >> ~/.zshrc ``` You can also set it in Python (useful for testing): ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} import os import pixeltable as pxt # Set in Python (current process only) # os.environ['OPENAI_API_KEY'] = 'sk-...' # Check if a key is set 'OPENAI_API_KEY' in os.environ ```
  True
### Option 2: config file **Use when:** Local development, want credentials available to all Pixeltable projects Create `~/.pixeltable/config.toml`: ```toml theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # ~/.pixeltable/config.toml [openai] api_key = "sk-..." [anthropic] api_key = "sk-ant-..." [google] api_key = "AIza..." ``` You can check if the config file exists: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Check config file location home_dir = pxt.home() # Usually ~/.pixeltable config_file = home_dir / 'config.toml' print(config_file) config_file.exists() ```
  /Users/asiegel/.pixeltable/config.toml
  True
### Option 3: getpass (interactive) **Use when:** Shared notebooks, demos, one-time sessions Prompt for the key at runtime—it won’t be saved anywhere: ### Verify your configuration Test that Pixeltable can access your credentials by checking the config: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} import getpass # Uncomment to use interactively: # if 'OPENAI_API_KEY' not in os.environ: # os.environ['OPENAI_API_KEY'] = getpass.getpass('OpenAI API Key: ') # Check which API keys are available services = [ 'OPENAI_API_KEY', 'ANTHROPIC_API_KEY', 'GOOGLE_API_KEY', 'MISTRAL_API_KEY', ] for svc in services: status = '✓' if svc in os.environ else '✗' print(f'{status} {svc}') ```
  ✓ OPENAI\_API\_KEY
  ✓ ANTHROPIC\_API\_KEY
  ✓ GOOGLE\_API\_KEY
  ✓ MISTRAL\_API\_KEY
## Explanation **Discovery order:** Pixeltable checks for API keys in this order: 1. Environment variable (e.g., `OPENAI_API_KEY`) 2. Config file (`~/.pixeltable/config.toml`) 3. Raises an error if not found **Supported services:**
**Config file is global:** All Pixeltable projects on your machine share the same config file. **Getpass is per-session:** The key only exists in memory for the current Python session. ## See also * [Pixeltable configuration reference](/platform/configuration) * [Working with OpenAI](/howto/providers/working-with-openai) # Extract fields from LLM JSON responses Source: https://docs.pixeltable.com/howto/cookbooks/core/workflow-json-extraction Open in Kaggle  Open in Colab  Download Notebook This documentation page is also available as an interactive notebook. You can launch the notebook in Kaggle or Colab, or download it for use with an IDE or local Jupyter installation, by clicking one of the above links. Parse and access specific fields from structured JSON responses returned by language models. ## Problem LLM APIs return nested JSON responses with metadata you don’t need. You want to extract just the text content or specific fields for downstream processing. ```json theme={"theme":{"light":"light-plus","dark":"dark-plus"}} { "id": "chatcmpl-123", "choices": [{ "message": { "content": "This is the actual response text" // ← You want this } }], "usage": {"tokens": 50} } ``` ## Solution **What’s in this recipe:** * Extract text content from chat completions * Access nested JSON fields * Create separate columns for different fields You use JSON path notation to extract specific fields from API responses and store them in computed columns. ### Setup ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} %pip install -qU pixeltable openai import getpass import os if 'OPENAI_API_KEY' not in os.environ: os.environ['OPENAI_API_KEY'] = getpass.getpass('OpenAI API Key: ') ``` ### Create prompts table ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} import pixeltable as pxt from pixeltable.functions import openai # Create a fresh directory pxt.drop_dir('json_demo', force=True) pxt.create_dir('json_demo') ```
  Connected to Pixeltable database at: postgresql+psycopg://postgres:@/pixeltable?host=/Users/pjlb/.pixeltable/pgdata
  Created directory 'json\_demo'.
  \
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} t = pxt.create_table('json_demo/prompts', {'prompt': pxt.String}) ```
  Created table 'prompts'.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} t.insert( [ {'prompt': 'What is the capital of France?'}, {'prompt': 'Write a haiku about coding'}, ] ) ```
  Inserting rows into \`prompts\`: 2 rows \[00:00, 325.83 rows/s]
  Inserted 2 rows with 0 errors.
  2 rows inserted, 2 values computed.
### Get LLM responses ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Add computed column for API response (returns full JSON) t.add_computed_column( response=openai.chat_completions( messages=[{'role': 'user', 'content': t.prompt}], model='gpt-4o-mini', ) ) ```
  Added 2 column values with 0 errors.
  2 rows updated, 2 values computed.
### Extract specific fields Use dot notation to access nested JSON fields: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Extract just the text content t.add_computed_column(text=t.response.choices[0].message.content) # Extract token usage t.add_computed_column(tokens=t.response.usage.total_tokens) ```
  Added 2 column values with 0 errors.
  Added 2 column values with 0 errors.
  2 rows updated, 2 values computed.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # View clean results t.select(t.prompt, t.text, t.tokens).collect() ```
## Explanation **Common extraction patterns:**
**Accessing JSON fields:** * Use dot notation for object properties: `response.usage` * Use brackets for array elements: `choices[0]` * Chain them: `response.choices[0].message.content` **Extracted columns are computed:** Changes to the source data automatically update all extracted fields. ## See also * [Configure API keys](/howto/cookbooks/core/workflow-api-keys) * [Extract structured data from images](/howto/cookbooks/images/vision-structured-output) # Add unique identifiers to your tables Source: https://docs.pixeltable.com/howto/cookbooks/core/workflow-uuid-identity Open in Kaggle  Open in Colab  Download Notebook This documentation page is also available as an interactive notebook. You can launch the notebook in Kaggle or Colab, or download it for use with an IDE or local Jupyter installation, by clicking one of the above links. Generate UUIDs for automatic row identification. ## Problem You need unique identifiers for rows in your data pipeline. Maybe you’re building an API that returns specific records, tracking processing status across systems, or joining data from multiple sources.
## Solution **What’s in this recipe:** * Create tables with auto-generated UUID primary keys * Add UUID columns to existing tables * Generate UUIDs with `uuid7()` You use `uuid7()` to generate UUIDs for each row. Define it in the schema with `{'column_name': uuid7()}` syntax, or add it to existing tables with `add_computed_column()`. ### Setup ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} %pip install -qU pixeltable ``` ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} import pixeltable as pxt from pixeltable.functions.uuid import uuid7 # Create a fresh directory pxt.drop_dir('uuid_demo', force=True) pxt.create_dir('uuid_demo') ``` ### Create a table with a UUID primary key Use `uuid7()` in your schema to create a column that auto-generates UUIDs: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Create table with auto-generated UUID primary key products = pxt.create_table( 'uuid_demo/products', { 'id': uuid7(), # Auto-generates UUID for each row 'name': pxt.String, 'price': pxt.Float, }, primary_key=['id'], ) ```
  Created table 'products'.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Insert data - no need to provide 'id', it's auto-generated products.insert( [ {'name': 'Laptop', 'price': 999.99}, {'name': 'Mouse', 'price': 29.99}, {'name': 'Keyboard', 'price': 79.99}, ] ) ```
  Inserted 3 rows with 0 errors in 0.02 s (191.21 rows/s)
  3 rows inserted.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # View the data - each row has a unique UUID products.collect() ```
### Add a UUID column to an existing table You can add a UUID column to a table that already exists using `add_computed_column()`: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Create a table without a UUID column orders = pxt.create_table( 'uuid_demo/orders', {'customer': pxt.String, 'amount': pxt.Float} ) ```
  Created table 'orders'.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Insert some data orders.insert( [ {'customer': 'Alice', 'amount': 150.00}, {'customer': 'Bob', 'amount': 75.50}, ] ) ```
  Inserted 2 rows with 0 errors in 0.01 s (310.49 rows/s)
  2 rows inserted.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Add a UUID column to existing table orders.add_computed_column(order_id=uuid7()) ```
  Added 2 column values with 0 errors in 0.02 s (98.14 rows/s)
  2 rows updated.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # View orders with their UUID column orders.collect() ```
## Explanation **Two ways to add UUIDs:**
Both use `uuid7()` which generates UUIDv7 (time-based) identifiers: * 128-bit values * Formatted as 32 hex digits with hyphens: `018e65c5-35e5-7c5d-8f37-f1c5b9c8a7b2` * Time-ordered for better database performance * Virtually guaranteed unique (collision probability is negligible) ## See also * [Tables and operations](/tutorials/tables-and-data-operations) * [Computed columns](/tutorials/computed-columns) # Working with the Pixeltable CLI Source: https://docs.pixeltable.com/howto/cookbooks/core/working-with-cli Open in Kaggle  Open in Colab  Download Notebook This documentation page is also available as an interactive notebook. You can launch the notebook in Kaggle or Colab, or download it for use with an IDE or local Jupyter installation, by clicking one of the above links. ## Problem You defined a multimodal pipeline in Python and now need to inspect tables, debug computed columns, roll back changes, and expose HTTP endpoints without writing more application code. Jumping into a REPL or building a custom admin UI for every project does not scale, especially when AI agents need stable, machine-readable output. ## Solution **What’s in this recipe:** * Inspect catalogs with `pxt ls`, `describe`, `columns`, and `idxs` * Query and debug rows with `pxt rows`, `count`, `get`, and `errors` * Manage versions with `pxt history` and `pxt revert` * Script and automate with `--json`, `-f`, and `pxt shell` * Write tables and routes in `app.py`. `pxt schema update` creates the tables; `pxt service diff` / `update` starts HTTP The `pxt` CLI ships with Pixeltable (v0.7.4+). Catalog commands talk to a local daemon (\~40 ms per call after the first invocation). Write tables in Python, then inspect and serve them from the terminal. See the [CLI reference](/platform/cli) for every flag. ### Setup ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} %pip install -qU 'pixeltable[serve]' ``` ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} import json import pixeltable as pxt import subprocess from pixeltable.functions.video import frame_iterator def pxt_json(*args: str) -> object: """Run pxt with --json and parse stdout.""" return json.loads(subprocess.check_output(['pxt', *args], text=True)) ``` ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} SAMPLE_VIDEO = 'https://raw.githubusercontent.com/pixeltable/pixeltable/release/docs/resources/bangkok.mp4' pxt.drop_dir('cli_demo', force=True) pxt.create_dir('cli_demo') videos = pxt.create_table( 'cli_demo/videos', {'video': pxt.Video, 'title': pxt.String} ) frames = pxt.create_view( 'cli_demo/frames', videos, iterator=frame_iterator(videos.video, fps=1), ) frames.add_computed_column(thumb=frames.frame.thumbnail((320, 180))) videos.insert([{'video': SAMPLE_VIDEO, 'title': 'Bangkok'}]) ``` ### Step 1: Inspect the catalog List directories and tables, then drill into schema and computed columns. Flag letters in `pxt ls -l`: `c` = computed column, `i` = index. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} !pxt ls -l cli_demo !pxt describe cli_demo/videos !pxt columns cli_demo/frames --computed !pxt idxs cli_demo/frames ``` ### Step 2: Query rows Peek at stored data from the terminal. Pass computed columns explicitly with `--cols`; unstored computed columns are skipped by default. Thumbnails may take a moment to compute after insert. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} !pxt count cli_demo/frames !pxt rows cli_demo/frames -n 1 --cols pos,thumb ``` ### Step 3: Debug computed-column failures When a stored computed column fails, `pxt errors` lists the failing rows by primary key. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf def boom_if_zero(x: int) -> int: if x == 0: raise ValueError('boom') return x failures = pxt.create_table( 'cli_demo/failures', {'k': pxt.Int}, primary_key='k' ) failures.add_computed_column( result=boom_if_zero(failures.k), on_error='ignore' ) failures.insert([{'k': 0}, {'k': 1}], on_error='ignore') !pxt errors cli_demo/failures ``` ### Step 4: Version control Every insert and schema change creates a new table version. Inspect the timeline, then roll back if needed. See [Track changes and revert](../../../howto/cookbooks/core/version-control-history) for the Python API. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} videos.add_computed_column(label=videos.title.upper()) !pxt history cli_demo/videos -n 5 !pxt revert cli_demo/videos -f !pxt history cli_demo/videos -n 3 ``` ### Step 5: Agent-friendly scripting Most catalog commands accept `--json` for stable, machine-readable output. Use `-f` to skip confirmation prompts in non-interactive contexts. For many commands in one session, `pxt shell` keeps the daemon warm: ```bash theme={"theme":{"light":"light-plus","dark":"dark-plus"}} pxt shell pxt> ls cli_demo pxt> count cli_demo/frames pxt> exit ``` ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} tables = [ e['path'] for e in pxt_json('ls', 'cli_demo', '--json')['entries'] if e['kind'] == 'table' ] print('tables:', tables) print( 'frame count:', pxt_json('count', 'cli_demo/frames', '--json')['count'], ) ``` ### Step 6: Config and health Check daemon health, runtime status, and resolved configuration (API keys show as `` when set). See [Configure API keys](../../../howto/cookbooks/core/workflow-api-keys) for credential setup. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} !pxt health !pxt status !pxt config --section openai ``` ### Step 7: Serve without writing a web app Write tables and routes in `app.py`. `pxt schema update` creates the tables. `pxt service example` writes a starting file; `pxt service diff` reports what serving it would change, without starting anything. See [HTTP Serving](/howto/deployment/serving). Full live flow. Use `pxt service list` for the URL; do not hard-code a port: ```bash theme={"theme":{"light":"light-plus","dark":"dark-plus"}} pxt init pxt schema update app.py cli_demo # create the tables pxt service update app.py cli_demo # start the services in the background pxt service list # the address, endpoint and pid of each service curl -X POST http://127.0.0.1:/videos -H 'Content-Type: application/json' \ -d '{"video": "https://raw.githubusercontent.com/pixeltable/pixeltable/release/docs/resources/bangkok.mp4", "title": "Bangkok"}' pxt rows cli_demo/frames -n 1 --cols pos,thumb pxt service stop videos # take it down again ``` ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} !pxt service example | head -20 ``` ### Next steps * [CLI reference](/platform/cli): every command and flag * [Dashboard](/platform/dashboard): browse tables and preview media in the browser * [HTTP Serving](/howto/deployment/serving): `FastAPIRouter` routes * [AI coding agents](/overview/building-pixeltable-with-llms): agent skills, MCP, and `pxt --json` workflows # Export data for analytics Source: https://docs.pixeltable.com/howto/cookbooks/data/data-export-analytics Open in Kaggle  Open in Colab  Download Notebook This documentation page is also available as an interactive notebook. You can launch the notebook in Kaggle or Colab, or download it for use with an IDE or local Jupyter installation, by clicking one of the above links. Export Pixeltable tables and queries to CSV, JSON, and Apache Iceberg for spreadsheets, scripts, and the lakehouse. ## Problem You’ve used Pixeltable to turn unstructured files (images, video, audio, documents) into structured columns: labels, metadata, embeddings, scores. Now an analytics tool or another team needs that data. Pixeltable columns aren’t always plain scalars, and formats differ in what they preserve. Three things about your data decide which format fits: * **Nested structures**: are you exporting embedding arrays or metadata objects? * **Rich types**: do you have columns whose type a reader couldn’t reliably infer from the values (timestamps, floats vs. ints, zero-padded IDs)? * **Media references**: do the exported media paths need to point to remote storage (S3, GCS, R2) so others can fetch the files? An export carries paths, not the files, and a path to a file on one person’s machine is oftentimes not useful to anyone else. ## Solution **What’s in this recipe:** * Export a table or filtered query to CSV, JSON, and Apache Iceberg * Keep media references portable instead of writing local file paths * Use advanced Iceberg options for existing tables, embeddings, and type overrides Pixeltable’s export helpers live in the `pxt.io` module, one function per destination. Here’s how each handles the three factors above:
`export_iceberg()` streams into an [Apache Iceberg](https://iceberg.apache.org/) catalog you supply; the other two write a single file. All three take a table or query as the first argument, so you pick exactly which columns to export, typically the structured columns your destination needs, such as labels, metadata, and embeddings. Exports mirror how Pixeltable already stores your data: media files live in a storage layer (Pixeltable’s media store, or your own S3/R2 bucket), while your tables hold the structured columns plus paths to those files. So an export sends the structured columns to your analytics format and writes the path to each media file, never the bytes, which is why others can fetch a file only when its path points to shared storage (covered below). ### Setup ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} %pip install -qU pixeltable pyiceberg ``` ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} import pixeltable as pxt import pixeltable.functions as pxtf # Create a fresh directory pxt.drop_dir('export_demo', force=True, if_not_exists='ignore') pxt.create_dir('export_demo') ``` ### Create sample data ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # A table of images, standing in for a multimodal pipeline images = pxt.create_table( 'export_demo/images', {'image': pxt.Image, 'label': pxt.String} ) base = 'https://raw.githubusercontent.com/pixeltable/pixeltable/main/docs/resources/images' images.insert( [ {'image': f'{base}/000000000036.jpg', 'label': 'cat'}, {'image': f'{base}/000000000090.jpg', 'label': 'bicycle'}, {'image': f'{base}/000000000139.jpg', 'label': 'living room'}, ] ) ```
  Created table 'images'.
  Inserted 3 rows with 0 errors in 0.59 s (5.04 rows/s)
  3 rows inserted.
We derive columns with Pixeltable’s built-in image functions, which run locally and need no API key. The pattern is the same for model-backed work: swap `pxtf.image.width` for `pxtf.openai.vision`, a Hugging Face object detector, or your own `@pxt.udf`, and everything downstream, including the export, is unchanged. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} images.add_computed_column(width=pxtf.image.width(images.image)) images.add_computed_column(height=pxtf.image.height(images.image)) images.add_computed_column(mode=pxtf.image.mode(images.image)) # The derived columns we'll export features = images.select( images.label, images.width, images.height, images.mode ) features.collect() ```
  Added 3 column values with 0 errors in 0.05 s (61.38 rows/s)
  Added 3 column values with 0 errors in 0.03 s (116.68 rows/s)
  Added 3 column values with 0 errors in 0.02 s (120.96 rows/s)
### Export to CSV `export_csv()` writes a query or table to a single file. It encodes nested `Json` and `Array` values as JSON strings and skips binary columns. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} import pandas as pd import tempfile from pathlib import Path out_dir = Path(tempfile.mkdtemp()) pxt.io.export_csv(features, out_dir / 'image_features.csv') # Read it back the way an external tool would pd.read_csv(out_dir / 'image_features.csv') ```
### Export to JSON `export_json()` writes [JSONL](https://jsonlines.org/) (one JSON object per line), which streams well and is easy to read line by line. Nested `Json` and `Array` columns keep their native structure rather than collapsing to a string. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} pxt.io.export_json(features, out_dir / 'image_features.jsonl') pd.read_json(out_dir / 'image_features.jsonl', lines=True) ```
### Export to Apache Iceberg `export_iceberg()` streams rows into a catalog you provide. For a self-contained demo we use [PyIceberg](https://py.iceberg.apache.org/)’s `SqlCatalog` with a local SQLite metadata store and a directory warehouse. Point this at your real catalog (REST, Glue, Hive) in production and nothing else changes. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} from pyiceberg.catalog.sql import SqlCatalog warehouse = out_dir / 'warehouse' warehouse.mkdir(parents=True, exist_ok=True) catalog = SqlCatalog( 'demo', uri=f'sqlite:///{warehouse}/catalog.db', warehouse=f'file://{warehouse}', ) ``` Pass a table or query, the catalog, and a table name. Iceberg names are namespace-qualified, written as `namespace.table`: in `analytics.image_features`, `analytics` is the namespace (a grouping of tables, like a schema in a SQL database) and `image_features` is the table. The namespace is created automatically if it’s new. Reading the table back returns the typed schema Iceberg stored, not just the values: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} pxt.io.export_iceberg(features, catalog, 'analytics.image_features') # The table carries a typed schema, not just a header row of values table = catalog.load_table('analytics.image_features') table.schema() ```
  Schema(NestedField(field\_id=1, name='label', field\_type=StringType(), required=False), NestedField(field\_id=2, name='width', field\_type=LongType(), required=False), NestedField(field\_id=3, name='height', field\_type=LongType(), required=False), NestedField(field\_id=4, name='mode', field\_type=StringType(), required=False), schema\_id=0, identifier\_field\_ids=\[])
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # And the rows themselves (Spark, DuckDB, or Snowflake would query the same table) table.scan().to_arrow().to_pandas() ```
#### Advanced Iceberg options `export_iceberg()` has a few extra controls for writing to a real lakehouse. **Handle existing tables.** You control what happens when the target table already exists with the `if_exists` parameter:
`replace` writes to a temporary table and swaps it in only on success, so a failed export leaves the existing table alone. (`export_csv` and `export_json` simply overwrite the file.) ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # image_features already holds the 3 rows we exported above; # appending the same 3 rows makes 6 pxt.io.export_iceberg( features, catalog, 'analytics.image_features', if_exists='append' ) # Reload to see the new snapshot, then count the rows catalog.load_table('analytics.image_features').scan().to_arrow().num_rows ```
  6
**Export embeddings.** Embeddings and feature vectors are `pxt.Array` columns. Iceberg has no fixed-shape tensor type, so project a fixed-shape array to a list with `.to_list()` first; variable-shape arrays already map to Iceberg lists. (CSV and JSON serialize arrays as JSON automatically.) ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} import numpy as np vectors = pxt.create_table( 'export_demo/vectors', {'doc_id': pxt.Int, 'embedding': pxt.Array[(4,), pxt.Float]}, ) vectors.insert( [ { 'doc_id': 0, 'embedding': np.array([0.1, 0.2, 0.3, 0.4], dtype=np.float32), }, { 'doc_id': 1, 'embedding': np.array([0.5, 0.6, 0.7, 0.8], dtype=np.float32), }, ] ) pxt.io.export_iceberg( vectors.select(vectors.doc_id, embedding=vectors.embedding.to_list()), catalog, 'analytics.embeddings', ) catalog.load_table('analytics.embeddings').scan().to_arrow().to_pandas() ```
  Created table 'vectors'.
  Inserted 2 rows with 0 errors in 0.00 s (421.55 rows/s)
**Override column types.** Iceberg infers types from the Pixeltable schema. Use `schema_overrides` to pin a specific PyArrow type, for example to store a 32-bit `pxt.Float` as a 64-bit `double`. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} import pyarrow as pa images.add_computed_column(aspect_ratio=images.width / images.height) pxt.io.export_iceberg( images.select(images.label, images.aspect_ratio), catalog, 'analytics.image_ratios', schema_overrides={'aspect_ratio': pa.float64()}, ) catalog.load_table('analytics.image_ratios').schema().as_arrow() ```
  Added 3 column values with 0 errors in 0.03 s (112.61 rows/s)
  label: large\_string
    -- field metadata --
    PARQUET:field\_id: '1'
  aspect\_ratio: double
    -- field metadata --
    PARQUET:field\_id: '2'
### Export a filtered query Every export function takes a query, so you can filter rows and project columns before writing, in any format. Here we keep only wide images and two columns. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} wide = images.where(images.width > 500).select(images.label, images.width) pxt.io.export_csv(wide, out_dir / 'wide_images.csv') pd.read_csv(out_dir / 'wide_images.csv') ```
### Keep media references portable An export carries a *reference* to each media file, not the bytes. A media column gives you two references to choose from: * Its **value** (e.g. `videos.video`) is the local cache path on the machine that ran the export. It resolves nowhere else. * Its **`.fileurl`** (e.g. `videos.video.fileurl`) is where the file is stored: the `https://`, `s3://`, or `gs://` address it was inserted from, or a `file://` path if you inserted a local file. The export writes a reference, not the bytes, so it can’t make a file reachable on its own. The recipient can get to your media only if it already lives in storage they can access too, which in practice means shared cloud storage (S3, GCS, R2). Media inserted from a cloud bucket or URL exports an address anyone with access can open; media inserted from a local file exports a `file://` path that resolves only on the machine that ran the export. To share local media, upload it to cloud storage first (see [Upload media to S3 and cloud storage](/howto/cookbooks/data/data-export-s3)). How each format handles media: * `export_csv()` and `export_json()` export `.fileurl` for you. * `export_iceberg()` inlines `Image` columns as bytes, but writes the local cache path for `Video`, `Audio`, and `Document`. Select `.fileurl` yourself when the table needs to travel. The video below is inserted from a URL, so its `.fileurl` is that URL, and CSV exports it directly: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} videos = pxt.create_table( 'export_demo/videos', {'video': pxt.Video, 'title': pxt.String} ) videos.insert( [ { 'video': 'https://raw.githubusercontent.com/pixeltable/pixeltable/main/tests/data/videos/bangkok_half_res.mp4', 'title': 'bangkok', } ] ) ```
  Created table 'videos'.
  Inserted 1 row with 0 errors in 0.65 s (1.53 rows/s)
  1 row inserted.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} pxt.io.export_csv( videos.select(videos.title, videos.video), out_dir / 'videos.csv' ) # to_dict keeps the full URL visible (a DataFrame would truncate it) pd.read_csv(out_dir / 'videos.csv').to_dict('records') ```
  \[\{'title': 'bangkok',
    'video': '[https://raw.githubusercontent.com/pixeltable/pixeltable/main/tests/data/videos/bangkok\_half\_res.mp4\'\\}\\](https://raw.githubusercontent.com/pixeltable/pixeltable/main/tests/data/videos/bangkok_half_res.mp4\'\\}\\)]
Exported directly to Iceberg, the same column becomes a machine-local path: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} pxt.io.export_iceberg( videos.select(videos.video), catalog, 'analytics.video_local' ) catalog.load_table('analytics.video_local').scan().to_arrow().to_pylist() ```
  \[\{'video': '/Users/username/.pixeltable/file\_cache/0eabea0a34ca471ea07a153f6485d552\_0\_b2eb75710e902d63b0b8ff19dc432062618731a0ec7df6f68beee7ad5fac98be.mp4'}]
Export `.fileurl` instead and Iceberg gets the portable URL too. With media in S3 or R2, this is the `s3://`/`gs://` URL a lakehouse engine can open: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} videos.add_computed_column(duration=pxtf.video.get_duration(videos.video)) pxt.io.export_iceberg( videos.select( videos.title, videos.duration, video_url=videos.video.fileurl ), catalog, 'analytics.videos', ) catalog.load_table('analytics.videos').scan().to_arrow().to_pylist() ```
  Added 1 column value with 0 errors in 0.04 s (28.16 rows/s)
  \[\{'title': 'bangkok',
    'duration': 18.479999542236328,
    'video\_url': '[https://raw.githubusercontent.com/pixeltable/pixeltable/main/tests/data/videos/bangkok\_half\_res.mp4\'\\}\\](https://raw.githubusercontent.com/pixeltable/pixeltable/main/tests/data/videos/bangkok_half_res.mp4\'\\}\\)]
## Explanation Every column has a Pixeltable [type](/platform/type-system), from scalars like `Int` and `Float` to the multimodal types (`Image`, `Video`, `Audio`, `Document`, `Array`, `Json`) that make it possible to store images, video, and embeddings in the same table. On export, each type maps to the closest thing the destination format supports:
**Iceberg details:** * Rows are streamed as PyArrow `RecordBatch`es; tune the in-memory batch size with `batch_size_bytes` (default 128 MB) for large exports. * Pass `namespace.table`; the namespace is created automatically, and a bare name is rejected. * A fixed-shape `pxt.Array` is rejected (project it with `.to_list()`), as is a `pxt.Json` column that can’t reduce to one concrete type. Pin those with `schema_overrides`. ## See also * [Export data for ML training](/howto/cookbooks/data/data-export-pytorch) - PyTorch datasets and Parquet * [Export to SQL databases](/howto/cookbooks/data/data-export-sql) - PostgreSQL, SQLite, MySQL, Snowflake * [Upload media to S3 and cloud storage](/howto/cookbooks/data/data-export-s3) - where your media files live * [Import Parquet files](/howto/cookbooks/data/data-import-parquet) - columnar import/export * [Pixeltable type system](/platform/type-system) - how multimodal columns are typed and stored # Export data for ML training Source: https://docs.pixeltable.com/howto/cookbooks/data/data-export-pytorch Open in Kaggle  Open in Colab  Download Notebook This documentation page is also available as an interactive notebook. You can launch the notebook in Kaggle or Colab, or download it for use with an IDE or local Jupyter installation, by clicking one of the above links. Convert Pixeltable data to PyTorch DataLoader format for model training. ## Problem You have prepared training data—images with labels, text with embeddings, or multimodal data—and need to export it for PyTorch model training.
## Solution **What’s in this recipe:** * Convert query results to PyTorch Dataset * Use with DataLoader for batch training * Export to Parquet for external tools You use `query.to_pytorch_dataset()` to create an iterable dataset compatible with PyTorch DataLoader. ### Setup ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} %pip install -qU pixeltable torch torchvision ``` ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} import pixeltable as pxt import torch from torch.utils.data import DataLoader # Create a fresh directory pxt.drop_dir('pytorch_demo', force=True) pxt.create_dir('pytorch_demo') ```
  Connected to Pixeltable database at: postgresql+psycopg://postgres:@/pixeltable?host=/Users/pjlb/.pixeltable/pgdata
  Created directory 'pytorch\_demo'.
  \
### Create sample training data ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Create table with images and labels training_data = pxt.create_table( 'pytorch_demo/training_data', {'image': pxt.Image, 'label': pxt.Int} ) ```
  Created table 'training\_data'.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Insert sample images with labels base_url = 'https://raw.githubusercontent.com/pixeltable/pixeltable/main/docs/resources/images' samples = [ {'image': f'{base_url}/000000000036.jpg', 'label': 0}, # cat {'image': f'{base_url}/000000000090.jpg', 'label': 1}, # other {'image': f'{base_url}/000000000139.jpg', 'label': 1}, # other ] training_data.insert(samples) ```
  Inserting rows into \`training\_data\`: 3 rows \[00:00, 659.03 rows/s]
  Inserted 3 rows with 0 errors.
  3 rows inserted, 6 values computed.
### Export to PyTorch dataset ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Add a resize step to ensure all images have the same size training_data.add_computed_column( image_resized=training_data.image.resize((224, 224)) ) # Convert to PyTorch dataset # 'pt' format returns images as CxHxW tensors with values in [0,1] pytorch_dataset = training_data.select( training_data.image_resized, training_data.label ).to_pytorch_dataset(image_format='pt') ```
  Added 3 column values with 0 errors.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Use with PyTorch DataLoader dataloader = DataLoader(pytorch_dataset, batch_size=2) # Get first batch to verify the shape batch = next(iter(dataloader)) batch[ 'image_resized' ].shape # Should be (2, 3, 224, 224) - batch_size x channels x height x width ```
  torch.Size(\[2, 3, 224, 224])
### Export to Parquet for external tools ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} import tempfile from pathlib import Path # Export to Parquet for use with other ML tools export_path = Path(tempfile.mkdtemp()) / 'training_data' pxt.io.export_parquet( training_data.select(training_data.label), # Non-image columns parquet_path=export_path, ) ``` ## Explanation **Export methods:**
**Image format options:**
**DataLoader tips:** * Data is cached to disk for efficient repeated loading * Use `num_workers > 0` for parallel data loading * Filter/transform data before export to reduce size ## See also * [Sample data for training](/howto/cookbooks/data/data-sampling) - Stratified sampling * [Import Parquet files](/howto/cookbooks/data/data-import-parquet) - Parquet import/export # Upload media to S3 and other cloud storage Source: https://docs.pixeltable.com/howto/cookbooks/data/data-export-s3 Open in Kaggle  Open in Colab  Download Notebook This documentation page is also available as an interactive notebook. You can launch the notebook in Kaggle or Colab, or download it for use with an IDE or local Jupyter installation, by clicking one of the above links. When Pixeltable generates media files (thumbnails, extracted frames, processed images), by default it stores them locally. For production workflows, you can configure Pixeltable to upload these files directly to object storage including Amazon S3, Google Cloud Storage, Azure Blob Storage, and S3-compatible services like Cloudflare R2, Backblaze B2, and Tigris. **Key features:** * Computed media (AI-generated outputs) automatically uploads to your bucket * Input media can optionally be persisted for durability * Files are cached locally and downloaded on-demand **Configuration options:** 1. **Global defaults** in `config.toml`: ```toml theme={"theme":{"light":"light-plus","dark":"dark-plus"}} [pixeltable] input_media_dest = "s3://my-bucket/input/" output_media_dest = "s3://my-bucket/output/" ``` 2. **Per-column destination** (computed columns only): ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} t.add_computed_column( thumbnail=t.image.thumbnail((128, 128)), destination='s3://my-bucket/thumbnails/' ) ``` In this notebook, you’ll learn how to configure object storage destinations for your media files. ## What you’ll learn * Where Pixeltable stores files by default * How to specify destinations for individual columns * How to configure global destinations for all columns * How destination precedence works ## How it works Pixeltable decides where to store media files using this priority: 1. **Column destination** (highest priority) — `destination` parameter in `add_computed_column()` 2. **Global configuration** — `input_media_dest` / `output_media_dest` in [config file](/platform/configuration) 3. **Pixeltable’s default local storage** — Used if nothing else is configured ## Prerequisites For this notebook, you’ll need: * `pixeltable` installed * (Optional) Cloud storage credentials if you want to use a cloud provider ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} %pip install -qU pixeltable ``` ## Setup Let’s set up our demo environment. We’ll create a Pixeltable directory for this demo, set up local destination paths, create a table, and insert a sample image. You can substitute cloud storage URIs (like `s3://my-bucket/path/`) anywhere you see a local destination path. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} import pixeltable as pxt from pathlib import Path # Clean slate for this demo pxt.drop_dir('blob_storage_demo', force=True) pxt.create_dir('blob_storage_demo') ``` Now we’ll create a table with an image column and insert a sample image from the web. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Create table t = pxt.create_table( 'blob_storage_demo/media', {'source_image': pxt.Image}, if_exists='replace', ) ```
  Created table 'media'.
We can inspect the schema before adding images to our table: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} t ```
Let’s insert a single sample image. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} sample_image = 'https://raw.githubusercontent.com/pixeltable/pixeltable/main/docs/resources/images/000000000036.jpg' t.insert(source_image=sample_image) ```
  Inserted 1 row with 0 errors in 0.77 s (1.29 rows/s)
  1 row inserted.
And we can see the image in our table: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} t.collect() ```
## Default destinations By default, Pixeltable stores all media files in local storage under `~/.pixeltable/media`: * **Input files** (files you insert) — If you insert a URL, Pixeltable stores the URL and downloads it to cache on access. If you insert a local file path, Pixeltable just stores the path reference (the file stays where it is). * **Output files** (files Pixeltable generates) — Stored in `~/.pixeltable/media` This works out of the box with no configuration. You can change these defaults, which we’ll cover in the rest of this notebook. Let’s check where the source image is stored. Since we inserted a URL (not a local file), Pixeltable stores the URL reference and will download it to cache when we access it. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Let's see where the source_image is stored by default t.select(t.source_image.fileurl).collect() ```
Now let’s add a computed column without specifying a destination. This will show us where Pixeltable stores **output** files by default. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Add computed column with no destination specified - uses default t.add_computed_column( flipped=t.source_image.transpose(0), if_exists='replace' ) ```
  Added 1 column value with 0 errors in 0.02 s (45.44 rows/s)
  1 row updated.
Check the file URL - it points to `~/.pixeltable/media`, the default location for generated files. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} t.select(t.flipped, t.flipped.fileurl).collect() ```
## Per-column destinations When you create a computed column, you can specify exactly where to store generated files using the `destination=` parameter. This gives you fine-grained control over outputs, which may be costly and/or difficult to re-generate. We’ll create a destination directory for storing one of our processed images. For this demo, we’re using a local directory on your Desktop, but you can replace this path with a cloud storage URI (like `s3://my-bucket/rotated/`). ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Create a local destination directory # For S3: dest_rotated = "s3://my-bucket/rotated/" # For GCS: dest_rotated = "gs://my-bucket/rotated/" base_path = Path.home() / 'Desktop' / 'pixeltable_outputs' base_path.mkdir(parents=True, exist_ok=True) dest_rotated = str(base_path / 'rotated') # Create directory (only needed for local paths) Path(dest_rotated).mkdir(exist_ok=True) ``` Now let’s add a computed column **with** an explicit destination to see the difference from the default behavior. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Add column WITH explicit destination t.add_computed_column( rotated=t.source_image.rotate(90), destination=dest_rotated, if_exists='replace', ) ```
  Added 1 column value with 0 errors in 0.02 s (48.98 rows/s)
  1 row updated.
Compare the file URLs. The `rotated` image uses our explicit destination, while `flipped` (created earlier) uses the default `~/.pixeltable/media` location. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} t.select(t.rotated, t.rotated.fileurl).collect() ```
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} t.select(t.flipped, t.flipped.fileurl).collect() ```
## Changing global destinations Instead of setting `destination=` on every column, you can change the global default for ALL columns. ### Output and input destinations You can configure two types of global destinations: * **`output_media_dest`** — Changes the default for files Pixeltable generates (computed columns) * **`input_media_dest`** — Changes the default for files you insert into tables You can set them to the same bucket or different buckets depending on your needs. ### How to configure You have two options: **Option 1: Configuration file** (`~/.pixeltable/config.toml`) ```toml theme={"theme":{"light":"light-plus","dark":"dark-plus"}} [pixeltable] # Where files Pixeltable generates are stored output_media_dest = "s3://my-bucket/output/" # Where files you insert are stored input_media_dest = "s3://my-bucket/input/" ``` **Option 2: Environment variables** ```bash theme={"theme":{"light":"light-plus","dark":"dark-plus"}} export PIXELTABLE_OUTPUT_MEDIA_DEST="s3://my-bucket/output/" export PIXELTABLE_INPUT_MEDIA_DEST="s3://my-bucket/input/" ``` ### Supported providers and URI formats
For complete authentication and setup details, see the [Cloud Storage documentation](/integrations/cloud-storage). ## Overriding global destinations Even if you configure global destinations, you can still override them for specific columns using the `destination=` parameter in `add_computed_column()`. Let’s create a new destination directory and add a thumbnail column that uses it. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Create a different destination for thumbnails dest_thumbnails = str(base_path / 'thumbnails') Path(dest_thumbnails).mkdir(exist_ok=True) # Add column with explicit destination (overrides any global default) t.add_computed_column( thumbnail=t.source_image.thumbnail((128, 128)), destination=dest_thumbnails, if_exists='replace', ) ```
  Added 1 column value with 0 errors in 0.02 s (47.89 rows/s)
  1 row updated.
Let’s view the thumbnail and its file URL. The explicit `destination=` parameter always wins, regardless of global configuration. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} t.select(t.thumbnail, t.thumbnail.fileurl).collect() ```
## Getting URLs for your files When your files are in object storage, you can get URLs that point directly to them. These URLs work in HTML, APIs, or any application you need to serve media with. The `.fileurl` property gives you direct URLs you can use anywhere. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} t.select( source=t.source_image.fileurl, rotated=t.rotated.fileurl, flipped=t.flipped.fileurl, ).collect() ```
## Generating presigned URLs **Note:** This section only applies if you’re using cloud storage (S3, GCS, Azure, R2, B2, Tigris). If you’re following along with local destinations (as in the examples above), you can skip this section or configure cloud storage to try it out.
When your files are in cloud storage, the `.fileurl` property returns storage URIs like `s3://bucket/path/file.jpg`. These aren’t directly accessible over HTTP. For private buckets or when you need time-limited HTTP access, use **presigned URLs**. These are temporary, authenticated URLs that allow anyone to access your files for a limited time without needing credentials. Presigned URLs are particularly useful for: * Sharing files from private buckets without making them public * Creating temporary download links with expiration * Serving media in web applications without exposing credentials * Providing time-limited access to sensitive content Use the `presigned_url` function from `pixeltable.functions.net`: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} import os # Use HTTPS URL format for Backblaze B2 b2_region = 'us-east-005' b2_bucket = 'pixeltable' cloud_destination = ( f'https://s3.{b2_region}.backblazeb2.com/{b2_bucket}/presigned-demo/' ) # Add the computed column t.add_computed_column( cloud_thumbnail=t.source_image.thumbnail((64, 64)), destination=cloud_destination, if_exists='replace', ) ```
  Added 1 column value with 0 errors in 0.22 s (4.46 rows/s)
  1 row updated.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Now generate presigned URLs for the cloud-stored files from pixeltable.functions import net t.select( cloud_thumbnail=t.cloud_thumbnail, storage_url=t.cloud_thumbnail.fileurl, presigned_url=net.presigned_url( t.cloud_thumbnail.fileurl, 3600 ), # 1-hour expiration ).collect() ```
The presigned URLs in the output are fully authenticated HTTP/HTTPS URLs that can be accessed directly in a browser or used in APIs without any credentials. ### Common expiration times
**Note:** Different storage providers have different maximum expiration limits. For example, Google Cloud Storage has a maximum 7-day expiration for presigned URLs. ### Troubleshooting presigned URLs If `presigned_url()` isn’t working: 1. **Local files**: Presigned URLs only work with cloud storage (S3, GCS, Azure, R2, B2, Tigris). If your files are stored locally (default), you’ll get an error. Configure a cloud destination first. 2. **Already HTTP URLs**: If `.fileurl` returns an `http://` or `https://` URL (not a storage URI like `s3://`), the file is already publicly accessible and doesn’t need a presigned URL. 3. **Credentials**: Ensure your cloud storage credentials are properly configured. See the [Cloud Storage documentation](/integrations/cloud-storage) for provider-specific setup. ## Common patterns Here are a few real-world patterns you might use: ### Pattern 1: All media in one place If you want everything in the same bucket, configure both input and output destinations in `~/.pixeltable/config.toml`: ```toml theme={"theme":{"light":"light-plus","dark":"dark-plus"}} [pixeltable] input_media_dest = "s3://my-bucket/media/" output_media_dest = "s3://my-bucket/media/" ``` Or set environment variables: ```bash theme={"theme":{"light":"light-plus","dark":"dark-plus"}} export PIXELTABLE_INPUT_MEDIA_DEST="s3://my-bucket/media/" export PIXELTABLE_OUTPUT_MEDIA_DEST="s3://my-bucket/media/" ``` ### Pattern 2: Separate input and output Keep source files separate from processed files in `~/.pixeltable/config.toml`: ```toml theme={"theme":{"light":"light-plus","dark":"dark-plus"}} [pixeltable] input_media_dest = "s3://my-bucket/uploads/" output_media_dest = "s3://my-bucket/processed/" ``` ### Pattern 3: Override for specific columns Use a global default, but send some columns elsewhere. First, set a global default in your config: ```toml theme={"theme":{"light":"light-plus","dark":"dark-plus"}} [pixeltable] output_media_dest = "s3://my-bucket/processed/" ``` Then in your code, most columns use the global default, but you can override specific ones: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Uses global default (s3://my-bucket/processed/) t.add_computed_column( thumbnail=t.image.thumbnail((128, 128)) ) # Overrides global default - goes to different location t.add_computed_column( large_thumbnail=t.image.thumbnail((512, 512)), destination='s3://my-bucket/thumbnails/' ) ``` ## Where do my files go? Understanding how Pixeltable handles different types of input files helps you make better decisions about storage configuration.
When you configure a cloud destination, Pixeltable populates both the destination and the local cache efficiently during `insert()`. For URLs, this means downloading once and using that download for both the upload and cache—avoiding wasteful upload→download cycles. ## What you learned * Pixeltable uses local storage by default for all media files * You can override the default for specific columns with the `destination` parameter * You can change the global default with `input_media_dest` and `output_media_dest` * Precedence: column destination > global config > Pixeltable’s default local storage * Use `.fileurl` to get URLs for your stored files * Use `net.presigned_url()` to generate time-limited, authenticated HTTP URLs for cloud storage files * Pixeltable handles caching intelligently to avoid wasteful operations ## See also * [Load from S3](../../../howto/cookbooks/data/data-import-s3) - Import media from cloud storage * [Cloud Storage Integration](../../../integrations/cloud-storage) - Provider setup ## Next steps * See the [Cloud Storage documentation](/integrations/cloud-storage) for complete provider setup and authentication details * Check out [Pixeltable Configuration](/platform/configuration) for all config options # Import/Export from SQL Databases Source: https://docs.pixeltable.com/howto/cookbooks/data/data-export-sql Open in Kaggle  Open in Colab  Download Notebook This documentation page is also available as an interactive notebook. You can launch the notebook in Kaggle or Colab, or download it for use with an IDE or local Jupyter installation, by clicking one of the above links. Send and receive data from PostgreSQL, SQLite, MySQL, TigerData, or Snowflake for use in external applications. **What’s in this recipe:** * Export entire tables or filtered queries to any SQL database * Importing exported data back into Pixeltable * Select specific columns for export * Handle existing tables with replace or append options * Connect to cloud PostgreSQL services (e.g. TigerData) ## Problem You have processed data in your pipeline—cleaned text, generated embeddings, extracted metadata—and need to send it to a SQL database for use by other applications or teams.
## Solution You use `export_sql()` & `import_sql()` to transfer tables or queries to and from any SQL database. The functions automatically map Pixeltable types to appropriate SQL types for each database dialect. ### Setup ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} %pip install -qU pixeltable snowflake-sqlalchemy ``` ### Create sample data ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} import pixeltable as pxt import tempfile from pathlib import Path from pixeltable.io.sql import export_sql, import_sql # Create a fresh directory pxt.drop_dir('sql_export_demo', force=True) pxt.create_dir('sql_export_demo') ```
  Created directory 'sql\_export\_demo'.
  \
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Create a table with product data products = pxt.create_table( 'sql_export_demo/products', { 'name': pxt.String, 'price': pxt.Float, 'in_stock': pxt.Bool, 'metadata': pxt.Json, }, ) ```
  Created table 'products'.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Insert sample products products.insert( [ { 'name': 'Wireless Mouse', 'price': 29.99, 'in_stock': True, 'metadata': {'category': 'electronics', 'rating': 4.5}, }, { 'name': 'USB-C Hub', 'price': 49.99, 'in_stock': False, 'metadata': {'category': 'electronics', 'rating': 4.2}, }, { 'name': 'Mechanical Keyboard', 'price': 89.99, 'in_stock': True, 'metadata': {'category': 'electronics', 'rating': 4.8}, }, { 'name': 'Monitor Stand', 'price': 39.99, 'in_stock': True, 'metadata': {'category': 'accessories', 'rating': 4.0}, }, { 'name': 'Webcam', 'price': 59.99, 'in_stock': False, 'metadata': {'category': 'electronics', 'rating': 3.9}, }, ] ) ```
  Inserted 5 rows with 0 errors in 0.01 s (501.18 rows/s)
  5 rows inserted.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # View the data products.collect() ```
### Export an entire table You pass a table and a SQLAlchemy connection string to export all rows and columns. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Create a SQLite database for this demo db_path = Path(tempfile.mkdtemp()) / 'products.db' connection_string = f'sqlite:///{db_path}' ``` ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Export the full table export_sql(products, 'products', db_connect_str=connection_string) ``` ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Verify the export with SQLAlchemy import sqlalchemy as sql engine = sql.create_engine(connection_string) with engine.connect() as conn: result = conn.execute(sql.text('SELECT * FROM products')).fetchall() result ```
  \[('Wireless Mouse', 29.99, 1, '\{"rating": 4.5, "category": "electronics"}'),
   ('USB-C Hub', 49.99, 0, '\{"rating": 4.2, "category": "electronics"}'),
   ('Mechanical Keyboard', 89.99, 1, '\{"rating": 4.8, "category": "electronics"}'),
   ('Monitor Stand', 39.99, 1, '\{"rating": 4.0, "category": "accessories"}'),
   ('Webcam', 59.99, 0, '\{"rating": 3.9, "category": "electronics"}')]
### Export a filtered query You can export any query result—filter rows, select specific columns, or apply transformations before export. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Export only in-stock products export_sql( products.where(products.in_stock == True), 'in_stock_products', db_connect_str=connection_string, ) ``` ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Verify filtered export with engine.connect() as conn: result = conn.execute( sql.text('SELECT name, price FROM in_stock_products') ).fetchall() result ```
  \[('Wireless Mouse', 29.99),
   ('Mechanical Keyboard', 89.99),
   ('Monitor Stand', 39.99)]
### Import a table & query You can use `import_sql()` to import an existing SQL table or query into Pixeltable. `pxt.Table` schema is automatically inferred from the schema of the existing SQL table, and can be overridden using `schema_overrides`. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Re-import the exported products sql_tbl = sql.Table('products', sql.MetaData(), autoload_with=engine) with engine.connect() as conn: import_sql( selectable=sql_tbl, conn=conn, tbl_name='sql_export_demo/imported_products', ) reimported_tbl = pxt.get_table('sql_export_demo/imported_products') reimported_tbl.collect() ```
  Created table 'imported\_products'.
  Inserted 5 rows with 0 errors in 0.01 s (779.78 rows/s)
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Re-import the exported products selecting only name and price, and overriding price type to Int (lossy cast) with engine.connect() as conn: import_sql( selectable=sql.select(sql_tbl.c.name, sql_tbl.c.price), conn=conn, tbl_name='sql_export_demo/imported_products_lossy', schema_overrides={'price': pxt.Int}, ) reimported_tbl = pxt.get_table('sql_export_demo/imported_products_lossy') reimported_tbl.collect() ```
  Created table 'imported\_products\_lossy'.
  Inserted 5 rows with 0 errors in 0.01 s (867.33 rows/s)
### Export specific columns You select only the columns you need before exporting. You can also rename columns in the output. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Export only name and price columns export_sql( products.select(products.name, products.price), 'price_list', db_connect_str=connection_string, ) ``` ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Export with renamed columns export_sql( products.select( product_name=products.name, unit_price=products.price ), 'renamed_columns', db_connect_str=connection_string, ) ``` ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Verify column selection inspector = sql.inspect(engine) columns = [col['name'] for col in inspector.get_columns('price_list')] columns ```
  \['name', 'price']
### Handle existing tables You control what happens when the target table already exists using the `if_exists` parameter:
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Append new data to existing table export_sql( products.where(products.price > 50), 'products', db_connect_str=connection_string, if_exists='insert', ) ``` ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Check row count after insert with engine.connect() as conn: result = conn.execute( sql.text('SELECT COUNT(*) FROM products') ).fetchone() f'Total rows after insert: {result[0]}' ```
  'Total rows after insert: 7'
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Replace with fresh data export_sql( products.select(products.name, products.price), 'products', db_connect_str=connection_string, if_exists='replace', ) ``` ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Check that table was replaced inspector = sql.inspect(engine) columns = [col['name'] for col in inspector.get_columns('products')] with engine.connect() as conn: row_count = conn.execute( sql.text('SELECT COUNT(*) FROM products') ).fetchone()[0] f'Columns: {columns}, Row count: {row_count}' ```
  "Columns: \['name', 'price'], Row count: 5"
### Export to cloud PostgreSQL (TigerData) You can export directly to cloud-hosted PostgreSQL databases like [TigerData](https://www.timescale.com/cloud) (Timescale Cloud). Get your credentials from the TigerData dashboard after creating a service. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} import getpass import os # Skip interactive sections in CI environments SKIP_CLOUD_TESTS = os.environ.get('CI') or os.environ.get( 'GITHUB_ACTIONS' ) if not SKIP_CLOUD_TESTS: # Enter your TigerData credentials interactively tigerdata_host = input( 'TigerData host (e.g., abc123.tsdb.cloud.timescale.com): ' ) tigerdata_port = input('TigerData port (e.g., 38963): ') tigerdata_user = input('TigerData username (e.g., tsdbadmin): ') tigerdata_password = getpass.getpass('TigerData password: ') tigerdata_dbname = input('TigerData database name (e.g., tsdb): ') # Build the connection string (use postgresql+psycopg:// for SQLAlchemy compatibility) tigerdata_connection = f'postgresql+psycopg://{tigerdata_user}:{tigerdata_password}@{tigerdata_host}:{tigerdata_port}/{tigerdata_dbname}?sslmode=require' else: print('Skipping TigerData section (running in CI)') ``` ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} if not SKIP_CLOUD_TESTS: # Export to TigerData export_sql( products, 'pixeltable_products', db_connect_str=tigerdata_connection, if_exists='replace', ) ``` ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} if not SKIP_CLOUD_TESTS: # Verify the export in TigerData tigerdata_engine = sql.create_engine(tigerdata_connection) with tigerdata_engine.connect() as conn: result = conn.execute( sql.text('SELECT * FROM pixeltable_products') ).fetchall() result ```
  \[('Wireless Mouse', 29.99, True, \{'rating': 4.5, 'category': 'electronics'}),
   ('USB-C Hub', 49.99, False, \{'rating': 4.2, 'category': 'electronics'}),
   ('Mechanical Keyboard', 89.99, True, \{'rating': 4.8, 'category': 'electronics'}),
   ('Monitor Stand', 39.99, True, \{'rating': 4.0, 'category': 'accessories'}),
   ('Webcam', 59.99, False, \{'rating': 3.9, 'category': 'electronics'})]
### Export to Snowflake You can export directly to [Snowflake](https://www.snowflake.com/) data warehouses. Get your account identifier from the Snowflake web interface under **Admin → Accounts**. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} if not SKIP_CLOUD_TESTS: # Enter your Snowflake credentials interactively snowflake_account = input( 'Snowflake account identifier (e.g., WEZMMGC-AIB20064): ' ) snowflake_user = input('Snowflake username: ') snowflake_password = getpass.getpass('Snowflake password: ') snowflake_warehouse = input( 'Snowflake warehouse (e.g., COMPUTE_WH): ' ) snowflake_database = input('Snowflake database: ') snowflake_schema = input('Snowflake schema (e.g., PUBLIC): ') # Build the connection string snowflake_connection = f'snowflake://{snowflake_user}:{snowflake_password}@{snowflake_account}/{snowflake_database}/{snowflake_schema}?warehouse={snowflake_warehouse}' else: print('Skipping Snowflake section (running in CI)') ``` ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} if not SKIP_CLOUD_TESTS: # Export to Snowflake (without JSON column) export_sql( products.select(products.name, products.price, products.in_stock), 'PIXELTABLE_PRODUCTS', db_connect_str=snowflake_connection, if_exists='replace', ) ``` ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} if not SKIP_CLOUD_TESTS: # Verify the export in Snowflake snowflake_engine = sql.create_engine(snowflake_connection) with snowflake_engine.connect() as conn: result = conn.execute( sql.text('SELECT * FROM PIXELTABLE_PRODUCTS') ).fetchall() result ```
  \[('Wireless Mouse', 29.99, True, None),
   ('USB-C Hub', 49.99, False, None),
   ('Mechanical Keyboard', 89.99, True, None),
   ('Monitor Stand', 39.99, True, None),
   ('Webcam', 59.99, False, None)]
### Exporting media data For tables containing media types (`pxt.Image`, `pxt.Video`, `pxt.Audio`), you have two options: 1. **Extract metadata before export** - Select only the columns you need (paths, embeddings, extracted text, etc.) and export those to SQL. 2. **Use Pixeltable destinations** - For syncing media files to cloud storage, use Pixeltable’s built-in destination support with providers like [Tigris](/howto/providers/working-with-tigris). **Example: Export image metadata to SQL** ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Create a table with images images = pxt.create_table( 'sql_export_demo/images', {'image': pxt.Image, 'label': pxt.String} ) # Add computed columns for metadata images.add_computed_column(width=images.image.width) images.add_computed_column(height=images.image.height) images.add_computed_column(mode=images.image.mode) ```
  Created table 'images'.
  Added 0 column values with 0 errors in 0.01 s
  Added 0 column values with 0 errors in 0.01 s
  Added 0 column values with 0 errors in 0.01 s
  No rows affected.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Insert sample images base_url = 'https://raw.githubusercontent.com/pixeltable/pixeltable/main/docs/resources/images' images.insert( [ {'image': f'{base_url}/000000000036.jpg', 'label': 'cat'}, {'image': f'{base_url}/000000000090.jpg', 'label': 'scene'}, ] ) ```
  Inserted 2 rows with 0 errors in 0.08 s (26.28 rows/s)
  2 rows inserted.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Export metadata (not the image itself) to SQL export_sql( images.select(images.label, images.width, images.height, images.mode), 'image_metadata', db_connect_str=connection_string, # or tigerdata_connection for cloud ) ``` ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Verify the metadata export with engine.connect() as conn: result = conn.execute( sql.text('SELECT * FROM image_metadata') ).fetchall() result ```
  \[('cat', 481, 640, 'RGB'), ('scene', 640, 429, 'RGB')]
### Round-trip media via fileurl Media columns can’t be written to SQL directly, but their location can. Select `images.image.fileurl` to export each image’s URL (here, the original GitHub links) as a plain string column. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Export the image locations (fileurl), not the image bytes export_sql( images.select(images.label, image=images.image.fileurl), 'image_locations', db_connect_str=connection_string, if_exists='replace', ) with engine.connect() as conn: locations = conn.execute( sql.text('SELECT label, image FROM image_locations') ).fetchall() locations ```
  \[('cat', '[https://raw.githubusercontent.com/pixeltable/pixeltable/main/docs/resources/images/000000000036.jpg\'](https://raw.githubusercontent.com/pixeltable/pixeltable/main/docs/resources/images/000000000036.jpg\');),
   ('scene', '[https://raw.githubusercontent.com/pixeltable/pixeltable/main/docs/resources/images/000000000090.jpg\')\\](https://raw.githubusercontent.com/pixeltable/pixeltable/main/docs/resources/images/000000000090.jpg\'\)\\)]
### Import the locations back as images Reflect the SQL table and import it, using `schema_overrides` to promote the location column back to `pxt.Image`. Pixeltable rehydrates each URL into a real image, so expressions like `.width` work again. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} src = sql.Table('image_locations', sql.MetaData(), autoload_with=engine) # schema_overrides promotes the location strings back into a pxt.Image column on import restored = import_sql( src, engine, 'sql_export_demo/restored_images', schema_overrides={'image': pxt.Image}, ) restored.select( restored.label, restored.image, width=restored.image.width ).collect() ```
  Created table 'restored\_images'.
  Inserted 2 rows with 0 errors in 0.01 s (142.39 rows/s)
## Explanation **Connection strings:** The function uses SQLAlchemy connection strings. Common formats:
**Type mapping:** Pixeltable types map to SQL types automatically:
**Unsupported types:** Media types like `pxt.Image`, `pxt.Video`, and `pxt.Audio` cannot be exported directly. Extract the data you need (paths, embeddings, metadata) before export. ## See also * [Working with Tigris](/howto/providers/working-with-tigris) - Sync media files to cloud storage * [Cloud Storage Integration](/integrations/cloud-storage) - S3, GCS, and Azure Blob storage * [Export to PyTorch](./data-export-pytorch) - Export for ML training # Import data from CSV files Source: https://docs.pixeltable.com/howto/cookbooks/data/data-import-csv Open in Kaggle  Open in Colab  Download Notebook This documentation page is also available as an interactive notebook. You can launch the notebook in Kaggle or Colab, or download it for use with an IDE or local Jupyter installation, by clicking one of the above links. Load data from CSV and Excel files into Pixeltable tables for processing and analysis. This notebook uses `pxt.create_table()` so you can run cells without a project. An app puts the same columns on a `TableModel` in `app.py` and creates the tables with `pxt schema update`. ## Problem You have data in CSV or Excel files that you want to process with AI models, add computed columns to, or combine with other data sources.
## Solution **What’s in this recipe:** * Import CSV files directly into tables * Import from Pandas DataFrames * Handle different data types You use `pxt.create_table()` with a `source` parameter to create a table from a CSV file, or insert DataFrame rows into an existing table. ### Setup ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} %pip install -qU pixeltable pandas ``` ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} import pandas as pd import pixeltable as pxt # Create a fresh directory pxt.drop_dir('import_demo', force=True) pxt.create_dir('import_demo') ```
  Connected to Pixeltable database at: postgresql+psycopg://postgres:@/pixeltable?host=/Users/pjlb/.pixeltable/pgdata
  Created directory 'import\_demo'.
  \
### Import CSV directly Use `create_table` with `source` to create a table from a CSV file: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Import CSV from URL csv_url = 'https://raw.githubusercontent.com/pixeltable/pixeltable/main/docs/resources/world-population-data.csv' population = pxt.create_table('import_demo/population', source=csv_url) ```
  Created table 'population'.

  Inserting rows into \`population\`: 0 rows \[00:00, ? rows/s]
  Inserting rows into \`population\`: 234 rows \[00:00, 9032.63 rows/s]
  Inserted 234 rows with 0 errors.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # View the imported data population.head(5) ```
### Import from Pandas DataFrame You can also create a DataFrame first and insert it: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Create a DataFrame df = pd.DataFrame( { 'name': ['Alice', 'Bob', 'Charlie'], 'age': [25, 30, 35], 'city': ['NYC', 'LA', 'Chicago'], } ) # Create table and insert DataFrame users = pxt.create_table( 'import_demo/users', {'name': pxt.String, 'age': pxt.Int, 'city': pxt.String}, ) users.insert(df) ```
  Created table 'users'.

  Inserting rows into \`users\`: 0 rows \[00:00, ? rows/s]
  Inserting rows into \`users\`: 3 rows \[00:00, 923.31 rows/s]
  Inserted 3 rows with 0 errors.
  3 rows inserted, 6 values computed.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # View the data users.collect() ```
## Explanation **Source types supported:**
**Type inference:** Pixeltable automatically infers column types from CSV data. You can override types using `schema_overrides`. **Large files:** For very large CSV files, consider: * Using `create_table(source=...)` which streams data * Importing in batches if memory is limited ## See also * [Tables documentation](/tutorials/tables-and-data-operations) * [Bringing data guide](/howto/cookbooks/data/data-import-csv) # Import data from Excel files Source: https://docs.pixeltable.com/howto/cookbooks/data/data-import-excel Open in Kaggle  Open in Colab  Download Notebook This documentation page is also available as an interactive notebook. You can launch the notebook in Kaggle or Colab, or download it for use with an IDE or local Jupyter installation, by clicking one of the above links. Load data from Excel spreadsheets (.xlsx) into Pixeltable tables. ## Problem You have data in Excel format that needs to be loaded for AI processing—reports, inventory lists, or business data exported from other systems.
## Solution **What’s in this recipe:** * Import Excel files directly into tables * Handle multiple sheets * Override column types when needed You use `pxt.create_table()` with an Excel file path as the `source` parameter. Pixeltable infers column types automatically. ### Setup ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} %pip install -qU pixeltable openpyxl pandas ``` ### Create sample Excel file ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} import pandas as pd import pixeltable as pxt import tempfile from pathlib import Path # Create sample Excel file for demo sample_data = pd.DataFrame( { 'order_id': [1001, 1002, 1003, 1004, 1005], 'customer': ['Alice', 'Bob', 'Carol', 'Dave', 'Eve'], 'product': [ 'Widget A', 'Gadget B', 'Widget A', 'Tool C', 'Gadget B', ], 'quantity': [2, 1, 5, 3, 2], 'price': [29.99, 149.99, 29.99, 79.99, 149.99], 'date': [ '2024-01-15', '2024-01-16', '2024-01-16', '2024-01-17', '2024-01-18', ], } ) # Save to temp Excel file temp_dir = tempfile.mkdtemp() excel_path = Path(temp_dir) / 'orders.xlsx' sample_data.to_excel(excel_path, index=False) sample_data ```
### Import Excel file ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Create a fresh directory pxt.drop_dir('excel_demo', force=True) pxt.create_dir('excel_demo') ```
  Connected to Pixeltable database at: postgresql+psycopg://postgres:@/pixeltable?host=/Users/pjlb/.pixeltable/pgdata
  Created directory 'excel\_demo'.
  \
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Import Excel file directly orders = pxt.create_table( 'excel_demo/orders', source=str(excel_path), source_format='excel', # Hint for Excel format ) ```
  Created table 'orders'.

  Inserting rows into \`orders\`: 0 rows \[00:00, ? rows/s]
  Inserting rows into \`orders\`: 5 rows \[00:00, 501.21 rows/s]
  Inserted 5 rows with 0 errors.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # View imported data orders.collect() ```
### Add computed columns ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Add computed column for order total orders.add_computed_column(total=orders.quantity * orders.price) ```
  Added 5 column values with 0 errors.
  5 rows updated, 10 values computed.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # View with computed total orders.select( orders.order_id, orders.customer, orders.product, orders.quantity, orders.price, orders.total, ).collect() ```
## Explanation **Import methods:**
**Excel-specific options:** Pass Pandas `read_excel` arguments via `extra_args`: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} pxt.create_table( 'table_name', source='data.xlsx', source_format='excel', extra_args={'sheet_name': 'Sheet2', 'skiprows': 1} ) ``` **Common extra\_args:**
## See also * [Import CSV files](/howto/cookbooks/data/data-import-csv) - CSV and tabular data * [Import Parquet files](/howto/cookbooks/data/data-import-parquet) - Columnar data # Import data from Hugging Face datasets Source: https://docs.pixeltable.com/howto/cookbooks/data/data-import-huggingface Open in Kaggle  Open in Colab  Download Notebook This documentation page is also available as an interactive notebook. You can launch the notebook in Kaggle or Colab, or download it for use with an IDE or local Jupyter installation, by clicking one of the above links. Load datasets from Hugging Face Hub into Pixeltable tables for processing with AI models. ## Problem You want to use a dataset from Hugging Face Hub—for fine-tuning, evaluation, or analysis. You need to load it into a format where you can add computed columns, embeddings, or AI transformations.
## Solution **What’s in this recipe:** * Import Hugging Face datasets directly into tables * Handle datasets with multiple splits (train/test/validation) * Work with image datasets You use `pxt.create_table()` with a Hugging Face dataset as the `source` parameter. Pixeltable automatically maps HF types to Pixeltable column types. ### Setup ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} %pip install -qU pixeltable datasets ``` ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} import pixeltable as pxt from datasets import load_dataset # Create a fresh directory pxt.drop_dir('hf_demo', force=True) pxt.create_dir('hf_demo') ```
  Created directory 'hf\_demo'.
  \
### Import a single split Load a specific split from a dataset: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Load a small subset for demo (first 100 rows of rotten_tomatoes) hf_dataset = load_dataset( 'cornell-movie-review-data/rotten_tomatoes', split='train[:100]' ) ``` ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Import into Pixeltable reviews = pxt.create_table('hf_demo/reviews', source=hf_dataset) ```
  Created table 'reviews'.
  Inserting rows into \`reviews\`: 100 rows \[00:00, 14781.69 rows/s]
  Inserted 100 rows with 0 errors.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # View imported data reviews.head(5) ```
### Import multiple splits Load a DatasetDict with multiple splits and track which split each row came from: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Load dataset with multiple splits (small subset for demo) hf_dataset_dict = load_dataset( 'cornell-movie-review-data/rotten_tomatoes', split={'train': 'train[:50]', 'test': 'test[:50]'}, ) ``` ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Import each split separately for clarity train_data = pxt.create_table( 'hf_demo/reviews_train', source=hf_dataset_dict['train'] ) test_data = pxt.create_table( 'hf_demo/reviews_test', source=hf_dataset_dict['test'] ) ```
  Created table 'reviews\_train'.
  Inserting rows into \`reviews\_train\`: 50 rows \[00:00, 10150.29 rows/s]
  Inserted 50 rows with 0 errors.
  Created table 'reviews\_test'.
  Inserting rows into \`reviews\_test\`: 50 rows \[00:00, 9883.37 rows/s]
  Inserted 50 rows with 0 errors.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # View training data train_data.head(5) ```
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # View test data test_data.head(3) ```
### Add AI-powered computed columns Enrich the dataset with AI models: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Add a computed column for text length reviews.add_computed_column(text_length=reviews.text.len()) ```
  Added 100 column values with 0 errors.
  100 rows updated, 200 values computed.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # View with computed column reviews.select(reviews.text, reviews.label, reviews.text_length).head(5) ```
### Type mapping Pixeltable automatically maps Hugging Face types to Pixeltable types:
Use `schema_overrides` to customize type mapping when needed. ## Explanation **Why import Hugging Face datasets into Pixeltable:** 1. **Add computed columns** - Enrich data with embeddings, AI analysis, or transformations 2. **Incremental processing** - Add new rows without reprocessing existing data 3. **Persistent storage** - Keep processed results across sessions 4. **Query capabilities** - Filter, aggregate, and join with other tables **Working with large datasets:** For very large datasets, consider loading in batches or using streaming mode in the `datasets` library before importing. ## See also * [Import CSV files](/howto/cookbooks/data/data-import-csv) - For CSV and Excel imports * [Semantic text search](/howto/cookbooks/search/search-semantic-text) - Add embeddings to text data * [Hugging Face integration notebook](/howto/providers/working-with-hugging-face) - Full integration guide # Import data from JSON files Source: https://docs.pixeltable.com/howto/cookbooks/data/data-import-json Open in Kaggle  Open in Colab  Download Notebook This documentation page is also available as an interactive notebook. You can launch the notebook in Kaggle or Colab, or download it for use with an IDE or local Jupyter installation, by clicking one of the above links. Load structured data from JSON files into Pixeltable tables for processing and analysis. ## Problem You have data in JSON format—from APIs, exports, or application logs. You need to load this data for processing with AI models or combining with other data sources.
## Solution **What’s in this recipe:** * Import JSON files directly into tables * Import from URLs (APIs, remote files) * Handle nested JSON structures You use `pxt.create_table()` with a `source` parameter to create a table from a JSON file or URL. The JSON must be an array of objects, where each object becomes a row. ### Setup ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} %pip install -qU pixeltable ``` ### Create sample JSON file First, create a sample JSON file to demonstrate the import process: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} import json import pixeltable as pxt import tempfile from pathlib import Path # Create sample JSON data (array of objects) sample_data = [ { 'id': 1, 'title': 'Introduction to ML', 'author': 'Alice', 'tags': ['ml', 'intro'], 'rating': 4.5, }, { 'id': 2, 'title': 'Deep Learning Basics', 'author': 'Bob', 'tags': ['dl', 'neural'], 'rating': 4.8, }, { 'id': 3, 'title': 'NLP Fundamentals', 'author': 'Carol', 'tags': ['nlp', 'text'], 'rating': 4.2, }, { 'id': 4, 'title': 'Computer Vision', 'author': 'Dave', 'tags': ['cv', 'images'], 'rating': 4.6, }, { 'id': 5, 'title': 'Reinforcement Learning', 'author': 'Eve', 'tags': ['rl', 'agents'], 'rating': 4.3, }, ] # Save to temporary JSON file temp_dir = tempfile.mkdtemp() json_path = Path(temp_dir) / 'articles.json' with open(json_path, 'w') as f: json.dump(sample_data, f, indent=2) ``` ### Import JSON file Use `create_table` with `source` to create a table directly from a JSON file: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Create a fresh directory pxt.drop_dir('json_demo', force=True) pxt.create_dir('json_demo') ```
  Connected to Pixeltable database at: postgresql+psycopg://postgres:@/pixeltable?host=/Users/pjlb/.pixeltable/pgdata
  Created directory 'json\_demo'.
  \
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Import JSON file into a new table articles = pxt.create_table( 'json_demo/articles', source=str(json_path), source_format='json', # Explicitly specify format when using local file paths ) ```
  Created table 'articles'.

  Inserting rows into \`articles\`: 0 rows \[00:00, ? rows/s]
  Inserting rows into \`articles\`: 5 rows \[00:00, 538.52 rows/s]
  Inserted 5 rows with 0 errors.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # View imported data articles.collect() ```
### Import from URL You can import JSON directly from a URL—useful for APIs and remote data: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Import from a public JSON URL # Using JSONPlaceholder API as an example posts = pxt.create_table( 'json_demo/posts', source='https://jsonplaceholder.typicode.com/posts', source_format='json', # Required for URL sources ) ```
  Created table 'posts'.

  Inserting rows into \`posts\`: 0 rows \[00:00, ? rows/s]
  Inserting rows into \`posts\`: 100 rows \[00:00, 15623.57 rows/s]
  Inserted 100 rows with 0 errors.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # View first few rows posts.head(5) ```
### Import from Python dictionaries Use `create_table` with a list of dictionaries as `source`—useful when you have data in memory: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Import from a list of dictionaries events = [ { 'event': 'page_view', 'user_id': 101, 'timestamp': '2024-01-15T10:30:00', }, { 'event': 'click', 'user_id': 101, 'timestamp': '2024-01-15T10:31:00', }, { 'event': 'purchase', 'user_id': 102, 'timestamp': '2024-01-15T10:32:00', }, ] event_table = pxt.create_table('json_demo/events', source=events) ```
  Created table 'events'.

  Inserting rows into \`events\`: 0 rows \[00:00, ? rows/s]
  Inserting rows into \`events\`: 3 rows \[00:00, 988.06 rows/s]
  Inserted 3 rows with 0 errors.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # View imported events event_table.collect() ```
### Add computed columns Once imported, you can enrich the data with computed columns: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Add a computed column combining title and author articles.add_computed_column( summary=articles.title + ' by ' + articles.author ) ```
  Added 5 column values with 0 errors.
  5 rows updated, 10 values computed.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # View with computed column articles.select( articles.title, articles.author, articles.summary ).collect() ```
## Explanation **JSON format requirements:** The JSON file must contain an array of objects at the top level: ```json theme={"theme":{"light":"light-plus","dark":"dark-plus"}} [ {"col1": "value1", "col2": 123}, {"col1": "value2", "col2": 456} ] ``` **Source types supported:**
**Nested JSON handling:** Nested objects and arrays are stored as JSON columns. You can access nested fields using Pixeltable’s JSON path syntax in computed columns. ## See also * [Import CSV files](/howto/cookbooks/data/data-import-csv) - For CSV and Excel imports * [Import Parquet files](/howto/cookbooks/data/data-import-parquet) - For Parquet data * [Extract fields from JSON](/howto/cookbooks/core/workflow-json-extraction) - Parse LLM response fields # Import data from Parquet files Source: https://docs.pixeltable.com/howto/cookbooks/data/data-import-parquet Open in Kaggle  Open in Colab  Download Notebook This documentation page is also available as an interactive notebook. You can launch the notebook in Kaggle or Colab, or download it for use with an IDE or local Jupyter installation, by clicking one of the above links. Load columnar data from Parquet files into Pixeltable tables for processing and analysis. ## Problem You have data stored in Parquet format—a common format for analytics, data lakes, and ML pipelines. You need to load this data for processing with AI models or combining with other data sources.
## Solution **What’s in this recipe:** * Import Parquet files directly into tables * Export tables to Parquet for external tools * Handle schema type overrides You use `pxt.create_table()` with a `source` parameter to create a table from a Parquet file. Pixeltable infers column types from the Parquet schema automatically. ### Setup ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} %pip install -qU pixeltable pyarrow pandas ``` ### Create sample Parquet file First, create a sample Parquet file to demonstrate the import process: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} import pandas as pd import pixeltable as pxt import tempfile from pathlib import Path # Create sample data sample_data = pd.DataFrame( { 'product_id': [1, 2, 3, 4, 5], 'name': [ 'Widget A', 'Widget B', 'Gadget X', 'Gadget Y', 'Tool Z', ], 'price': [29.99, 39.99, 149.99, 199.99, 79.99], 'category': ['widgets', 'widgets', 'gadgets', 'gadgets', 'tools'], 'in_stock': [True, False, True, True, False], } ) # Save to temporary Parquet file temp_dir = tempfile.mkdtemp() parquet_path = Path(temp_dir) / 'products.parquet' sample_data.to_parquet(parquet_path, index=False) sample_data ```
### Import Parquet file Use `create_table` with the `source` parameter to create a table directly from the Parquet file: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Create a fresh directory pxt.drop_dir('parquet_demo', force=True) pxt.create_dir('parquet_demo') ```
  Connected to Pixeltable database at: postgresql+psycopg://postgres:@/pixeltable?host=/Users/pjlb/.pixeltable/pgdata
  Created directory 'parquet\_demo'.
  \
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Import Parquet file into a new table products = pxt.create_table( 'parquet_demo/products', source=str(parquet_path) ) ```
  Created table 'products'.

  Inserting rows into \`products\`: 0 rows \[00:00, ? rows/s]
  Inserting rows into \`products\`: 5 rows \[00:00, 653.18 rows/s]
  Inserted 5 rows with 0 errors.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # View imported data products.collect() ```
### Add computed columns Once imported, you can add computed columns like any other Pixeltable table: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Add a computed column for discounted price products.add_computed_column(sale_price=products.price * 0.9) ```
  Added 5 column values with 0 errors.
  5 rows updated, 10 values computed.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # View with computed column products.select( products.name, products.price, products.sale_price ).collect() ```
### Import with primary key Specify a primary key when you need upsert behavior or unique constraints: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Import with a primary key products_pk = pxt.create_table( 'parquet_demo/products_with_pk', source=str(parquet_path), primary_key='product_id', ) ```
  Created table 'products\_with\_pk'.

  Inserting rows into \`products\_with\_pk\`: 0 rows \[00:00, ? rows/s]
  Inserting rows into \`products\_with\_pk\`: 5 rows \[00:00, 1548.97 rows/s]
  Inserted 5 rows with 0 errors.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # View the table products_pk.collect() ```
### Export table to Parquet Export your processed data back to Parquet for use with other toolee ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Export to Parquet (note: image columns require inline_images=True) export_path = Path(temp_dir) / 'exported_products' pxt.io.export_parquet( products.select(products.name, products.price, products.sale_price), parquet_path=export_path, ) ``` ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Verify export by reading back import pyarrow.parquet as pq exported_table = pq.read_table(export_path) exported_table.to_pandas() ```
## Explanation **When to use Parquet import:**
**Key features:** * Automatic schema inference from Parquet metadata * Support for partitioned datasets (directory of files) * Export with `pxt.io.export_parquet` for interoperability * Primary key support for upsert workflows ## See also * [Import CSV files](/howto/cookbooks/data/data-import-csv) - For CSV and Excel imports * [Import JSON files](/howto/cookbooks/data/data-import-json) - For JSON data # Load media from S3 and other cloud storage Source: https://docs.pixeltable.com/howto/cookbooks/data/data-import-s3 Open in Kaggle  Open in Colab  Download Notebook This documentation page is also available as an interactive notebook. You can launch the notebook in Kaggle or Colab, or download it for use with an IDE or local Jupyter installation, by clicking one of the above links. Import images, videos, and audio files from S3, GCS, HTTP URLs, or local paths into Pixeltable tables. ## Problem You have media files stored in cloud storage (S3, GCS) or accessible via HTTP URLs. You need to process these files with AI models without downloading them all upfront.
## Solution **What’s in this recipe:** * Reference media files by URL (S3, HTTP, local paths) * Automatic caching of remote files on access * Process files lazily without bulk downloads You insert media URLs as references. Pixeltable stores the URLs and automatically downloads/caches files when you access them through queries or computed columns. ### Setup ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} %pip install -qU pixeltable ``` ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} import pixeltable as pxt # Create a fresh directory pxt.drop_dir('cloud_demo', force=True) pxt.create_dir('cloud_demo') ```
  Connected to Pixeltable database at: postgresql+psycopg://postgres:@/pixeltable?host=/Users/pjlb/.pixeltable/pgdata
  Created directory 'cloud\_demo'.
  \
### Load images from HTTP URLs Reference images by URL—Pixeltable downloads them on demand: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Create a table with image column images = pxt.create_table('cloud_demo/images', {'image': pxt.Image}) ```
  Created table 'images'.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Insert images by URL (HTTP) image_urls = [ 'https://raw.githubusercontent.com/pixeltable/pixeltable/main/docs/resources/images/000000000036.jpg', 'https://raw.githubusercontent.com/pixeltable/pixeltable/main/docs/resources/images/000000000090.jpg', 'https://raw.githubusercontent.com/pixeltable/pixeltable/main/docs/resources/images/000000000106.jpg', ] images.insert([{'image': url} for url in image_urls]) ```
  Inserting rows into \`images\`: 3 rows \[00:00, 767.91 rows/s]
  Inserted 3 rows with 0 errors.
  3 rows inserted, 6 values computed.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # View images - files are downloaded and cached on access images.collect() ```
### Load videos from S3 Reference videos in S3 buckets (using public Multimedia Commons bucket): ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Create a table with video column videos = pxt.create_table('cloud_demo/videos', {'video': pxt.Video}) ```
  Created table 'videos'.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Insert videos by S3 URL (public bucket, no credentials needed) s3_prefix = 's3://multimedia-commons/' video_paths = [ 'data/videos/mp4/ffe/ffb/ffeffbef41bbc269810b2a1a888de.mp4', 'data/videos/mp4/ffe/feb/ffefebb41485539f964760e6115fbc44.mp4', ] videos.insert([{'video': s3_prefix + path} for path in video_paths]) ```
  Inserting rows into \`videos\`: 2 rows \[00:00, 1477.13 rows/s]
  Inserted 2 rows with 0 errors.
  2 rows inserted, 4 values computed.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # View videos - downloaded and cached on access videos.collect() ```
### Add computed columns on remote media Process remote media with computed columns—files are fetched automatically: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Add computed columns for image properties images.add_computed_column(width=images.image.width) images.add_computed_column(height=images.image.height) ```
  Added 3 column values with 0 errors.
  Added 3 column values with 0 errors.
  3 rows updated, 6 values computed.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # View with computed properties images.select(images.image, images.width, images.height).collect() ```
### Generate presigned URLs for serving media When you store media in private cloud storage, you need presigned URLs to serve files over HTTP. The `presigned_url` function converts storage URIs to time-limited, publicly accessible URLs: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} import pixeltable.functions as pxtf # Generate presigned URLs for videos (1-hour expiration) videos.select( videos.video, original_uri=videos.video.fileurl, http_url=pxtf.net.presigned_url(videos.video.fileurl, 3600), ).collect() ```
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Store presigned URLs as computed column for API responses videos.add_computed_column( serving_url=pxtf.net.presigned_url( videos.video.fileurl, 86400 ) # 24-hour expiration ) ```
  Added 2 column values with 0 errors.
  2 rows updated, 4 values computed.
**Use cases for presigned URLs:** * Serve private media in web applications without exposing credentials * Generate download links for end users * Integrate with CDNs or video players that require HTTP URLs **Provider limitations:**
Note: HTTP/HTTPS URLs pass through unchanged (already publicly accessible). ### Supported URL formats Pixeltable supports multiple URL schemes for media files:
\*Configure AWS/GCP credentials via environment variables or config files. ## Explanation **How caching works:** 1. URLs are stored as references in the table 2. Files are downloaded on first access (query or computed column) 3. Downloaded files are cached in `~/.pixeltable/file_cache/` 4. Cache uses LRU eviction when space is needed **Benefits of URL-based storage:** * **Lazy loading** - Only download files when needed * **Deduplication** - Same URL is cached once * **Incremental processing** - Add files without bulk downloads * **Cloud-native** - Works directly with object storage **For private S3 buckets:** Configure AWS credentials using standard methods: * Environment variables (`AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY`) * AWS credentials file (`~/.aws/credentials`) * IAM roles (when running on EC2/ECS) ## See also * [Upload to S3](../../../howto/cookbooks/data/data-export-s3) - Store generated media in S3/GCS * [Import from CSV](../../../howto/cookbooks/data/data-import-csv) - Load structured data * [Extract frames from videos](/howto/cookbooks/video/video-extract-frames) - Process video files * [Analyze images in batch](/howto/cookbooks/images/vision-batch-analysis) - AI vision on images * [Configure API keys](/howto/cookbooks/core/workflow-api-keys) - Set up credentials # Sample data for training and testing Source: https://docs.pixeltable.com/howto/cookbooks/data/data-sampling Open in Kaggle  Open in Colab  Download Notebook This documentation page is also available as an interactive notebook. You can launch the notebook in Kaggle or Colab, or download it for use with an IDE or local Jupyter installation, by clicking one of the above links. Create training, validation, and test splits with random or stratified sampling. ## Problem You have a large dataset and need to create subsets for ML training—random samples for quick experiments, stratified samples for balanced classes, or reproducible splits for benchmarking.
## Solution **What’s in this recipe:** * Random sampling with `sample(n=...)` * Percentage-based sampling with `sample(fraction=...)` * Stratified sampling with `stratify_by=` You use `query.sample()` to create random subsets, with optional stratification for balanced class distribution. ### Setup ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} %pip install -qU pixeltable ``` ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} import pixeltable as pxt # Create a fresh directory pxt.drop_dir('sampling_demo', force=True) pxt.create_dir('sampling_demo') ```
  Connected to Pixeltable database at: postgresql+psycopg://postgres:@/pixeltable?host=/Users/pjlb/.pixeltable/pgdata
  Created directory 'sampling\_demo'.
  \
### Create sample dataset ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Create a dataset with labels data = pxt.create_table( 'sampling_demo/data', {'text': pxt.String, 'label': pxt.String, 'score': pxt.Float}, ) # Insert sample data with imbalanced classes samples = [ {'text': 'Great product!', 'label': 'positive', 'score': 0.9}, {'text': 'Love it', 'label': 'positive', 'score': 0.85}, {'text': 'Amazing quality', 'label': 'positive', 'score': 0.95}, {'text': 'Best purchase ever', 'label': 'positive', 'score': 0.88}, {'text': 'Highly recommend', 'label': 'positive', 'score': 0.92}, {'text': 'Fantastic!', 'label': 'positive', 'score': 0.91}, {'text': 'Terrible', 'label': 'negative', 'score': 0.1}, {'text': 'Waste of money', 'label': 'negative', 'score': 0.15}, {'text': 'It is okay', 'label': 'neutral', 'score': 0.5}, {'text': 'Average product', 'label': 'neutral', 'score': 0.55}, ] data.insert(samples) ```
  Created table 'data'.

  Inserting rows into \`data\`: 0 rows \[00:00, ? rows/s]
  Inserting rows into \`data\`: 10 rows \[00:00, 857.13 rows/s]
  Inserted 10 rows with 0 errors.
  10 rows inserted, 20 values computed.
### Random sampling ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Sample exactly N rows data.sample(n=5, seed=42).collect() ```
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Sample a percentage of rows sample_50pct = data.sample(fraction=0.5, seed=42).collect() ``` ### Stratified sampling ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Stratified sampling: 50% from each class data.sample(fraction=0.5, stratify_by=data.label, seed=42).collect() ```
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Equal allocation: N rows from each class data.sample(n_per_stratum=1, stratify_by=data.label, seed=42).collect() ```
### Sampling from filtered data ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Sample from filtered query (high-confidence predictions only) data.where(data.score > 0.8).sample(n=3, seed=42).collect() ```
### Persist samples as tables ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Create a persistent table from a sample for dev/test train_sample = data.sample(fraction=0.8, seed=42) test_sample = data.sample(fraction=0.2, seed=43) # Persist as new tables train_table = pxt.create_table('sampling_demo/train', source=train_sample) test_table = pxt.create_table('sampling_demo/test', source=test_sample) ```
  Created table 'train'.

  Inserting rows into \`train\`: 0 rows \[00:00, ? rows/s]
  Inserting rows into \`train\`: 9 rows \[00:00, 3080.27 rows/s]
  Created table 'test'.

  Inserting rows into \`test\`: 0 rows \[00:00, ? rows/s]
  Inserting rows into \`test\`: 3 rows \[00:00, 1333.92 rows/s]
## Explanation **Sampling methods:**
**Stratification options:**
**Tips:** * Always set `seed` for reproducible experiments * Use stratified sampling for imbalanced datasets * Combine with `.where()` to sample from subsets ## See also * [Export for ML training](/howto/cookbooks/data/data-export-pytorch) - PyTorch DataLoader export * [Import Hugging Face datasets](/howto/cookbooks/data/data-import-huggingface) - Load pre-split datasets # Add watermarks to images Source: https://docs.pixeltable.com/howto/cookbooks/images/img-add-watermarks Open in Kaggle  Open in Colab  Download Notebook This documentation page is also available as an interactive notebook. You can launch the notebook in Kaggle or Colab, or download it for use with an IDE or local Jupyter installation, by clicking one of the above links. ## Problem You need to add watermarks to hundreds of different images to protect copyright, add branding, or mark drafts. ## Solution **What’s in this recipe:** * Create simple text watermarks * Test transformations before applying * Apply to multiple images automatically You add watermarks to images using a custom UDF that wraps Pillow’s `ImageDraw` (relies on PIL/Pillow). This gives you full control over watermark placement, font, transparency, and color. You can iterate on transformations before adding them to your table. Use `.select()` with `.collect()` to preview results on sample images—nothing is stored in your table. If you want to collect only the first few rows, use `.head(n)` instead of `.collect()`. Once you’re satisfied, use `.add_computed_column()` to apply watermarks to all images in your table. For more on this workflow, see [Get fast feedback on transformations](/howto/cookbooks/core/dev-iterative-workflow). ### Setup ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} %pip install -qU pixeltable ``` ### Load images ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} import pixeltable as pxt from PIL import Image, ImageDraw, ImageFont # Create a fresh directory (drop existing if present) pxt.drop_dir('image_demo', force=True) pxt.create_dir('image_demo') ```
  Connected to Pixeltable database at: postgresql+psycopg://postgres:@/pixeltable?host=/Users/pjlb/.pixeltable/pgdata
  Created directory 'image\_demo'.
  \
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} t = pxt.create_table('image_demo/watermarks', {'image': pxt.Image}) ```
  Created table 'watermarks'.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} t.insert( [ { 'image': 'https://raw.githubusercontent.com/pixeltable/pixeltable/main/docs/resources/images/000000000049.jpg' }, { 'image': 'https://raw.githubusercontent.com/pixeltable/pixeltable/main/docs/resources/images/000000000036.jpg' }, { 'image': 'https://raw.githubusercontent.com/pixeltable/pixeltable/main/docs/resources/images/000000000090.jpg' }, ] ) ```
  Inserting rows into \`watermarks\`: 0 rows \[00:00, ? rows/s]
  Inserting rows into \`watermarks\`: 3 rows \[00:00, 532.86 rows/s]
  Inserted 3 rows with 0 errors.
  3 rows inserted, 6 values computed.
### Iterate: add watermarks to a few images first ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf def add_watermark(img: Image.Image, text: str) -> Image.Image: """Add a watermark to bottom-right corner.""" img = img.copy().convert('RGBA') overlay = Image.new('RGBA', img.size, (0, 0, 0, 0)) draw = ImageDraw.Draw(overlay) # Draw white text in bottom-right corner font = ImageFont.load_default(size=40) position = (img.width - 150, img.height - 60) draw.text(position, text, font=font, fill=(255, 255, 255, 200)) result = Image.alpha_composite(img, overlay) return result.convert('RGB') ``` ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Test on first image t.select(t.image, add_watermark(t.image, '© 2024')).head(1) ```
### Add: add watermarks to all images in your table ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Add watermark to all images t.add_computed_column(watermarked=add_watermark(t.image, '© 2024')) ```
  Added 3 column values with 0 errors.
  3 rows updated, 3 values computed.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # View all results t.collect() ```
## Explanation **How the watermark technique works:** The UDF creates a transparent overlay on top of each image. The overlay is created with the same dimensions as the image (`Image.new('RGBA', img.size, ...)`), so watermarks adapt automatically whether you’re processing small thumbnails or large photos. The function draws white text with semi-transparent fill (alpha=200, where 255 is fully opaque), composites the overlay onto the original image using `Image.alpha_composite()`, and converts back to RGB since most image formats don’t support transparency. **To customize the UDF:** * Position: Change the `(x, y)` coordinates in the `position` variable * Color: Modify the `(R, G, B, Alpha)` fill value (0-255 for each) * Size: Adjust the font size parameter in `ImageFont.load_default(size=40)` * Font: Use `ImageFont.truetype('path/to/font.ttf', size)` for custom fonts **The Pixeltable workflow:** In traditional databases, `.select()` just picks which columns to view. In Pixeltable, `.select()` also lets you compute new transformations on the fly—define new columns without storing them. This makes `.select()` perfect for testing transformations before you commit them. When you use `.select()`, you’re creating a query that doesn’t execute until you call `.collect()`. You must use `.collect()` to execute the query and return results—nothing is stored in your table. If you want to collect only the first few rows, use `.head(n)` instead of `.collect()` to test on a subset before processing your full dataset. Once satisfied, use `.add_computed_column()` with the same expression to persist results permanently. For more on this workflow, see [Get fast feedback on transformations](/howto/cookbooks/core/dev-iterative-workflow). ## See also * [Test transformations with fast feedback loops](/howto/cookbooks/core/dev-iterative-workflow) * [Transform images with PIL operations](/howto/cookbooks/images/img-pil-transforms) * *Pillow techniques from [Real Python: Image Processing With the Python Pillow Library](https://realpython.com/image-processing-with-the-python-pillow-library/)* # Adjust image opacity Source: https://docs.pixeltable.com/howto/cookbooks/images/img-adjust-opacity Open in Kaggle  Open in Colab  Download Notebook This documentation page is also available as an interactive notebook. You can launch the notebook in Kaggle or Colab, or download it for use with an IDE or local Jupyter installation, by clicking one of the above links. ## Problem You need to make hundreds of images semi-transparent for backgrounds, overlays, or watermarks. ## Solution **What’s in this recipe:** * Set image opacity (transparency level) * Test transformations before applying * Apply to multiple images automatically You adjust image transparency using a custom UDF that modifies alpha channels (relies on PIL/Pillow). This gives you precise control over transparency levels. You can iterate on transformations before adding them to your table. Use `.select()` with `.collect()` to preview results on sample images—nothing is stored in your table. If you want to collect only the first few rows, use `.head(n)` instead of `.collect()`. Once you’re satisfied, use `.add_computed_column()` to apply the opacity adjustment to all images in your table. For more on this workflow, see [Get fast feedback on transformations](/howto/cookbooks/core/dev-iterative-workflow). ### Setup ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} %pip install -qU pixeltable ``` ### Load images ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} import pixeltable as pxt from PIL import Image # Create a fresh directory (drop existing if present) pxt.drop_dir('image_demo', force=True) pxt.create_dir('image_demo') ```
  Connected to Pixeltable database at: postgresql+psycopg://postgres:@/pixeltable?host=/Users/pjlb/.pixeltable/pgdata
  Created directory 'image\_demo'.
  \
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} t = pxt.create_table('image_demo/opacity', {'image': pxt.Image}) ```
  Created table 'opacity'.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} t.insert( [ { 'image': 'https://raw.githubusercontent.com/pixeltable/pixeltable/main/docs/resources/images/000000000776.jpg' }, { 'image': 'https://raw.githubusercontent.com/pixeltable/pixeltable/main/docs/resources/images/000000000885.jpg' }, { 'image': 'https://raw.githubusercontent.com/pixeltable/pixeltable/main/docs/resources/images/000000000016.jpg' }, ] ) ```
  Inserting rows into \`opacity\`: 0 rows \[00:00, ? rows/s]
  Inserting rows into \`opacity\`: 3 rows \[00:00, 545.05 rows/s]
  Inserted 3 rows with 0 errors.
  3 rows inserted, 6 values computed.
### Iterate: adjust opacity for a few images first You define a custom function using the `@pxt.udf` decorator to make it available in Pixeltable. Inside the function, you use standard PIL (Pillow) operations to manipulate images. Pixeltable handles applying your function to every row in your table. **How it works:** * All image manipulation (`.convert()`, `.split()`, `.point()`, `.putalpha()`) comes from the PIL/Pillow library * These are standard Python image operations—see [Pillow docs](https://pillow.readthedocs.io/) for reference * The `@pxt.udf` decorator lets Pixeltable apply your function to table rows * The opacity parameter (0.0 = fully transparent, 1.0 = fully opaque) controls the alpha scaling ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf def set_opacity(img: Image.Image, opacity: float) -> Image.Image: """Set image opacity (0.0 = fully transparent, 1.0 = fully opaque).""" img = img.convert('RGBA') alpha = img.split()[3] # Get alpha channel alpha = alpha.point(lambda p: int(p * opacity)) # Scale alpha values img.putalpha(alpha) return img ``` ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Test 25%, 50%, and 75% opacity t.select( t.image, alpha_25=set_opacity(t.image, 0.25), alpha_50=set_opacity(t.image, 0.5), alpha_75=set_opacity(t.image, 0.75), ).head(1) ```
### Add: adjust opacity for all images in your table ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Create 50% opacity for backgrounds t.add_computed_column(semi_transparent=set_opacity(t.image, 0.5)) ```
  Added 3 column values with 0 errors.
  3 rows updated, 3 values computed.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # View original and semi-transparent side by side t.collect() ```
## Explanation **How the opacity technique works:** The UDF modifies the alpha channel to control transparency. The function converts the image to RGBA mode (which includes an alpha channel for transparency), extracts the alpha channel with `.split()[3]`, scales all values by the desired opacity factor using `.point(lambda p: int(p * opacity))`, and applies it back with `.putalpha()`. This preserves the original image while adjusting only the transparency level. **To customize the UDF:** * **Opacity levels**: Use 0.25 for very faint backgrounds, 0.5 for standard transparency, 0.75 for subtle effects * **Selective transparency**: Modify the lambda function in `.point()` to apply different transparency to different pixel values * **Preserve regions**: Add conditional logic to keep certain areas fully opaque **The Pixeltable workflow:** In traditional databases, `.select()` just picks which columns to view. In Pixeltable, `.select()` also lets you compute new transformations on the fly—define new columns without storing them. This makes `.select()` perfect for testing transformations before you commit them. When you use `.select()`, you’re creating a query that doesn’t execute until you call `.collect()`. You must use `.collect()` to execute the query and return results—nothing is stored in your table. If you want to collect only the first few rows, use `.head(n)` instead of `.collect()` to test on a subset before processing your full dataset. Once satisfied, use `.add_computed_column()` with the same expression to persist results permanently. For more on this workflow, see [Get fast feedback on transformations](/howto/cookbooks/core/dev-iterative-workflow). ## See also * [Test transformations with fast feedback loops](/howto/cookbooks/core/dev-iterative-workflow) * [Add watermarks to images](/howto/cookbooks/images/img-add-watermarks) * [Transform images with PIL operations](/howto/cookbooks/images/img-pil-transforms) # Apply image filters Source: https://docs.pixeltable.com/howto/cookbooks/images/img-apply-filters Open in Kaggle  Open in Colab  Download Notebook This documentation page is also available as an interactive notebook. You can launch the notebook in Kaggle or Colab, or download it for use with an IDE or local Jupyter installation, by clicking one of the above links. ## Problem You need to apply filters to hundreds of images—blur, sharpen, edge detection, and other enhancements. ## Solution **What’s in this recipe:** * Apply common image filters * Test filters before applying * Process multiple images in batch You apply image filters (blur, sharpen, edge detection) to images in your table using custom UDFs that wrap Pillow’s `ImageFilter` module (relies on PIL/Pillow). This gives you control over filter parameters. You can iterate on transformations before adding them to your table. Use `.select()` with `.collect()` to preview results on sample images—nothing is stored in your table. If you want to collect only the first few rows, use `.head(n)` instead of `.collect()`. Once you’re satisfied, use `.add_computed_column()` to apply the filter to all images in your table. For more on this workflow, see [Get fast feedback on transformations](/howto/cookbooks/core/dev-iterative-workflow). ### Setup ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} %pip install -qU pixeltable ``` ### Load images ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} import pixeltable as pxt from PIL import ImageFilter # Create a fresh directory (drop existing if present) pxt.drop_dir('image_demo', force=True) pxt.create_dir('image_demo') ```
  Connected to Pixeltable database at: postgresql+psycopg://postgres:@/pixeltable?host=/Users/pjlb/.pixeltable/pgdata
  Created directory 'image\_demo'.
  \
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} t = pxt.create_table('image_demo/filters', {'image': pxt.Image}) ```
  Created table 'filters'.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} t.insert( [ { 'image': 'https://raw.githubusercontent.com/pixeltable/pixeltable/main/docs/resources/images/000000000090.jpg' }, { 'image': 'https://raw.githubusercontent.com/pixeltable/pixeltable/main/docs/resources/images/000000000106.jpg' }, { 'image': 'https://raw.githubusercontent.com/pixeltable/pixeltable/main/docs/resources/images/000000000285.jpg' }, ] ) ```
  Inserting rows into \`filters\`: 0 rows \[00:00, ? rows/s]
  Inserting rows into \`filters\`: 3 rows \[00:00, 538.79 rows/s]
  Inserted 3 rows with 0 errors.
  3 rows inserted, 6 values computed.
### Iterate: apply filters to a few images first ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf def apply_blur(img: pxt.Image) -> pxt.Image: """Apply blur filter.""" return img.filter(ImageFilter.BLUR) @pxt.udf def apply_sharpen(img: pxt.Image) -> pxt.Image: """Apply sharpen filter.""" return img.filter(ImageFilter.SHARPEN) @pxt.udf def apply_find_edges(img: pxt.Image) -> pxt.Image: """Apply edge detection filter.""" return img.filter(ImageFilter.FIND_EDGES) @pxt.udf def apply_edge_enhance(img: pxt.Image) -> pxt.Image: """Apply edge enhancement filter.""" return img.filter(ImageFilter.EDGE_ENHANCE) ``` ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Test blur and sharpen t.select(t.image, apply_blur(t.image), apply_sharpen(t.image)).head(1) ```
### Add: apply filters to all images in your table ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Add filter columns t.add_computed_column(blurred=apply_blur(t.image)) t.add_computed_column(sharpened=apply_sharpen(t.image)) t.add_computed_column(edges=apply_find_edges(t.image)) t.add_computed_column(edge_enhanced=apply_edge_enhance(t.image)) ```
  Added 3 column values with 0 errors.
  Added 3 column values with 0 errors.
  Added 3 column values with 0 errors.
  Added 3 column values with 0 errors.
  3 rows updated, 3 values computed.
### View results Compare original and filtered images. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Compare blur and sharpen t.select(t.image, t.blurred, t.sharpened).collect() ```
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Compare edge detection filters t.select(t.image, t.edges, t.edge_enhanced).collect() ```
## Explanation **How the filter technique works:** The UDFs wrap PIL’s `ImageFilter` module to apply convolution-based filters to images. Each filter uses a predefined kernel that processes pixel neighborhoods to achieve different effects. Blur averages surrounding pixels to reduce detail, Sharpen enhances pixel differences to increase detail, Find Edges detects boundaries between contrasting regions, and Edge Enhance strengthens edges while preserving the full image. You can apply multiple filters to the same image to create different versions for analysis or visual effects. **To customize the UDFs:** * **Blur intensity**: Use `ImageFilter.BoxBlur(radius)` or `ImageFilter.GaussianBlur(radius)` for adjustable blur strength * **Edge detection**: Combine with grayscale conversion for clearer edge maps * **Filter stacking**: Apply multiple filters in sequence for complex effects * **Custom kernels**: Use `ImageFilter.Kernel()` to define your own convolution filters **The Pixeltable workflow:** In traditional databases, `.select()` just picks which columns to view. In Pixeltable, `.select()` also lets you compute new transformations on the fly—define new columns without storing them. This makes `.select()` perfect for testing transformations before you commit them. When you use `.select()`, you’re creating a query that doesn’t execute until you call `.collect()`. You must use `.collect()` to execute the query and return results—nothing is stored in your table. If you want to collect only the first few rows, use `.head(n)` instead of `.collect()` to test on a subset before processing your full dataset. Once satisfied, use `.add_computed_column()` with the same expression to persist results permanently. For more on this workflow, see [Get fast feedback on transformations](/howto/cookbooks/core/dev-iterative-workflow). ## See also * [Test transformations with fast feedback loops](/howto/cookbooks/core/dev-iterative-workflow) * [Adjust image brightness and contrast](/howto/cookbooks/images/img-brightness-contrast) * *Pillow techniques from [Real Python: Image Processing With the Python Pillow Library](https://realpython.com/image-processing-with-the-python-pillow-library/)* # Adjust image brightness and contrast Source: https://docs.pixeltable.com/howto/cookbooks/images/img-brightness-contrast Open in Kaggle  Open in Colab  Download Notebook This documentation page is also available as an interactive notebook. You can launch the notebook in Kaggle or Colab, or download it for use with an IDE or local Jupyter installation, by clicking one of the above links. ## Problem You need to fix inconsistent lighting across hundreds of images—adjusting brightness, contrast, and color saturation. ## Solution **What’s in this recipe:** * Adjust brightness, contrast, and saturation * Test adjustments before applying * Process multiple images in batch You adjust brightness, contrast, and saturation for images in your table using custom UDFs that wrap Pillow’s `ImageEnhance` module (relies on PIL/Pillow). This lets you control enhancement levels to match your needs. You can iterate on transformations before adding them to your table. Use `.select()` with `.collect()` to preview results on sample images—nothing is stored in your table. If you want to collect only the first few rows, use `.head(n)` instead of `.collect()`. Once you’re satisfied, use `.add_computed_column()` to apply the adjustments to all images in your table. For more on this workflow, see [Get fast feedback on transformations](/howto/cookbooks/core/dev-iterative-workflow). ### Setup ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} %pip install -qU pixeltable ``` ### Load images ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} import pixeltable as pxt from PIL import ImageEnhance # Create a fresh directory (drop existing if present) pxt.drop_dir('image_demo', force=True) pxt.create_dir('image_demo') ```
  Connected to Pixeltable database at: postgresql+psycopg://postgres:@/pixeltable?host=/Users/pjlb/.pixeltable/pgdata
  Created directory 'image\_demo'.
  \
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} t = pxt.create_table('image_demo/enhancements', {'image': pxt.Image}) ```
  Created table 'enhancements'.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} t.insert( [ { 'image': 'https://raw.githubusercontent.com/pixeltable/pixeltable/main/docs/resources/images/000000000016.jpg' }, { 'image': 'https://raw.githubusercontent.com/pixeltable/pixeltable/main/docs/resources/images/000000000049.jpg' }, { 'image': 'https://raw.githubusercontent.com/pixeltable/pixeltable/main/docs/resources/images/000000000036.jpg' }, ] ) ```
  Inserting rows into \`enhancements\`: 0 rows \[00:00, ? rows/s]
  Inserting rows into \`enhancements\`: 3 rows \[00:00, 601.16 rows/s]
  Inserted 3 rows with 0 errors.
  3 rows inserted, 6 values computed.
### Iterate: adjust brightness and contrast for a few images first ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf def adjust_brightness(img: pxt.Image, factor: float) -> pxt.Image: """Adjust brightness. factor < 1 = darker, > 1 = brighter.""" return ImageEnhance.Brightness(img).enhance(factor) @pxt.udf def adjust_contrast(img: pxt.Image, factor: float) -> pxt.Image: """Adjust contrast. factor < 1 = lower, > 1 = higher.""" return ImageEnhance.Contrast(img).enhance(factor) @pxt.udf def adjust_saturation(img: pxt.Image, factor: float) -> pxt.Image: """Adjust saturation. factor < 1 = less saturated, > 1 = more saturated.""" return ImageEnhance.Color(img).enhance(factor) ``` ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Test brightness adjustments t.select( t.image, adjust_brightness(t.image, 0.5), adjust_brightness(t.image, 1.5), ).head(1) ```
### Add: adjust brightness and contrast for all images in your table ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Brightness adjustments (1.0 = original) t.add_computed_column(darker=adjust_brightness(t.image, 0.5)) t.add_computed_column(brighter=adjust_brightness(t.image, 1.5)) # Contrast adjustments t.add_computed_column(low_contrast=adjust_contrast(t.image, 0.5)) t.add_computed_column(high_contrast=adjust_contrast(t.image, 2.0)) # Color saturation t.add_computed_column(desaturated=adjust_saturation(t.image, 0.3)) t.add_computed_column(saturated=adjust_saturation(t.image, 2.0)) ```
  Added 3 column values with 0 errors.
  Added 3 column values with 0 errors.
  Added 3 column values with 0 errors.
  Added 3 column values with 0 errors.
  Added 3 column values with 0 errors.
  Added 3 column values with 0 errors.
  3 rows updated, 3 values computed.
### View results Compare different enhancement levels side-by-side. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Compare brightness levels t.select(t.image, t.darker, t.brighter).collect() ```
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Compare contrast levels t.select(t.image, t.low_contrast, t.high_contrast).collect() ```
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Compare saturation levels t.select(t.image, t.desaturated, t.saturated).collect() ```
## Explanation **How the enhancement technique works:** The UDFs wrap PIL’s `ImageEnhance` module to adjust visual properties of images. Each enhancement type creates an enhancer object for the image, then applies a multiplication factor. A factor of 1.0 leaves the image unchanged, values below 1.0 decrease the property (darker, less contrast, desaturated), and values above 1.0 increase it (brighter, more contrast, saturated). You can apply different factors to the same image to create multiple variations for comparison or different use cases. **To customize the UDFs:** * **Brightness factors**: Use 0.5 for darker images, 1.5 for brighter, or adjust to match your lighting needs * **Contrast factors**: Use 0.5 for lower contrast, 2.0 for higher contrast, or fine-tune for image clarity * **Saturation factors**: Use 0.3 for desaturated/muted colors, 2.0 for vibrant colors, or 0.0 for complete grayscale * **Combine adjustments**: Apply multiple enhancements to create complex transformations **The Pixeltable workflow:** In traditional databases, `.select()` just picks which columns to view. In Pixeltable, `.select()` also lets you compute new transformations on the fly—define new columns without storing them. This makes `.select()` perfect for testing transformations before you commit them. When you use `.select()`, you’re creating a query that doesn’t execute until you call `.collect()`. You must use `.collect()` to execute the query and return results—nothing is stored in your table. If you want to collect only the first few rows, use `.head(n)` instead of `.collect()` to test on a subset before processing your full dataset. Once satisfied, use `.add_computed_column()` with the same expression to persist results permanently. For more on this workflow, see [Get fast feedback on transformations](/howto/cookbooks/core/dev-iterative-workflow). ## See also * [Test transformations with fast feedback loops](/howto/cookbooks/core/dev-iterative-workflow) * [Apply image filters](/howto/cookbooks/images/img-apply-filters) * *Pillow techniques from [Real Python: Image Processing With the Python Pillow Library](https://realpython.com/image-processing-with-the-python-pillow-library/)* # Detect objects in images Source: https://docs.pixeltable.com/howto/cookbooks/images/img-detect-objects Open in Kaggle  Open in Colab  Download Notebook This documentation page is also available as an interactive notebook. You can launch the notebook in Kaggle or Colab, or download it for use with an IDE or local Jupyter installation, by clicking one of the above links. Automatically identify and locate objects in images using YOLOX object detection models. This notebook uses `pxt.create_table()` so you can run cells without a project. An app puts the same columns on a `TableModel` in `app.py` and creates the tables with `pxt schema update`. ## Problem You have images that need object detection—identifying what objects are present and where they’re located. Manual labeling is slow and expensive.
## Solution **What’s in this recipe:** * Detect objects using YOLOX models (runs locally, no API needed) * Get bounding boxes and class labels * Filter detections by confidence threshold You add a computed column that runs YOLOX on each image. Detection happens automatically when you insert new images. ### Setup ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} %pip install -qU pixeltable pixeltable-yolox ``` ### Load images ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} import pixeltable as pxt from pixeltable.functions.yolox import yolox # Create a fresh directory pxt.drop_dir('detection_demo', force=True) pxt.create_dir('detection_demo') ```
  Connected to Pixeltable database at: postgresql+psycopg://postgres:@/pixeltable?host=/Users/pjlb/.pixeltable/pgdata
  Created directory 'detection\_demo'.
  \
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Create table for images images = pxt.create_table('detection_demo/images', {'image': pxt.Image}) ```
  Created table 'images'.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Insert sample images (COCO dataset samples with common objects) image_urls = [ 'https://raw.githubusercontent.com/pixeltable/pixeltable/main/docs/resources/images/000000000036.jpg', 'https://raw.githubusercontent.com/pixeltable/pixeltable/main/docs/resources/images/000000000090.jpg', 'https://raw.githubusercontent.com/pixeltable/pixeltable/main/docs/resources/images/000000000106.jpg', ] images.insert([{'image': url} for url in image_urls]) ```
  Inserting rows into \`images\`: 0 rows \[00:00, ? rows/s]
  Inserting rows into \`images\`: 3 rows \[00:00, 523.85 rows/s]
  Inserted 3 rows with 0 errors.
  3 rows inserted, 6 values computed.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # View images images.collect() ```
### Run object detection Add a computed column that runs YOLOX on each image: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Run YOLOX object detection # model_id options: yolox_nano, yolox_tiny, yolox_s, yolox_m, yolox_l, yolox_x images.add_computed_column( detections=yolox(images.image, model_id='yolox_m', threshold=0.5) ) ```
  Added 3 column values with 0 errors.
  3 rows updated, 3 values computed.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # View detection results images.select(images.image, images.detections).collect() ```
### Extract detection details Parse the detection output to get object counts and classes: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Extract number of detections @pxt.udf def count_objects(detections: dict) -> int: """Count the number of detected objects.""" return len(detections.get('labels', [])) images.add_computed_column(object_count=count_objects(images.detections)) ```
  Added 3 column values with 0 errors.
  3 rows updated, 6 values computed.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Extract unique object classes @pxt.udf def get_classes(detections: dict) -> list: """Get list of detected object classes.""" return list(set(detections.get('labels', []))) images.add_computed_column(object_classes=get_classes(images.detections)) ```
  Added 3 column values with 0 errors.
  3 rows updated, 3 values computed.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # View summary images.select( images.image, images.object_count, images.object_classes ).collect() ```
## Explanation **YOLOX model sizes:**
**Detection output format:** The `detections` dictionary contains: * `labels`: List of class names (e.g., “person”, “car”, “dog”) * `boxes`: Bounding box coordinates \[x1, y1, x2, y2] * `scores`: Confidence scores (0-1) **Adjusting threshold:** * Higher threshold (0.7-0.9): Fewer detections, higher confidence * Lower threshold (0.3-0.5): More detections, may include false positives ## See also * [Extract frames from videos](/howto/cookbooks/video/video-extract-frames) - Detect objects in video frames * [Analyze images in batch](/howto/cookbooks/images/vision-batch-analysis) - AI vision analysis * [Find similar images](/howto/cookbooks/search/search-similar-images) - Visual similarity search # Compare object detection and panoptic segmentation Source: https://docs.pixeltable.com/howto/cookbooks/images/img-detection-vs-segmentation Open in Kaggle  Open in Colab  Download Notebook This documentation page is also available as an interactive notebook. You can launch the notebook in Kaggle or Colab, or download it for use with an IDE or local Jupyter installation, by clicking one of the above links. Understand when to use bounding boxes versus pixel-level masks for image analysis. **What’s in this recipe:** * Run object detection to get bounding boxes and labels * Run panoptic segmentation to get pixel-level masks * Visualize and compare outputs side-by-side ## Problem You need to analyze objects in images, but there are two approaches:
Which should you use? Detection is faster but approximate. Segmentation is slower but precise. ## Solution Run both approaches on the same images using DETR models and compare the results. ### Setup ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} %pip install -qU pixeltable torch transformers timm ``` ### Load images ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} import numpy as np import pixeltable as pxt from pixeltable.functions.huggingface import ( detr_for_object_detection, detr_for_segmentation, ) from pixeltable.functions.vision import bboxes_draw, overlay_segmentation pxt.drop_dir('detection_vs_seg', force=True) pxt.create_dir('detection_vs_seg') ```
  Connected to Pixeltable database at: postgresql+psycopg://postgres:@/pixeltable?host=/Users/pjlb/.pixeltable/pgdata
  Created directory 'detection\_vs\_seg'.
  \
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} images = pxt.create_table('detection_vs_seg/images', {'image': pxt.Image}) base_url = 'https://raw.githubusercontent.com/pixeltable/pixeltable/main/docs/resources/images' images.insert( [ {'image': f'{base_url}/000000000034.jpg'}, {'image': f'{base_url}/000000000049.jpg'}, ] ) ```
  Created table 'images'.
  Inserted 2 rows with 0 errors in 0.22 s (9.21 rows/s)
  2 rows inserted.
### Run object detection The `detr_for_object_detection` function returns bounding boxes, labels, and confidence scores. **Parameters:** * `model_id`: DETR variant (`facebook/detr-resnet-50` or `facebook/detr-resnet-101`) * `threshold`: Confidence threshold (0.0-1.0). Higher = fewer but more confident detections **Output:** ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} {'boxes': [[x1, y1, x2, y2], ...], 'scores': [0.98, ...], 'label_text': ['person', ...]} ``` ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} images.add_computed_column( detections=detr_for_object_detection( images.image, model_id='facebook/detr-resnet-50', threshold=0.8 ) ) ```
  Added 2 column values with 0 errors in 4.09 s (0.49 rows/s)
  2 rows updated.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # View detection results images.select(images.image, images.detections).collect() ```
### Visualize detections with bounding boxes Use `bboxes_draw` to overlay the detection results on the original image. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} images.add_computed_column( detection_viz=bboxes_draw( images.image, boxes=images.detections.boxes, labels=images.detections.label_text, fill=True, width=2, ) ) ```
  Added 2 column values with 0 errors in 0.03 s (58.89 rows/s)
  2 rows updated.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} images.select(images.detection_viz).collect() ```
### Run panoptic segmentation The `detr_for_segmentation` function returns pixel-level masks and segment metadata. **Parameters:** * `model_id`: Segmentation model (`facebook/detr-resnet-50-panoptic`) * `threshold`: Confidence threshold for filtering segments **Output:** ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} { 'segmentation': np.ndarray, # (H, W) array where each pixel = segment ID 'segments_info': [{'id': 1, 'label_text': 'person', 'score': 0.98}, ...] } ``` > **Note:** The full segmentation output contains a numpy array that > can’t be stored as JSON. We store just the `segments_info` metadata > and compute the pixel-level visualization inline. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Store just the segments_info (JSON-serializable) as a computed column # The segmentation array will be computed inline for visualization seg_expr = detr_for_segmentation( images.image, model_id='facebook/detr-resnet-50-panoptic', threshold=0.5, ) images.add_computed_column(segments_info=seg_expr.segments_info) ``` ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # View stored segmentation info images.select(images.image, images.segments_info).collect() ```
### Visualize segmentation with colored overlay Use `overlay_segmentation` to visualize the pixel masks with colored regions and contours. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Compute segmentation visualization inline # Cast the segmentation array to the proper type for overlay_segmentation seg_expr = detr_for_segmentation( images.image, model_id='facebook/detr-resnet-50-panoptic', threshold=0.5, ) segmentation_map = seg_expr.segmentation.astype( pxt.Array[(None, None), np.int32] ) images.select( segmentation_viz=overlay_segmentation( images.image, segmentation_map, alpha=0.5, draw_contours=True, contour_thickness=2, ) ).collect() ```
### Compare side-by-side ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Side-by-side comparison: original, detection, segmentation seg_expr = detr_for_segmentation( images.image, model_id='facebook/detr-resnet-50-panoptic', threshold=0.5, ) segmentation_map = seg_expr.segmentation.astype( pxt.Array[(None, None), np.int32] ) images.select( images.image, images.detection_viz, segmentation_viz=overlay_segmentation( images.image, segmentation_map, alpha=0.5, draw_contours=True, contour_thickness=2, ), ).collect() ```
### Count objects per image ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Count objects per image (using stored columns) images.select( images.image, num_detections=images.detections.boxes.apply(len, col_type=pxt.Int), num_segments=images.segments_info.apply(len, col_type=pxt.Int), ).collect() ```
## Explanation Detection gives fast, approximate locations. Segmentation gives slower but precise boundaries. ### Capability comparison
### Performance tradeoffs
### When to use each **Choose detection when:** * You need to know *what* objects are present and *where* (approximately) * Speed matters (detection is 2x faster) * You need search, filtering, or counting * Bounding boxes suffice for visualization **Choose segmentation when:** * You need *exact* object boundaries (pixel-perfect masks) * You’re doing image editing, compositing, or AR * You need to measure actual object area/coverage * You want scene composition analysis (what % is sky vs buildings) ## See also * [Detect objects in images](./img-detect-objects) - Object detection with YOLOX * [Visualize detections](./img-visualize-detections) - Draw bounding boxes and labels * [DETR documentation](https://huggingface.co/docs/transformers/model_doc/detr) - Hugging Face model docs # Generate captions for images Source: https://docs.pixeltable.com/howto/cookbooks/images/img-generate-captions Open in Kaggle  Open in Colab  Download Notebook This documentation page is also available as an interactive notebook. You can launch the notebook in Kaggle or Colab, or download it for use with an IDE or local Jupyter installation, by clicking one of the above links. Automatically create descriptive captions for images using AI vision models. ## Problem You have a collection of images that need captions—for accessibility, SEO, content management, or searchability. Writing captions manually doesn’t scale.
## Solution **What’s in this recipe:** * Generate captions using OpenAI’s vision models * Customize caption style (short, detailed, SEO-focused) * Process images in batch automatically You add a computed column that sends each image to a vision model with a captioning prompt. New images are captioned automatically on insert. ### Setup ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} %pip install -qU pixeltable openai ``` ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} import getpass import os if 'OPENAI_API_KEY' not in os.environ: os.environ['OPENAI_API_KEY'] = getpass.getpass('OpenAI API Key: ') ``` ### Load images ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} import pixeltable as pxt from pixeltable.functions.openai import chat_completions # Create a fresh directory pxt.drop_dir('caption_demo', force=True) pxt.create_dir('caption_demo') ```
  Connected to Pixeltable database at: postgresql+psycopg://postgres:@/pixeltable?host=/Users/asiegel/.pixeltable/pgdata
  Created directory 'caption\_demo'.
  \
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Create table for images images = pxt.create_table('caption_demo/images', {'image': pxt.Image}) ```
  Created table 'images'.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Insert sample images image_urls = [ 'https://raw.githubusercontent.com/pixeltable/pixeltable/main/docs/resources/images/000000000036.jpg', 'https://raw.githubusercontent.com/pixeltable/pixeltable/main/docs/resources/images/000000000090.jpg', 'https://raw.githubusercontent.com/pixeltable/pixeltable/main/docs/resources/images/000000000106.jpg', ] images.insert([{'image': url} for url in image_urls]) ```
  Inserted 3 rows with 0 errors in 0.12 s (25.17 rows/s)
  3 rows inserted.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # View images images.collect() ```
### Generate captions Add a computed column that generates captions using the vision model: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Add caption using OpenAI vision messages = [ { 'role': 'user', 'content': [ { 'type': 'text', 'text': 'Write a concise, descriptive caption for this image in one sentence.', }, {'type': 'image_url', 'image_url': images.image}, ], } ] images.add_computed_column( caption=chat_completions(messages, model='gpt-4o-mini') ) ```
  Added 3 column values with 0 errors in 4.62 s (0.65 rows/s)
  3 rows updated.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # View images with captions images.select( images.image, images.caption['choices'][0]['message']['content'] ).collect() ```
### Different caption styles You can generate multiple caption styles for different uses: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Add alt text for accessibility (brief) messages = [ { 'role': 'user', 'content': [ { 'type': 'text', 'text': 'Write a brief alt text for this image (under 125 characters) for screen readers.', }, {'type': 'image_url', 'image_url': images.image}, ], } ] images.add_computed_column( alt_text=chat_completions(messages, model='gpt-4o-mini') ) ```
  Added 3 column values with 0 errors in 3.51 s (0.85 rows/s)
  3 rows updated.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Add detailed description messages = [ { 'role': 'user', 'content': [ { 'type': 'text', 'text': 'Describe this image in detail, including objects, colors, setting, and mood.', }, {'type': 'image_url', 'image_url': images.image}, ], } ] images.add_computed_column( description=chat_completions(messages, model='gpt-4o-mini') ) ```
  Added 3 column values with 0 errors in 11.28 s (0.27 rows/s)
  3 rows updated.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # View all caption types images.select( images.image, images.caption['choices'][0]['message']['content'], images.alt_text['choices'][0]['message']['content'], images.description['choices'][0]['message']['content'], ).collect() ```
## Explanation **Caption prompt patterns:**
**Model selection:** * `gpt-4o-mini`: Fast and affordable, good for most captioning tasks * `gpt-4o`: Higher quality for complex images or detailed descriptions ## See also * [Analyze images in batch](/howto/cookbooks/images/vision-batch-analysis) - Run custom prompts on images * [Extract structured data from images](/howto/cookbooks/images/vision-structured-output) - Get JSON from images * [Find similar images](/howto/cookbooks/search/search-similar-images) - Visual similarity search # Transform images with AI-powered editing Source: https://docs.pixeltable.com/howto/cookbooks/images/img-image-to-image Open in Kaggle  Open in Colab  Download Notebook This documentation page is also available as an interactive notebook. You can launch the notebook in Kaggle or Colab, or download it for use with an IDE or local Jupyter installation, by clicking one of the above links. ## Problem You have a batch of images that need AI-powered transformations—like turning photos into paintings, adding stylistic effects, or modifying content based on text prompts.
## Solution **What’s in this recipe:** * Transform images using text prompts with Hugging Face Stable Diffusion models * Control transformation strength and quality settings * Process batches of images automatically You can iterate on transformations before adding them to your table. Use `.select()` with `.collect()` to preview results on sample images—nothing is stored in your table. If you want to collect only the first few rows, use `.head(n)` instead of `.collect()`. Once you’re satisfied, use `.add_computed_column()` to apply the transformation to all images in your table. For more on this workflow, see [Get fast feedback on transformations](/howto/cookbooks/core/dev-iterative-workflow). ### Setup ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} %pip install -qU pixeltable torch transformers diffusers accelerate ``` ### Load images ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} import pixeltable as pxt from pixeltable.functions.huggingface import image_to_image # Create a fresh directory (drop existing if present) pxt.drop_dir('img2img_demo', force=True) pxt.create_dir('img2img_demo') ```
  Connected to Pixeltable database at: postgresql+psycopg://postgres:@/pixeltable?host=/Users/cpestano/.pixeltable/pgdata
  Created directory 'img2img\_demo'.
  \
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} t = pxt.create_table( 'img2img_demo/images', { 'image': pxt.Image, 'prompt': pxt.String, 'negative_prompt': pxt.String, }, ) ```
  Created table 'images'.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} t.insert( [ { 'image': 'https://raw.githubusercontent.com/pixeltable/pixeltable/main/docs/resources/images/000000000285.jpg', 'prompt': 'oil painting style, vibrant colors, brushstrokes visible', 'negative_prompt': 'blurry, low quality, bad anatomy', }, { 'image': 'https://raw.githubusercontent.com/pixeltable/pixeltable/main/docs/resources/images/000000000776.jpg', 'prompt': 'watercolor painting, soft edges, artistic', 'negative_prompt': 'blurry, low quality, bad anatomy', }, ] ) ```
  Inserted 2 rows with 0 errors in 0.49 s (4.07 rows/s)
  2 rows inserted.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # View original images and prompts t.collect() ```
### Iterate: test transformation on a single image Use `.select()` to define the transformation, then `.head(n)` to preview results on a subset of images. Nothing is stored in your table. The `image_to_image` function requires: * `image`: The source image to transform * `prompt`: Text describing the desired output * `model_id`: A Hugging Face model ID that supports image-to-image (e.g., `stable-diffusion-v1-5/stable-diffusion-v1-5`) ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Preview transformation on first image t.select( t.image, t.prompt, image_to_image( t.image, t.prompt, model_id='stable-diffusion-v1-5/stable-diffusion-v1-5', ), ).head(1) ```
### Iterate: adjust transformation strength You control how much the model modifies the original image using `strength` (0.0-1.0): * **Lower values** (0.3-0.5): Subtle changes, preserves more of the original * **Higher values** (0.7-1.0): Dramatic changes, more creative freedom You pass additional parameters through `model_kwargs`. For example, `negative_prompt` text describing what you don’t want the output to be. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Preview with lower strength (more preservation of original) t.select( t.image, t.prompt, t.negative_prompt, image_to_image( t.image, t.prompt, model_id='stable-diffusion-v1-5/stable-diffusion-v1-5', model_kwargs={ 'negative_prompt': t.negative_prompt, 'strength': 0.5, 'num_inference_steps': 30, }, ), ).head(1) ```
### Add: apply transformation to all images Once you’re satisfied with the results, use `.add_computed_column()` with the same expression. This processes all rows and stores the results permanently in your table. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Save as computed column t.add_computed_column( transformed=image_to_image( t.image, t.prompt, model_id='stable-diffusion-v1-5/stable-diffusion-v1-5', model_kwargs={ 'strength': 0.5, 'num_inference_steps': 25, 'negative_prompt': t.negative_prompt, }, ) ) ```
  Added 2 column values with 0 errors in 53.83 s (0.04 rows/s)
  2 rows updated.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # View original and transformed images side by side t.select(t.image, t.prompt, t.negative_prompt, t.transformed).collect() ```
### Use reproducible results with seeds You set a `seed` parameter to get the same output every time you run the transformation. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Add reproducible transformation t.add_computed_column( transformed_seeded=image_to_image( t.image, t.prompt, model_id='stable-diffusion-v1-5/stable-diffusion-v1-5', seed=42, model_kwargs={ 'strength': 0.5, 'negative_prompt': t.negative_prompt, }, ) ) ```
  Added 2 column values with 0 errors in 96.24 s (0.02 rows/s)
  2 rows updated.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # View results t.select(t.image, t.transformed_seeded).collect() ```
## Explanation **How image-to-image works:** Image-to-image diffusion models take an existing image and a text prompt, then generate a new image that blends the structure of the original with the guidance from the prompt. The `strength` parameter controls the balance—lower values preserve more of the original, while higher values allow more dramatic transformations. **Model compatibility:** The `image_to_image` UDF uses `AutoPipelineForImage2Image` from the diffusers library, which automatically detects the model type and selects the appropriate pipeline. You use any compatible model: * `stable-diffusion-v1-5/stable-diffusion-v1-5` - General-purpose, runs on most hardware * `stabilityai/stable-diffusion-xl-base-1.0` - Higher quality, needs more VRAM **Key parameters:** * `strength` (0.0-1.0): How much to transform the image * `negative_prompt`: Text describing what to avoid in the generated image (e.g., “blurry, low quality”). * `num_inference_steps`: Quality vs speed tradeoff (more steps = better quality) * `guidance_scale`: How closely to follow the prompt (7-8 is typical) * `seed`: For reproducible results ## See also * [Apply filters to images](/howto/cookbooks/images/img-apply-filters) * [Generate captions for images](/howto/cookbooks/images/img-generate-captions) * [Hugging Face image-to-image models](https://huggingface.co/models?pipeline_tag=image-to-image) # Transform images with PIL operations Source: https://docs.pixeltable.com/howto/cookbooks/images/img-pil-transforms Open in Kaggle  Open in Colab  Download Notebook This documentation page is also available as an interactive notebook. You can launch the notebook in Kaggle or Colab, or download it for use with an IDE or local Jupyter installation, by clicking one of the above links. ## Problem You need to resize, rotate, crop, or convert hundreds of images—and keep track of all the transformed versions. ## Solution **What’s in this recipe:** * Basic image operations (resize, rotate, flip, crop) * Track image properties * Iterate on transformations before adding to your table You apply PIL transformations (resize, rotate, flip, crop) to images in your table using Pixeltable’s built-in image functions—common operations that work directly on image columns. You can iterate on transformations before adding them to your table. Use `.select()` with `.collect()` to preview results on sample images—nothing is stored in your table. If you want to collect only the first few rows, use `.head(n)` instead of `.collect()`. Once you’re satisfied, use `.add_computed_column()` to apply the transformation to all images in your table. For more on this workflow, see [Get fast feedback on transformations](/howto/cookbooks/core/dev-iterative-workflow). ### Setup ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} %pip install -qU pixeltable ``` ### Load images ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} import pixeltable as pxt # Create a fresh directory (drop existing if present) pxt.drop_dir('image_demo', force=True) pxt.create_dir('image_demo') ```
  Connected to Pixeltable database at: postgresql+psycopg://postgres:@/pixeltable?host=/Users/pjlb/.pixeltable/pgdata
  Created directory 'image\_demo'.
  \
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} t = pxt.create_table('image_demo/images', {'image': pxt.Image}) ```
  Created table 'images'.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} t.insert( [ { 'image': 'https://raw.githubusercontent.com/pixeltable/pixeltable/main/docs/resources/images/000000000285.jpg' }, { 'image': 'https://raw.githubusercontent.com/pixeltable/pixeltable/main/docs/resources/images/000000000776.jpg' }, { 'image': 'https://raw.githubusercontent.com/pixeltable/pixeltable/main/docs/resources/images/000000000885.jpg' }, ] ) ```
  Inserting rows into \`images\`: 0 rows \[00:00, ? rows/s]
  Inserting rows into \`images\`: 3 rows \[00:00, 708.38 rows/s]
  Inserted 3 rows with 0 errors.
  3 rows inserted, 6 values computed.
### Iterate: check image properties for a few images first Use `.select()` to define the transformation, then `.collect()` to execute and return results. If you want to collect only the first few rows, use `.head(n)` instead of `.collect()`. Nothing is stored in your table. Pixeltable includes these built-in functions for image properties: * `.height` - Get image height in pixels * `.width` - Get image width in pixels * `.mode` - Get color mode (RGB, RGBA, L for grayscale, etc.) ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Preview the properties t.select(t.image, t.image.height, t.image.width, t.image.mode).collect() ```
### Add: check image properties for all images in your table ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Save as computed columns t.add_computed_column(height=t.image.height) t.add_computed_column(width=t.image.width) t.add_computed_column(mode=t.image.mode) # RGB, RGBA, L (grayscale), etc. ```
  Added 3 column values with 0 errors.
  Added 3 column values with 0 errors.
  Added 3 column values with 0 errors.
  3 rows updated, 6 values computed.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # View images with computed height, width, and mode columns t.collect() ```
### Iterate: resize a few images first Use `.select()` to define the transformation, then `.collect()` to execute and return results. If you want to collect only the first few rows, use `.head(n)` instead of `.collect()`. Nothing is stored in your table. Pixeltable includes a built-in function for resizing image files with PIL: * `.resize(width, height)` - Change image dimensions ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Preview the resize operation t.select(t.image, t.image.resize((224, 224))).head(1) ```
### Add: resize all images in your table Once you’re satisfied with the results, use `.add_computed_column()` with the same expression. This processes all rows and stores the results permanently in your table. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Save as computed column t.add_computed_column(resized=t.image.resize((224, 224))) ```
  Added 3 column values with 0 errors.
  3 rows updated, 3 values computed.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # View images with resized column t.collect() ```
### Iterate: rotate a few images first Use `.select()` to define the transformation, then `.collect()` to execute and return results. If you want to collect only the first few rows, use `.head(n)` instead of `.collect()`. Nothing is stored in your table. Pixeltable includes a built-in function for rotating image files with PIL: * `.rotate(degrees)` - Rotate image by specified degrees ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Preview the rotation t.select(t.image, t.image.rotate(90)).head(1) ```
### Add: rotate all images in your table Once you’re satisfied with the results, use `.add_computed_column()` with the same expression. This processes all rows and stores the results permanently in your table. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Save as computed column t.add_computed_column(rotated=t.image.rotate(90)) ```
  Added 3 column values with 0 errors.
  3 rows updated, 3 values computed.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # View images with rotated column t.collect() ```
### Iterate: flip a few images first Use `.select()` to define the transformation, then `.collect()` to execute and return results. If you want to collect only the first few rows, use `.head(n)` instead of `.collect()`. Nothing is stored in your table. Pixeltable includes a built-in function for transposing image files with PIL (note that for this transform you will need import PIL to access the `FLIP_*` constants): * `.transpose(Image.FLIP_TOP_BOTTOM)` - Flip image vertically * `.transpose(Image.FLIP_LEFT_RIGHT)` - Mirror image horizontally ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Import PIL Image to access flip constants from PIL import Image # Preview both flip operations t.select( t.image, t.image.transpose(Image.FLIP_TOP_BOTTOM), t.image.transpose(Image.FLIP_LEFT_RIGHT), ).head(1) ```
### Add: flip all images in your table Once you’re satisfied with the results, use `.add_computed_column()` with the same expression. This processes all rows and stores the results permanently in your table. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Flip vertically (top to bottom) t.add_computed_column(flip_v=t.image.transpose(Image.FLIP_TOP_BOTTOM)) # Flip horizontally (left to right, mirror effect) t.add_computed_column(flip_h=t.image.transpose(Image.FLIP_LEFT_RIGHT)) ```
  Added 3 column values with 0 errors.
  Added 3 column values with 0 errors.
  3 rows updated, 3 values computed.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # View original and flipped versions side by side t.select(t.image, t.flip_v, t.flip_h).collect() ```
### Iterate: crop a few images first Use `.select()` to define the transformation, then `.collect()` to execute and return results. If you want to collect only the first few rows, use `.head(n)` instead of `.collect()`. Nothing is stored in your table. Pixeltable includes a built-in function for cropping image files with PIL: * `.crop(box)` - Extract a rectangular region from the image (box format: `(left, top, right, bottom)`) ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Preview the center crop # Box format: (left, top, right, bottom) t.select( t.image, t.image.crop( ( t.image.width // 4, t.image.height // 4, 3 * t.image.width // 4, 3 * t.image.height // 4, ) ), ).head(1) ```
### Add: crop all images in your table Once you’re satisfied with the results, use `.add_computed_column()` with the same expression. This processes all rows and stores the results permanently in your table. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Save as computed column t.add_computed_column( center_crop=t.image.crop( ( t.image.width // 4, t.image.height // 4, 3 * t.image.width // 4, 3 * t.image.height // 4, ) ) ) ```
  Added 3 column values with 0 errors.
  3 rows updated, 3 values computed.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # View center-cropped images t.select(t.center_crop).collect() ```
## Explanation **How PIL transformations work in Pixeltable:** Pixeltable provides built-in functions that wrap PIL (Pillow) operations for image manipulation. These functions work directly on image columns in your table—no need to write loops or manage file I/O. When you call `.resize()`, `.rotate()`, or other methods on an image column, Pixeltable handles applying the transformation to each image automatically. All these transformations use standard PIL operations under the hood. For more details on PIL functionality, see the [Pillow documentation](https://pillow.readthedocs.io/). **To customize transformations:** * **Resize**: Change dimensions with `.resize((width, height))` - specify target size in pixels * **Rotate**: Rotate counterclockwise with `.rotate(degrees)` - use negative values for clockwise rotation * **Flip**: Use `.transpose(Image.FLIP_LEFT_RIGHT)` for horizontal mirror or `.transpose(Image.FLIP_TOP_BOTTOM)` for vertical flip * **Crop**: Extract regions with `.crop((left, top, right, bottom))` - coordinates are in pixels from top-left origin * **Properties**: Access `.width`, `.height`, and `.mode` to get image dimensions and color mode **The Pixeltable workflow:** In traditional databases, `.select()` just picks which columns to view. In Pixeltable, `.select()` also lets you compute new transformations on the fly—define new columns without storing them. This makes `.select()` perfect for testing transformations before you commit them. When you use `.select()`, you’re creating a query that doesn’t execute until you call `.collect()`. You must use `.collect()` to execute the query and return results—nothing is stored in your table. If you want to collect only the first few rows, use `.head(n)` instead of `.collect()` to test on a subset before processing your full dataset. Once satisfied, use `.add_computed_column()` with the same expression to persist results permanently. For more on this workflow, see [Get fast feedback on transformations](/howto/cookbooks/core/dev-iterative-workflow). ## See also * [Convert RGB images to grayscale](/howto/cookbooks/images/img-rgb-to-grayscale) * [Apply filters to images](/howto/cookbooks/images/img-apply-filters) * [Test transformations with fast feedback loops](/howto/cookbooks/core/dev-iterative-workflow) # Promptable segmentation with SAM 3 Source: https://docs.pixeltable.com/howto/cookbooks/images/img-promptable-segmentation Open in Kaggle  Open in Colab  Download Notebook This documentation page is also available as an interactive notebook. You can launch the notebook in Kaggle or Colab, or download it for use with an IDE or local Jupyter installation, by clicking one of the above links. Segment every object matching a concept (a short text phrase or a bounding box) using Meta’s [Segment Anything Model 3](https://huggingface.co/facebook/sam3). **What’s in this recipe:** * Segment a single object from a text prompt * Segment many instances of a concept at once * Segment a specific region with a bounding box * Segment objects across the frames of a video * Apply SAM 3 to three customer use cases: satellite imagery, medical scans, and solar inspection ## Problem Classical segmentation models output a fixed set of class labels. SAM 3 instead performs *promptable concept segmentation*: you describe what you want with a short noun phrase (“zebra”, “car”) or a bounding box, and the model returns one instance mask for every matching object.
## Solution Use the [`sam3_for_segmentation()`](/sdk/latest/huggingface#udf-sam3_for_segmentation) UDF in [`pixeltable.functions.huggingface`](/sdk/latest/huggingface). It returns a typed result with one entry per detected instance: a confidence `score`, a bounding `box`, and a binary `mask`. The [`overlay_segmentation()`](/sdk/latest/vision#udf-overlay_segmentation) vision UDF accepts those masks directly, so you can visualize results without any glue code. > **Note:** `facebook/sam3` is a gated model. Request access on its > [model page](https://huggingface.co/facebook/sam3), then authenticate > with `huggingface-cli login` (or set the `HF_TOKEN` environment > variable) before running this notebook. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} %pip install -qU pixeltable torch torchvision transformers ``` ### Set up a table of images We load five images: a grazing zebra, a bento box full of food, an aerial view of the Davis-Monthan AMARG aircraft boneyard, a chest X-ray, and a solar array. A Pixeltable table stores the originals; every example below computes its segmentation on the fly with `select`, so nothing extra is persisted. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} import pixeltable as pxt from pixeltable.functions.huggingface import ( sam3_for_segmentation, sam_automatic_mask_generation, ) from pixeltable.functions.vision import bboxes_draw, overlay_segmentation pxt.drop_dir('sam3_demo', force=True) pxt.create_dir('sam3_demo') base_url = 'https://raw.githubusercontent.com/pixeltable/pixeltable/main/docs/resources/images' images = pxt.create_table( 'sam3_demo/images', {'name': pxt.String, 'image': pxt.Image} ) images.insert( [ {'name': 'zebra', 'image': f'{base_url}/000000000034.jpg'}, {'name': 'bento', 'image': f'{base_url}/000000000009.jpg'}, {'name': 'boneyard', 'image': f'{base_url}/amarg-boneyard.jpg'}, {'name': 'chest_xray', 'image': f'{base_url}/chest-xray.jpg'}, {'name': 'solar_farm', 'image': f'{base_url}/solar-farm.jpg'}, ] ) ```
  Connected to Pixeltable database at: postgresql+psycopg://postgres:@/pixeltable?host=/Users/cpestano/.pixeltable/pgdata
  Created directory 'sam3\_demo'.
  Created table 'images'.
  Inserted 5 rows with 0 errors in 0.84 s (5.98 rows/s)
  5 rows inserted.
### Segment from a text prompt Pass a short noun phrase as `text`. SAM 3 returns one binary mask per matching instance. Here we segment the zebra and overlay the mask on the original image with [`overlay_segmentation()`](/sdk/latest/vision#udf-overlay_segmentation). ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} images.select( images.image, segmented=overlay_segmentation( images.image, sam3_for_segmentation(images.image, text='zebra').masks, draw_contours=True, ), ).where(images.name == 'zebra').collect() ```
### Segment many instances automatically A single call to [`sam_automatic_mask_generation()`](/sdk/latest/huggingface#udf-sam_automatic_mask_generation) segments every object it detects in the image and returns unlabeled masks. He we segment a bento box of fruit and overlay the masks on the image. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} images.select( segmented=overlay_segmentation( images.image, sam_automatic_mask_generation(images.image).masks, draw_contours=True, ) ).where(images.name == 'bento').collect() ```
### Segment a region with a bounding box Skip the text prompt and pass a bounding box in `[x1, y1, x2, y2]` pixel coordinates to segment whatever concept that region contains. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} images.select( images.image, segmented=overlay_segmentation( images.image, sam3_for_segmentation( images.image, input_boxes=[[10, 30, 470, 300]] ).masks, draw_contours=True, ), ).where(images.name == 'zebra').collect() ```
## Segment objects across a video Segmentation works on video frames too. We split the first three seconds of a Bangkok traffic clip into frames at the video’s native frame rate with [`frame_iterator()`](/sdk/latest/video#iterator-frame_iterator), segment every `car`, overlay the masks, and reassemble the result with the [`make_video()`](/sdk/latest/video#uda-make_video) aggregator. Extracting and rebuilding at the same frame rate keeps the output at the original tempo. SAM segments each frame independently and does not track objects across frames, so the per-instance colors would otherwise flicker from frame to frame. To keep the overlay stable, we merge every car mask in a frame into a single region, so all cars share one consistent color. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} from pixeltable.functions.huggingface import sam3_for_video_segmentation from pixeltable.functions.video import clip, make_video pxt.drop_dir('sam3_video', force=True) pxt.create_dir('sam3_video') bangkok = 'https://raw.githubusercontent.com/pixeltable/pixeltable/main/docs/resources/bangkok.mp4' videos = pxt.create_table('sam3_video/videos', {'video': pxt.Video}) videos.add_computed_column( clipped_video=clip(videos.video, start_time=0, end_time=2) ) videos.insert([{'video': bangkok}]) frames = pxt.create_view( 'sam3_video/frames', videos, iterator=sam3_for_video_segmentation( videos.clipped_video, text=['car', 'motorcycle'], fps=5 ), ) ```
  Created directory 'sam3\_video'.
  Created table 'videos'.
  Added 0 column values with 0 errors in 0.00 s
  Inserted 1 row with 0 errors in 1.88 s (0.53 rows/s)
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # bangkok.mp4 plays at 5 fps; rebuild at the same rate to keep the tempo. # Note: You can improve the final frame's quality by padding the end then trimming it off # this is because SAMv3 looks ahead a few frames for object tracking frames.group_by(videos).select( segmented_video=make_video( frames.pos, bboxes_draw( img=overlay_segmentation( frames.frame, frames.masks, draw_contours=True ), boxes=frames.boxes.to_list(), labels=frames.labels.to_list(), ), fps=5, ) ).collect() ```
## Defense and intelligence: count aircraft in a satellite image Geospatial imagery often shows hundreds of vehicles, vessels, or aircraft in a single frame, and the assets of interest change from mission to mission. SAM 3’s open-vocabulary text prompt picks every instance out of the scene in a single pass; the `scores` column lets you threshold by confidence before counting or feeding the masks into a downstream tracker. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} from pixeltable.functions.image import stitch_tiles, tile_iterator tiles = pxt.create_view( 'sam3_demo/tiles', images.where(images.name == 'boneyard'), iterator=tile_iterator( images.image, tile_size=(1006, 1006), overlap=(45, 45) ), ) tiles.add_computed_column( segmented_tile=overlay_segmentation( tiles.tile, sam3_for_segmentation(tiles.tile, text='aircraft').masks, draw_contours=True, ) ) tiles.group_by(tiles.image).select( stitched=stitch_tiles( tiles.pos, tiles.segmented_tile, tiles.tile_box, tiles.image.width, tiles.image.height, ) ).collect() ```
  Added 15 column values with 0 errors in 34.06 s (0.44 rows/s)
## Medical imaging: segment the lungs in a chest X-ray SAM 3 is trained on natural images, so grayscale modalities like X-ray, CT, and MRI sit outside its training distribution and a free-form text prompt is less reliable than on RGB scenes. A high-contrast structure like the lungs in a PA chest X-ray is usually within reach. For anatomy SAM 3 doesn’t pick up zero-shot, a bounding box around the region of interest is the radiologist’s fallback and produces a clean mask without any text prompt. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} images.select( images.image, segmented=overlay_segmentation( images.image, sam3_for_segmentation(images.image, text='lung').masks, draw_contours=True, ), ).where(images.name == 'chest_xray').collect() ```
## Energy infrastructure: inspect a solar array Drone and aerial inspection of utility-scale solar, wind, and transmission assets is dominated by one bottleneck: finding every instance of the asset in a frame before any defect classifier runs. A single concept prompt returns per-panel masks that can be persisted in a computed column and chained into downstream UDFs (thermal-anomaly detection, soiling estimation, and so on). ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} images.select( images.image, segmented=overlay_segmentation( images.image, sam3_for_segmentation(images.image, text='solar panel').masks, draw_contours=True, ), ).where(images.name == 'solar_farm').collect() ```
## Explanation SAM 3 differs from earlier segmentation models in two ways: 1. **Concept-based prompting.** Instead of choosing from a fixed taxonomy, you describe the concept with free-form text, bounding boxes, or both. 2. **Instance masks for every match.** One forward pass returns a mask, score, and box for each matching instance. Because [`sam3_for_segmentation()`](/sdk/latest/huggingface#udf-sam3_for_segmentation) is an ordinary UDF, you can store its result in a computed column to segment new rows automatically, or compute it on the fly with `select` as we did here. [`overlay_segmentation()`](/sdk/latest/vision#udf-overlay_segmentation) consumes the per-instance mask stack directly, so visualizing results never requires reshaping arrays by hand. ## See also * [Compare object detection and panoptic segmentation](./img-detection-vs-segmentation) * [Detect objects in images](./img-detect-objects) * [SAM 3 documentation](https://huggingface.co/docs/transformers/model_doc/sam3) # Convert color images to grayscale Source: https://docs.pixeltable.com/howto/cookbooks/images/img-rgb-to-grayscale Open in Kaggle  Open in Colab  Download Notebook This documentation page is also available as an interactive notebook. You can launch the notebook in Kaggle or Colab, or download it for use with an IDE or local Jupyter installation, by clicking one of the above links. ## Problem You need to convert color images to grayscale for analysis, preprocessing, or model inputs that require single-channel images. Different conversion methods produce different results—you need to choose the right approach for your use case. ## Solution **What’s in this recipe:** * Simple conversion with PIL * Perceptually accurate grayscale (weighted RGB channels) * Custom UDF for advanced conversion You convert RGB images to grayscale in your table using either Pixeltable’s built-in `.convert()` method for standard conversion, or a custom UDF (relies on NumPy and PIL/Pillow) for gamma-corrected conversion when scientific accuracy matters. You can iterate on transformations before adding them to your table. Use `.select()` with `.collect()` to preview results on sample images—nothing is stored in your table. If you want to collect only the first few rows, use `.head(n)` instead of `.collect()`. Once you’re satisfied, use `.add_computed_column()` to apply the conversion to all images in your table. For more on this workflow, see [Get fast feedback on transformations](/howto/cookbooks/core/dev-iterative-workflow). **Conversion methods:**
The simple method uses PIL’s built-in conversion. The gamma-corrected method requires a custom UDF (not built into PIL) that applies perceptual weighting in linear color space. *For technical details on gamma correction and grayscale conversion, see [Wikipedia: Grayscale](https://en.wikipedia.org/wiki/Grayscale).* ### Setup ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} %pip install -qU pixeltable numpy ``` ### Load images ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} import numpy as np import pixeltable as pxt from PIL import Image # Create a fresh directory (drop existing if present) pxt.drop_dir('image_demo', force=True) pxt.create_dir('image_demo') ```
  Connected to Pixeltable database at: postgresql+psycopg://postgres:@/pixeltable?host=/Users/pjlb/.pixeltable/pgdata
  Created directory 'image\_demo'.
  \
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} t = pxt.create_table('image_demo/gray', {'image': pxt.Image}) ```
  Created table 'gray'.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} t.insert( [ { 'image': 'https://raw.githubusercontent.com/pixeltable/pixeltable/main/docs/resources/images/000000000036.jpg' }, { 'image': 'https://raw.githubusercontent.com/pixeltable/pixeltable/main/docs/resources/images/000000000090.jpg' }, { 'image': 'https://raw.githubusercontent.com/pixeltable/pixeltable/main/docs/resources/images/000000000106.jpg' }, ] ) ```
  Inserting rows into \`gray\`: 0 rows \[00:00, ? rows/s]
  Inserting rows into \`gray\`: 3 rows \[00:00, 617.66 rows/s]
  Inserted 3 rows with 0 errors.
  3 rows inserted, 6 values computed.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # View loaded images t.collect() ```
### Iterate: convert with linear approximation for a few images first ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Query: Preview the conversion t.select(t.image, t.image.convert('L')).head(1) ```
### Add: convert with linear approximation for all images in your table ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Commit: Save as computed column (built-in PIL conversion - fast and good for most use cases) t.add_computed_column(grayscale=t.image.convert('L')) ```
  Added 3 column values with 0 errors.
  3 rows updated, 3 values computed.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # View images with grayscale column t.collect() ```
### Iterate: convert with gamma decompression for a few images first ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf def rgb_to_gray_accurate(img: Image.Image) -> Image.Image: """Convert RGB to grayscale with full gamma correction. Most accurate but slower. Gamma-decompresses, applies perceptual weights in linear space, then re-compresses for display. """ rgb = np.array(img).astype(np.float32) / 255.0 # Gamma decompress: make pixel values perceptually linear rgb_lin = ((rgb + 0.055) / 1.055) ** 2.4 rgb_lin = np.where(rgb <= 0.04045, rgb / 12.92, rgb_lin) # Apply perceptual weights in linear space gray_lin = ( 0.2126 * rgb_lin[:, :, 0] + 0.7152 * rgb_lin[:, :, 1] + 0.0722 * rgb_lin[:, :, 2] ) # Gamma compress: make values display-ready gray = 1.055 * gray_lin ** (1 / 2.4) - 0.055 gray = np.where(gray_lin <= 0.0031308, 12.92 * gray_lin, gray) gray = (gray * 255).astype(np.uint8) return Image.fromarray(gray) ``` ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Compare both methods on first image t.select(t.image, t.grayscale, rgb_to_gray_accurate(t.image)).head(1) ```
### Add: convert with gamma decompression for all images in your table ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} t.add_computed_column(accurate=rgb_to_gray_accurate(t.image)) ```
  Added 3 column values with 0 errors.
  3 rows updated, 3 values computed.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # View all results t.collect() ```
## Explanation **Two approaches:** 1. **Simple (`.convert('L')`):** PIL’s built-in. Fast, good for most use cases (model preprocessing, general analysis). 2. **Gamma-corrected (custom UDF):** Not built into PIL. Requires a custom UDF that: * Gamma-decompresses to linear space * Applies perceptual weights: 0.2126 × R + 0.7152 × G + 0.0722 × B * Gamma-compresses back for display * Slower but most perceptually accurate * Use for scientific imaging, professional photography **Why gamma matters:** Displays aren’t linear—doubling a pixel value doesn’t double perceived brightness. Gamma correction accounts for this. For best results, convert to linear space before weighting, then convert back. *The gamma-corrected method is based on [Brandon Rohrer’s explanation](https://brandonrohrer.com/convert_rgb_to_grayscale.html) of perceptually accurate RGB to grayscale conversion.* **The Pixeltable workflow:** In traditional databases, `.select()` just picks which columns to view. In Pixeltable, `.select()` also lets you compute new transformations on the fly—define new columns without storing them. This makes `.select()` perfect for testing transformations before you commit them. When you use `.select()`, you’re creating a query that doesn’t execute until you call `.collect()`. You must use `.collect()` to execute the query and return results—nothing is stored in your table. If you want to collect only the first few rows, use `.head(n)` instead of `.collect()` to test on a subset before processing your full dataset. Once satisfied, use `.add_computed_column()` with the same expression to persist results permanently. For more on this workflow, see [Get fast feedback on transformations](/howto/cookbooks/core/dev-iterative-workflow). ## See also * [Transform images with PIL operations](/howto/cookbooks/images/img-pil-transforms) * [Test transformations with fast feedback loops](/howto/cookbooks/core/dev-iterative-workflow) # Visualize object detections Source: https://docs.pixeltable.com/howto/cookbooks/images/img-visualize-detections Open in Kaggle  Open in Colab  Download Notebook This documentation page is also available as an interactive notebook. You can launch the notebook in Kaggle or Colab, or download it for use with an IDE or local Jupyter installation, by clicking one of the above links. Draw bounding boxes on images to visualize object detection results. ## Problem You’ve run object detection on images but need to visualize the results—see where objects were detected and verify the model’s accuracy.
## Solution **What’s in this recipe:** * Run object detection with YOLOX * Draw bounding boxes on images * Color-code by object class You create a pipeline that detects objects and then draws the results on the original image. ### Setup ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} %pip install -qU pixeltable pixeltable-yolox ``` ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} import pixeltable as pxt from pixeltable.functions.vision import bboxes_draw from pixeltable.functions.yolox import yolox # Create a fresh directory pxt.drop_dir('viz_demo', force=True) pxt.create_dir('viz_demo') ```
  Connected to Pixeltable database at: postgresql+psycopg://postgres:@/pixeltable?host=/Users/pjlb/.pixeltable/pgdata
  Created directory 'viz\_demo'.
  \
### Create detection and visualization pipeline ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Create table for images images = pxt.create_table('viz_demo/images', {'image': pxt.Image}) ```
  Created table 'images'.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Step 1: Run object detection images.add_computed_column( detections=yolox(images.image, model_id='yolox_m', threshold=0.5) ) ```
  Added 0 column values with 0 errors.
  No rows affected.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Step 2: Draw bounding boxes on the image # Note: bboxes_draw takes image, boxes, and labels (scores are not used for drawing) images.add_computed_column( annotated=bboxes_draw( images.image, images.detections.bboxes, labels=images.detections.labels, ) ) ```
  Added 0 column values with 0 errors.
  No rows affected.
### Detect and visualize ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Insert sample images base_url = 'https://raw.githubusercontent.com/pixeltable/pixeltable/main/docs/resources/images' image_urls = [ f'{base_url}/000000000036.jpg', # cats f'{base_url}/000000000139.jpg', # elephants ] images.insert([{'image': url} for url in image_urls]) ```
  Inserting rows into \`images\`: 0 rows \[00:00, ? rows/s]
  Inserting rows into \`images\`: 2 rows \[00:00, 236.29 rows/s]
  Inserted 2 rows with 0 errors.
  2 rows inserted, 8 values computed.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # View original vs annotated images side by side images.select(images.image, images.annotated).collect() ```
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # View detection details images.select(images.detections).collect() ```
## Explanation **Pipeline flow:**
  Image → YOLOX detection → Bounding boxes + labels → bboxes\_draw → Annotated image
**Detection output format:** The `yolox` function returns a dict with: * `bboxes` - List of \[x1, y1, x2, y2] coordinates * `labels` - List of class names (e.g., “cat”, “dog”) * `scores` - List of confidence scores (0-1) **YOLOX model options:**
## See also * [Detect objects in images](/howto/cookbooks/images/img-detect-objects) - Object detection basics * [Extract video frames](/howto/cookbooks/video/video-extract-frames) - Detect objects in video # Analyze images in batch with AI vision Source: https://docs.pixeltable.com/howto/cookbooks/images/vision-batch-analysis Open in Kaggle  Open in Colab  Download Notebook This documentation page is also available as an interactive notebook. You can launch the notebook in Kaggle or Colab, or download it for use with an IDE or local Jupyter installation, by clicking one of the above links. Run the same AI prompt against multiple images automatically, without writing loops or managing API calls. ## Problem You have a collection of images that all need the same analysis—like “Describe this image”, “Is this product damaged?”, or “What objects are visible?”. Writing a loop to call an API for each image is tedious and error-prone. You need to handle rate limits, retries, and track which images succeeded or failed.
## Solution **What’s in this recipe:** * Analyze multiple images with a single prompt using `openai.chat_completions()` * Get all results at once, stored in your table * No loops or manual API calls You add a computed column that applies `openai.chat_completions()` with multimodal messages to every image in your table. Pixeltable handles the API calls, retries, and result storage automatically. When you insert new images, the analysis runs automatically—no extra code needed. ### Setup ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} %pip install -qU pixeltable openai import getpass import os if 'OPENAI_API_KEY' not in os.environ: os.environ['OPENAI_API_KEY'] = getpass.getpass('OpenAI API Key: ') ``` ### Load images ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} import pixeltable as pxt from pixeltable.functions import openai # Create a fresh directory pxt.drop_dir('vision_demo', force=True) pxt.create_dir('vision_demo') ```
  Connected to Pixeltable database at: postgresql+psycopg://postgres:@/pixeltable?host=/Users/asiegel/.pixeltable/pgdata
  Created directory 'vision\_demo'.
  \
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} t = pxt.create_table('vision_demo/images', {'image': pxt.Image}) ```
  Created table 'images'.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Insert sample images t.insert( [ { 'image': 'https://raw.githubusercontent.com/pixeltable/pixeltable/main/docs/resources/images/000000000036.jpg' }, { 'image': 'https://raw.githubusercontent.com/pixeltable/pixeltable/main/docs/resources/images/000000000090.jpg' }, { 'image': 'https://raw.githubusercontent.com/pixeltable/pixeltable/main/docs/resources/images/000000000106.jpg' }, ] ) ```
  Inserted 3 rows with 0 errors in 0.03 s (88.80 rows/s)
  3 rows inserted.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # View loaded images t.collect() ```
### Analyze images with AI Add a computed column using `openai.chat_completions()`. The prompt runs automatically on all images: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Define the prompt messages = [ { 'role': 'user', 'content': [ { 'type': 'text', 'text': 'Describe this image in one sentence.', }, {'type': 'image_url', 'image_url': t.image}, ], } ] # Add computed column for AI analysis using openai.chat_completions() t.add_computed_column( description=openai.chat_completions(messages, model='gpt-4o-mini') ) ```
  Added 3 column values with 0 errors in 4.84 s (0.62 rows/s)
  3 rows updated.
### View results `openai.chat_completions()` returns a JSON structure containing the output, which we can unpack in the usual way: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # View results: image alongside its AI-generated description t.select( t.image, t.description, t.description['choices'][0]['message']['content'], ).collect() ```
### New images are analyzed automatically When you insert more images, the analysis runs without any extra code: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Insert a new image - analysis happens automatically t.insert( [ { 'image': 'https://raw.githubusercontent.com/pixeltable/pixeltable/main/docs/resources/images/000000000139.jpg' } ] ) # View all results including the new image t.select( t.image, t.description, t.description['choices'][0]['message']['content'], ).collect() ```
## Explanation **How it works:** 1. Add images to your table 2. Define a computed column with `openai.chat_completions()` 3. Pixeltable executes the API call for each row automatically 4. Results are cached—rerunning won’t re-call the API 5. New rows trigger automatic computation **Changing the prompt:** To use a different prompt, add a new computed column with `if_exists='replace'`: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} messages = ... t.add_computed_column( description=openai.chat_completions(messages, model='gpt-4o-mini'), if_exists='replace' ) ``` **Using other providers:** Replace `openai.chat_completions` with: * `anthropic.messages` for Claude * `gemini.generate_content` for Gemini * `together.chat_completions` for Together AI ## See also * [Configure API keys](/howto/cookbooks/core/workflow-api-keys) * [Working with OpenAI](/howto/providers/working-with-openai) ### New images are analyzed automatically When you insert more images, the analysis runs without any extra code: # Extract structured data from images Source: https://docs.pixeltable.com/howto/cookbooks/images/vision-structured-output Open in Kaggle  Open in Colab  Download Notebook This documentation page is also available as an interactive notebook. You can launch the notebook in Kaggle or Colab, or download it for use with an IDE or local Jupyter installation, by clicking one of the above links. Use AI vision to extract JSON data from receipts, forms, documents, and other images. ## Problem You have images containing structured information (receipts, forms, ID cards) and need to extract specific fields as JSON for downstream processing.
## Solution **What’s in this recipe:** * Extract structured JSON from images using GPT-4o * Use `openai.chat_completions()` with multimodal messages * Access individual fields from the extracted data You use Pixeltable’s `openai.chat_completions()` function with multimodal messages that include images directly. Request JSON output via `response_format` in `model_kwargs`. ### Setup ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} %pip install -qU pixeltable openai import getpass import os if 'OPENAI_API_KEY' not in os.environ: os.environ['OPENAI_API_KEY'] = getpass.getpass('OpenAI API Key: ') ``` ### Load images ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} import pixeltable as pxt from pixeltable.functions import openai # Create a fresh directory pxt.drop_dir('extraction_demo', force=True) pxt.create_dir('extraction_demo') ```
  Connected to Pixeltable database at: postgresql+psycopg://postgres:@/pixeltable?host=/Users/asiegel/.pixeltable/pgdata
  Created directory 'extraction\_demo'.
  \
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} t = pxt.create_table('extraction_demo/images', {'image': pxt.Image}) ```
  Created table 'images'.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Insert sample images t.insert( [ { 'image': 'https://raw.githubusercontent.com/pixeltable/pixeltable/main/docs/resources/images/000000000036.jpg' }, { 'image': 'https://raw.githubusercontent.com/pixeltable/pixeltable/main/docs/resources/images/000000000090.jpg' }, ] ) ```
  Inserted 2 rows with 0 errors in 0.03 s (60.43 rows/s)
  2 rows inserted.
### Extract structured data Use `openai.chat_completions()` to analyze images and get JSON output: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Add extraction column using openai.chat_completions with multimodal messages PROMPT = """Analyze this image and extract the following as JSON: - description: A brief description of the image - objects: List of objects visible in the image - dominant_colors: List of dominant colors - scene_type: Type of scene (indoor, outdoor, etc.)""" messages = [ { 'role': 'user', 'content': [ {'type': 'text', 'text': PROMPT}, {'type': 'image_url', 'image_url': t.image}, ], } ] t.add_computed_column( data=openai.chat_completions( messages, model='gpt-4o-mini', model_kwargs={'response_format': {'type': 'json_object'}}, ) ) ```
  Added 2 column values with 0 errors in 7.55 s (0.26 rows/s)
  2 rows updated.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # View extracted data t.select( t.image, t.data, t.data['choices'][0]['message']['content'] ).collect() ```
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # You can also parse the JSON into individual columns if needed import json @pxt.udf def parse_description(data: str) -> str: return json.loads(data).get('description', '') t.select( t.image, description=parse_description( t.data['choices'][0]['message']['content'] ), ).collect() ```
## Explanation **Getting JSON output:** Pass `model_kwargs={'response_format': {'type': 'json_object'}}` to get structured JSON. **Other extraction use cases:**
## See also * [Analyze images in batch](/howto/cookbooks/images/vision-batch-analysis) * [Configure API keys](/howto/cookbooks/core/workflow-api-keys) # Create text embeddings with OpenAI Source: https://docs.pixeltable.com/howto/cookbooks/search/embed-text-openai Open in Kaggle  Open in Colab  Download Notebook This documentation page is also available as an interactive notebook. You can launch the notebook in Kaggle or Colab, or download it for use with an IDE or local Jupyter installation, by clicking one of the above links. Generate vector embeddings for text data to enable semantic search and similarity matching. This notebook uses `pxt.create_table()` so you can run cells without a project. An app puts the same columns on a `TableModel` in `app.py` and creates the tables with `pxt schema update`. ## Problem You need to convert text into vector embeddings for: * Semantic search (find similar documents) * RAG pipelines (retrieve relevant context) * Clustering and classification
## Solution **What’s in this recipe:** * Generate embeddings with OpenAI’s models * Store embeddings as computed columns * Use embeddings for similarity queries You add an embedding column that automatically generates vectors for new rows. The embeddings are cached and only recomputed when the source text changes. ### Setup ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} %pip install -qU pixeltable openai ``` ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} import getpass import os if 'OPENAI_API_KEY' not in os.environ: os.environ['OPENAI_API_KEY'] = getpass.getpass('OpenAI API Key: ') ``` ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} import pixeltable as pxt from pixeltable.functions.openai import embeddings # Create a fresh directory pxt.drop_dir('embed_demo', force=True) pxt.create_dir('embed_demo') ```
  Connected to Pixeltable database at: postgresql+psycopg://postgres:@/pixeltable?host=/Users/pjlb/.pixeltable/pgdata
  Created directory 'embed\_demo'.
  \
### Create table with embedding column ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Create table for documents docs = pxt.create_table( 'embed_demo/documents', {'title': pxt.String, 'content': pxt.String} ) ```
  Created table 'documents'.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Add embedding column using OpenAI's text-embedding-3-small docs.add_computed_column( embedding=embeddings(docs.content, model='text-embedding-3-small') ) ```
  Added 0 column values with 0 errors.
  No rows affected.
### Insert documents ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Insert sample documents sample_docs = [ { 'title': 'Python Basics', 'content': 'Python is a high-level programming language known for its clear syntax and readability.', }, { 'title': 'Machine Learning', 'content': 'Machine learning is a subset of AI that enables systems to learn from data.', }, { 'title': 'Web Development', 'content': 'Web development involves building websites and web applications using HTML, CSS, and JavaScript.', }, { 'title': 'Data Science', 'content': 'Data science combines statistics, programming, and domain expertise to extract insights from data.', }, { 'title': 'Cloud Computing', 'content': 'Cloud computing provides on-demand computing resources over the internet.', }, ] docs.insert(sample_docs) ```
  Inserting rows into \`documents\`: 5 rows \[00:00, 553.22 rows/s]
  Inserted 5 rows with 0 errors.
  5 rows inserted, 15 values computed.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # View documents with embeddings (showing first 5 dimensions) result = docs.select(docs.title, docs.embedding).collect() ``` ### Query by similarity Find documents similar to a query by creating an embedding index: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Add embedding index for semantic search docs.add_embedding_index( column='content', string_embed=embeddings.using(model='text-embedding-3-small'), ) ``` ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Search for similar documents sim = docs.content.similarity( string='artificial intelligence applications' ) results = ( docs.where(sim > 0.2) .order_by(sim, asc=False) .limit(3) .select(docs.title, docs.content, sim=sim) ) results.collect() ```
## Explanation **OpenAI embedding models:**
**Similarity metrics:**
**Key benefits of computed embedding columns:** * Embeddings are generated automatically on insert * Results are cached—no re-computation on subsequent queries * Index enables fast similarity search at scale ## See also * [Semantic text search](/howto/cookbooks/search/search-semantic-text) - Full semantic search patterns * [Chunk documents for RAG](/howto/cookbooks/text/doc-chunk-for-rag) - Prepare documents for retrieval # Build semantic search for text Source: https://docs.pixeltable.com/howto/cookbooks/search/search-semantic-text Open in Kaggle  Open in Colab  Download Notebook This documentation page is also available as an interactive notebook. You can launch the notebook in Kaggle or Colab, or download it for use with an IDE or local Jupyter installation, by clicking one of the above links. Create a searchable knowledge base that finds content by meaning, not just keywords. ## Problem You have a collection of text content (articles, notes, documentation) and need to find relevant items based on meaning. Keyword search fails when users phrase queries differently from the source text:
## Solution **What’s in this recipe:** * Create a text table with embeddings * Search by semantic similarity * Combine with metadata filters You add an embedding index to your text column. Pixeltable automatically generates embeddings for each row and enables similarity search. ### Setup ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} %pip install -qU pixeltable sentence-transformers ``` ### Create knowledge base ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} import pixeltable as pxt from pixeltable.functions.huggingface import sentence_transformer # Create a fresh directory pxt.drop_dir('search_demo', force=True) pxt.create_dir('search_demo') ```
  Connected to Pixeltable database at: postgresql+psycopg://postgres:@/pixeltable?host=/Users/pjlb/.pixeltable/pgdata
  Created directory 'search\_demo'.
  \
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Create table with content and metadata kb = pxt.create_table( 'search_demo/articles', {'title': pxt.String, 'content': pxt.String, 'category': pxt.String}, ) ```
  Created table 'articles'.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Insert sample content kb.insert( [ { 'title': 'Debugging best practices', 'content': 'Use logging, breakpoints, and unit tests to identify and fix issues in your code.', 'category': 'engineering', }, { 'title': 'Machine learning model optimization', 'content': 'Improve training efficiency with batch normalization, learning rate schedules, and early stopping.', 'category': 'ml', }, { 'title': 'Production infrastructure setup', 'content': 'Deploy applications using containers, load balancers, and automated scaling.', 'category': 'devops', }, { 'title': 'API design principles', 'content': 'Create RESTful endpoints with proper versioning, authentication, and error handling.', 'category': 'engineering', }, ] ) ```
  Inserting rows into \`articles\`: 4 rows \[00:00, 577.69 rows/s]
  Inserted 4 rows with 0 errors.
  4 rows inserted, 12 values computed.
### Add semantic search Create an embedding index on the content column: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Add embedding index kb.add_embedding_index( column='content', string_embed=sentence_transformer.using(model_id='all-MiniLM-L6-v2'), ) ``` ### Search by meaning Find content semantically similar to your query: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Search by meaning query = 'how to fix bugs' sim = kb.content.similarity(string=query) results = ( kb.order_by(sim, asc=False) .select(kb.title, kb.content, score=sim) .limit(2) ) results.collect() ```
### Filter by metadata Combine semantic search with metadata filters: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Search within a specific category query = 'best practices' sim = kb.content.similarity(string=query) results = ( kb.where(kb.category == 'engineering') # Filter first .order_by(sim, asc=False) .select(kb.title, kb.category, score=sim) .limit(2) ) results.collect() ```
## Explanation **How similarity search works:** 1. Your query is converted to an embedding vector 2. Pixeltable finds the most similar vectors in the index 3. Results are ranked by cosine similarity (0 to 1) **Embedding models:**
**New content is indexed automatically:** When you insert new rows, embeddings are generated without extra code. ## See also * [Vector database documentation](/platform/embedding-indexes) * [Split documents for RAG](/howto/cookbooks/text/doc-chunk-for-rag) # Find similar images with CLIP Source: https://docs.pixeltable.com/howto/cookbooks/search/search-similar-images Open in Kaggle  Open in Colab  Download Notebook This documentation page is also available as an interactive notebook. You can launch the notebook in Kaggle or Colab, or download it for use with an IDE or local Jupyter installation, by clicking one of the above links. Build visual similarity search to find images that look alike using OpenAI’s CLIP model. ## Problem You have a collection of images and need to find visually similar ones—for duplicate detection, content recommendations, or visual search.
## Solution **What’s in this recipe:** * Create image embeddings with CLIP * Search by image similarity * Search by text description (cross-modal) You add an embedding index using CLIP, which understands both images and text. This enables finding similar images or searching images by text description. ### Setup ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} %pip install -qU pixeltable sentence-transformers torch ``` ### Load images ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} import pixeltable as pxt from pixeltable.functions.huggingface import clip # Create a fresh directory pxt.drop_dir('image_search_demo', force=True) pxt.create_dir('image_search_demo') ```
  Connected to Pixeltable database at: postgresql+psycopg://postgres:@/pixeltable?host=/Users/pjlb/.pixeltable/pgdata
  Created directory 'image\_search\_demo'.
  \
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} images = pxt.create_table( 'image_search_demo/images', {'image': pxt.Image} ) ```
  Created table 'images'.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Insert sample images images.insert( [ { 'image': 'https://raw.githubusercontent.com/pixeltable/pixeltable/main/docs/resources/images/000000000036.jpg' }, { 'image': 'https://raw.githubusercontent.com/pixeltable/pixeltable/main/docs/resources/images/000000000090.jpg' }, { 'image': 'https://raw.githubusercontent.com/pixeltable/pixeltable/main/docs/resources/images/000000000106.jpg' }, { 'image': 'https://raw.githubusercontent.com/pixeltable/pixeltable/main/docs/resources/images/000000000139.jpg' }, ] ) ```
  Inserting rows into \`images\`: 4 rows \[00:00, 973.44 rows/s]
  Inserted 4 rows with 0 errors.
  4 rows inserted, 8 values computed.
### Create CLIP embedding index Add an embedding index using CLIP for cross-modal search: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Add CLIP embedding index (supports both image and text queries) images.add_embedding_index( 'image', embedding=clip.using(model_id='openai/clip-vit-base-patch32') ) ``` ### Search by text description Find images matching a text query: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Search by text description query = 'people eating food' sim = images.image.similarity(string=query) results = ( images.order_by(sim, asc=False) .select(images.image, score=sim) .limit(2) ) results.collect() ```
## Explanation **Why CLIP:** CLIP (Contrastive Language-Image Pre-training) understands both images and text in the same embedding space. This enables: * Image-to-image search (find similar photos) * Text-to-image search (find photos matching a description) **Index parameters:**
**Both must use the same model** for cross-modal search to work. **New images are indexed automatically:** When you insert new images, embeddings are generated without extra code. ## See also * [Semantic text search](/howto/cookbooks/search/search-semantic-text) * [Vector database documentation](/platform/embedding-indexes) # Split documents into chunks for RAG Source: https://docs.pixeltable.com/howto/cookbooks/text/doc-chunk-for-rag Open in Kaggle  Open in Colab  Download Notebook This documentation page is also available as an interactive notebook. You can launch the notebook in Kaggle or Colab, or download it for use with an IDE or local Jupyter installation, by clicking one of the above links. Break PDFs and documents into searchable chunks for retrieval-augmented generation (RAG) pipelines. ## Problem You have PDF documents or text files that you want to use for retrieval-augmented generation (RAG). Before you can search them, you need to: 1. Split documents into smaller chunks 2. Generate embeddings for each chunk 3. Store everything in a searchable index
## Solution **What’s in this recipe:** * Split PDFs into sentences with token limits * Control chunk size with token limits * Add embeddings for semantic search You create a view with a `document_splitter` iterator that automatically breaks documents into chunks. Then you add an embedding index for semantic search. ### Setup ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} %pip install -qU pixeltable sentence-transformers spacy tiktoken !python -m spacy download en_core_web_sm -q ``` ### Load documents ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} import pixeltable as pxt from pixeltable.functions.document import document_splitter from pixeltable.functions.huggingface import sentence_transformer # Create a fresh directory pxt.drop_dir('rag_demo', force=True) pxt.create_dir('rag_demo') ```
  Created directory 'rag\_demo'.
  \
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Create table for documents docs = pxt.create_table('rag_demo/documents', {'document': pxt.Document}) ```
  Created table 'documents'.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Insert a sample PDF docs.insert( [ { 'document': 'https://raw.githubusercontent.com/pixeltable/pixeltable/main/docs/resources/rag-demo/Argus-Market-Digest-June-2024.pdf' } ] ) ```
  Inserting rows into \`documents\`: 1 rows \[00:00, 775.86 rows/s]
  Inserted 1 row with 0 errors.
  1 row inserted, 2 values computed.
### Split into chunks Create a view that splits each document into sentences with a token limit: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Create a view that splits documents into chunks chunks = pxt.create_view( 'rag_demo/chunks', docs, iterator=document_splitter( docs.document, separators='sentence,token_limit', # Split by sentence with token limit limit=300, # Max 300 tokens per chunk ), ) ```
  Inserting rows into \`chunks\`: 217 rows \[00:00, 42111.88 rows/s]
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # View the chunks chunks.select(chunks.text).head(5) ```
### Add semantic search Create an embedding index on the chunks for similarity search: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Add embedding index for semantic search chunks.add_embedding_index( column='text', string_embed=sentence_transformer.using(model_id='all-MiniLM-L6-v2'), ) ``` ### Search your documents Use similarity search to find relevant chunks: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Search for relevant chunks query = 'market trends' sim = chunks.text.similarity(string=query) results = ( chunks.order_by(sim, asc=False) .select(chunks.text, score=sim) .limit(3) ) results.collect() ```
## Explanation **Separator options:**
You can combine separators: `separators='sentence,token_limit'` **Chunk sizing:** * `limit`: Maximum tokens per chunk (default: 500) * `overlap`: Tokens to overlap between chunks (default: 0) **New documents are processed automatically:** When you insert new documents, chunks and embeddings are generated without extra code. ## See also * [Iterators documentation](/platform/iterators) * [RAG demo notebook](/howto/use-cases/rag-demo) # Extract text from PowerPoint, Word, and Excel files Source: https://docs.pixeltable.com/howto/cookbooks/text/doc-extract-text-from-office-files Open in Kaggle  Open in Colab  Download Notebook This documentation page is also available as an interactive notebook. You can launch the notebook in Kaggle or Colab, or download it for use with an IDE or local Jupyter installation, by clicking one of the above links. Transform office documents into searchable, analyzable text data. **What’s in this recipe:** * Extract text from PPTX, DOCX, and XLSX files * Split documents by headings, paragraphs, or custom limits * Preserve document structure and metadata for analysis ## Problem You have office documents—presentations, reports, spreadsheets—that contain valuable text data. You need to extract this text to analyze content, search across documents, or feed into AI models. Manual extraction means opening each file, copying text, and losing structural information like headings and page boundaries. You need an automated way to process hundreds or thousands of office files while preserving their organization. ## Solution You extract text from office documents using Pixeltable’s document type with Microsoft’s MarkItDown library. This converts PowerPoint, Word, and Excel files to structured text automatically. You use `DocumentSplitter` to split documents by headings, paragraphs, or token limits. Each split creates a view where each row represents a chunk of the document with its metadata. ### Setup ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} %pip install -qU pixeltable 'markitdown[pptx,docx,xlsx]' mistune tiktoken ``` ### Load office documents ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} import pixeltable as pxt from pixeltable.iterators.document import DocumentSplitter # Create a fresh directory (drop existing if present) pxt.drop_dir('office_docs', force=True) pxt.create_dir('office_docs') ```
  Connected to Pixeltable database at: postgresql+psycopg://postgres:@/pixeltable?host=/Users/pjlb/.pixeltable/pgdata
  Created directory 'office\_docs'.
  \
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Create table for office documents docs = pxt.create_table('office_docs/documents', {'doc': pxt.Document}) ```
  Created table 'documents'.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Sample PowerPoint from Pixeltable repo # Replace with your own PPTX, DOCX, or XLSX files sample_url = 'https://raw.githubusercontent.com/pixeltable/pixeltable/main/docs/resources/calpy.pptx' docs.insert([{'doc': sample_url}]) ```
  Inserting rows into \`documents\`: 1 rows \[00:00, 57.40 rows/s]
  Inserted 1 row with 0 errors.
  1 row inserted, 2 values computed.
### Extract full document text You create a view with `DocumentSplitter` to extract text. Setting `separators=''` extracts the full document without splitting. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Create a view to extract full document text full_text = pxt.create_view( 'office_docs/full_text', docs, iterator=DocumentSplitter.create( document=docs.doc, separators='', # No splitting - extract full document ), ) ```
  Inserting rows into \`full\_text\`: 1 rows \[00:00, 196.50 rows/s]
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Preview extracted text full_text.select(full_text.doc, full_text.text).head(1) ```
### Split documents by headings You split documents by headings to preserve their logical structure. Each section under a heading becomes a separate chunk. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Create view that splits by headings by_heading = pxt.create_view( 'office_docs/by_heading', docs, iterator=DocumentSplitter.create( document=docs.doc, separators='heading', metadata='heading', # Preserve heading structure ), ) ```
  Inserting rows into \`by\_heading\`: 87 rows \[00:00, 10359.54 rows/s]
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # View chunks with their headings by_heading.select(by_heading.heading, by_heading.text).head(5) ```
### Split by token limit for AI models You split documents by token count when feeding chunks to AI models. The `overlap` parameter ensures chunks share context at boundaries. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Create view with token-based splitting by_tokens = pxt.create_view( 'office_docs/by_tokens', docs, iterator=DocumentSplitter.create( document=docs.doc, separators='heading,token_limit', # Split by heading first, then by tokens limit=512, # Maximum tokens per chunk overlap=50, # Overlap between chunks to preserve context metadata='heading', ), ) ```
  Inserting rows into \`by\_tokens\`: 2369 rows \[00:00, 9212.05 rows/s]
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Preview chunks with token limits by_tokens.select(by_tokens.doc, by_tokens.heading, by_tokens.text).head(3) ```
### Search across documents You search across all document chunks using standard Pixeltable queries. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Find chunks containing specific keywords by_tokens.where(by_tokens.text.contains('Python')).select( by_tokens.doc, by_tokens.text ).head(3) ```
## Explanation **Supported formats:** * PowerPoint: `.pptx`, `.ppt` * Word: `.docx`, `.doc` * Excel: `.xlsx`, `.xls` **Separator options:** * `heading` - Split by document headings (preserves structure) * `paragraph` - Split by paragraphs * `sentence` - Split by sentences * `token_limit` - Split by token count (requires `limit` parameter) * `char_limit` - Split by character count (requires `limit` parameter) * Multiple separators work together: `'heading,token_limit'` splits by heading first, then ensures no chunk exceeds token limit **Metadata fields:** * `heading` - Hierarchical heading structure (e.g., `{'h1': 'Introduction', 'h2': 'Overview'}`) * `title` - Document title * `sourceline` - Source line number (HTML and Markdown documents) **Token overlap:** The `overlap` parameter ensures chunks share context at boundaries. This prevents sentences from being split mid-thought when feeding chunks to AI models. ## See also * [Get fast feedback on transformations](/howto/cookbooks/core/dev-iterative-workflow) * [Pixeltable Document API](/sdk/latest/document) # Ingest a web page Source: https://docs.pixeltable.com/howto/cookbooks/text/doc-ingest-website Insert a URL as a document, then compare chunking strategies on it A `pxt.Document` column takes a URL as readily as a local path. Insert the URL and Pixeltable fetches and parses the page, so there is no separate scraping step to keep in sync with the table. ## Insert the URL ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} import pixeltable as pxt from pixeltable.functions.document import document_splitter pxt.create_dir('web', if_exists='ignore') sites = pxt.create_table('web.sites', {'url': pxt.Document}, if_exists='ignore') sites.insert([{'url': 'https://en.wikipedia.org/wiki/Ant'}]) ``` ## Chunk it, two ways The chunking strategy decides what a retrieval hit looks like, so it is worth seeing the difference before committing to one. A view per strategy, over the same table: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} by_paragraph = pxt.create_view( 'web.by_paragraph', sites, iterator=document_splitter(sites.url, separators='paragraph'), ) by_size = pxt.create_view( 'web.by_size', sites, iterator=document_splitter(sites.url, separators='char_limit', limit=1200), ) ``` On that one page: **152 paragraph chunks** against **117 fixed-size chunks**. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} print(by_paragraph.count(), by_size.count()) ``` `paragraph` follows the document's own structure, so a chunk is a complete thought and a retrieval hit reads as prose. Chunk length is whatever the author wrote, which on a page with one-line paragraphs produces chunks too small to carry context. `char_limit` gives you predictable sizes and predictable embedding cost, and cuts mid-sentence. Valid separators are `heading`, `paragraph`, `sentence`, `token_limit`, `char_limit`, and `page`. `sentence` needs `spacy` and its default English model: run `pip install spacy`, then `python -m spacy download en_core_web_sm`. `token_limit` needs `pip install tiktoken`. Combine separators with a comma, most structural first: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} iterator=document_splitter(sites.url, separators='heading,token_limit', limit=300) ``` That splits on headings, then splits any section still over 300 tokens, which keeps chunks inside a model's context window without cutting across two topics. ## Make it searchable Declare an index on the chunk text and query it with `similarity()`: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} from pixeltable.functions.huggingface import sentence_transformer embed = sentence_transformer.using(model_id='intfloat/e5-large-v2') by_paragraph.add_embedding_index('text', string_embed=embed) sim = by_paragraph.text.similarity(string='how do ants communicate') by_paragraph.order_by(sim, asc=False).limit(5).select(by_paragraph.text, sim).collect() ``` The index loads with the chunks already there and updates as you insert more URLs. ## Notes * Insert several URLs and every view and index below the table extends to them. There is no per-page bookkeeping. * `sentence_transformer` needs `pip install sentence-transformers`, and downloads the model on first use. * `pxt.create_table()`, `create_view()`, and `add_embedding_index()` are the notebook and test form. An application declares the tables and views on a `TableModel` in `app.py`, puts the index in `__indexes__`, and creates them with `pxt schema update`. # Extract named entities from text Source: https://docs.pixeltable.com/howto/cookbooks/text/text-extract-entities Open in Kaggle  Open in Colab  Download Notebook This documentation page is also available as an interactive notebook. You can launch the notebook in Kaggle or Colab, or download it for use with an IDE or local Jupyter installation, by clicking one of the above links. Identify and extract people, organizations, locations, dates, and other entities from text using LLMs. ## Problem You have unstructured text containing important information—names, companies, dates, locations—that you need to extract and structure for analysis, search, or integration with other systems.
## Solution **What’s in this recipe:** * Extract entities as structured JSON * Use OpenAI’s structured output for reliable parsing * Access extracted entities as queryable columns You use structured output to get entities in a consistent JSON format. The entities are stored as JSON columns that you can query and filter. ### Setup ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} %pip install -qU pixeltable openai ```
  WARNING: Ignoring invalid distribution \~orch (/opt/miniconda3/envs/pixeltable/lib/python3.11/site-packages)
  WARNING: Ignoring invalid distribution \~orch (/opt/miniconda3/envs/pixeltable/lib/python3.11/site-packages)
  WARNING: Ignoring invalid distribution \~orch (/opt/miniconda3/envs/pixeltable/lib/python3.11/site-packages)
  WARNING: Ignoring invalid distribution \~orch (/opt/miniconda3/envs/pixeltable/lib/python3.11/site-packages)
  WARNING: Ignoring invalid distribution \~orch (/opt/miniconda3/envs/pixeltable/lib/python3.11/site-packages)
  WARNING: Ignoring invalid distribution \~orch (/opt/miniconda3/envs/pixeltable/lib/python3.11/site-packages)
  Note: you may need to restart the kernel to use updated packages.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} import getpass import os if 'OPENAI_API_KEY' not in os.environ: os.environ['OPENAI_API_KEY'] = getpass.getpass('OpenAI API Key: ') ``` ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} import json import pixeltable as pxt from pixeltable.functions.openai import chat_completions # Create a fresh directory pxt.drop_dir('entities_demo', force=True) pxt.create_dir('entities_demo') ```
  Created directory 'entities\_demo'.
  \
### Define entity extraction schema ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Define the JSON schema for entity extraction entity_schema = { 'type': 'json_schema', 'json_schema': { 'name': 'entities', 'strict': True, 'schema': { 'type': 'object', 'properties': { 'people': { 'type': 'array', 'items': {'type': 'string'}, 'description': 'Names of people mentioned', }, 'organizations': { 'type': 'array', 'items': {'type': 'string'}, 'description': 'Names of companies, institutions, or groups', }, 'locations': { 'type': 'array', 'items': {'type': 'string'}, 'description': 'Geographic locations (cities, countries, addresses)', }, 'dates': { 'type': 'array', 'items': {'type': 'string'}, 'description': 'Dates or time references', }, }, 'required': ['people', 'organizations', 'locations', 'dates'], 'additionalProperties': False, }, }, } ``` ### Create extraction pipeline ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Create table for articles articles = pxt.create_table( 'entities_demo/articles', {'title': pxt.String, 'content': pxt.String} ) ```
  Created table 'articles'.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Add entity extraction column extraction_prompt = ( 'Extract all named entities from the following text:\n\n' + articles.content ) articles.add_computed_column( extraction_response=chat_completions( messages=[{'role': 'user', 'content': extraction_prompt}], model='gpt-4o-mini', model_kwargs={'response_format': entity_schema}, ) ) ```
  Added 0 column values with 0 errors.
  No rows affected.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Extract the entities JSON articles.add_computed_column( entities=articles.extraction_response.choices[0].message.content ) ```
  Added 0 column values with 0 errors.
  No rows affected.
### Extract entities from text ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Insert sample articles sample_articles = [ { 'title': 'Tech Acquisition', 'content': 'Microsoft announced today that CEO Satya Nadella will lead the acquisition of a Seattle-based startup. The deal, expected to close in March 2024, is valued at $500 million.', }, { 'title': 'Sports Update', 'content': 'LeBron James led the Los Angeles Lakers to victory against the Boston Celtics on Tuesday night at Staples Center. Coach Darvin Ham praised the teams performance.', }, { 'title': 'Research Breakthrough', 'content': 'Dr. Sarah Chen at Stanford University published groundbreaking research on renewable energy. The study, funded by the National Science Foundation, was conducted in Palo Alto, California.', }, ] articles.insert(sample_articles) ```
  Inserting rows into \`articles\`: 3 rows \[00:00, 404.21 rows/s]
  Inserted 3 rows with 0 errors.
  3 rows inserted, 12 values computed.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # View extracted entities articles.select(articles.title, articles.entities).collect() ```
## Explanation **Structured output ensures reliable extraction:** By using OpenAI’s structured output (`response_format`), the model always returns valid JSON matching the schema. No post-processing or error handling needed. **Common entity types:**
**Customizing the schema:** Modify the `entity_schema` to extract domain-specific entities—product SKUs, legal terms, medical conditions, etc. ## See also * [Extract structured data from images](/howto/cookbooks/images/vision-structured-output) - JSON extraction from images * [Extract fields from JSON](/howto/cookbooks/core/workflow-json-extraction) - Parse LLM response fields # Summarize text with LLMs Source: https://docs.pixeltable.com/howto/cookbooks/text/text-summarize Open in Kaggle  Open in Colab  Download Notebook This documentation page is also available as an interactive notebook. You can launch the notebook in Kaggle or Colab, or download it for use with an IDE or local Jupyter installation, by clicking one of the above links. Generate concise summaries of long text, articles, or documents using large language models. This notebook uses `pxt.create_table()` so you can run cells without a project. An app puts the same columns on a `TableModel` in `app.py` and creates the tables with `pxt schema update`. ## Problem You have long text content—articles, transcripts, documents—that needs to be summarized. Processing each piece manually is time-consuming and inconsistent.
## Solution **What’s in this recipe:** * Summarize text using OpenAI GPT models * Customize summary style with prompts * Process multiple documents automatically You add a computed column that calls an LLM to generate summaries. When you insert new text, summaries are generated automatically. ### Setup ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} %pip install -qU pixeltable openai ```
  WARNING: Ignoring invalid distribution \~orch (/opt/miniconda3/envs/pixeltable/lib/python3.11/site-packages)
  WARNING: Ignoring invalid distribution \~orch (/opt/miniconda3/envs/pixeltable/lib/python3.11/site-packages)
  WARNING: Ignoring invalid distribution \~orch (/opt/miniconda3/envs/pixeltable/lib/python3.11/site-packages)
  WARNING: Ignoring invalid distribution \~orch (/opt/miniconda3/envs/pixeltable/lib/python3.11/site-packages)
  WARNING: Ignoring invalid distribution \~orch (/opt/miniconda3/envs/pixeltable/lib/python3.11/site-packages)
  WARNING: Ignoring invalid distribution \~orch (/opt/miniconda3/envs/pixeltable/lib/python3.11/site-packages)
  Note: you may need to restart the kernel to use updated packages.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} import getpass import os if 'OPENAI_API_KEY' not in os.environ: os.environ['OPENAI_API_KEY'] = getpass.getpass('OpenAI API Key: ') ``` ### Load sample text ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} import pixeltable as pxt from pixeltable.functions import openai # Create a fresh directory pxt.drop_dir('summarize_demo', force=True) pxt.create_dir('summarize_demo') ```
  Connected to Pixeltable database at: postgresql+psycopg://postgres:@/pixeltable?host=/Users/pjlb/.pixeltable/pgdata
  Created directory 'summarize\_demo'.
  \
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Create table for articles articles = pxt.create_table( 'summarize_demo/articles', {'title': pxt.String, 'content': pxt.String}, ) ```
  Created table 'articles'.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Sample articles to summarize sample_articles = [ { 'title': 'The Rise of Electric Vehicles', 'content': """Electric vehicles (EVs) have seen unprecedented growth in recent years, transforming the automotive industry. Sales increased by 60% globally in 2023, with China leading the market followed by Europe and North America. Major automakers like Tesla, BYD, and traditional manufacturers have invested billions in EV technology. Battery costs have dropped significantly, making EVs more affordable for consumers. Government incentives and stricter emissions regulations continue to drive adoption. Charging infrastructure is expanding rapidly, with new fast-charging networks being deployed across major highways. Despite challenges like range anxiety and charging times, consumer acceptance is growing steadily.""", }, { 'title': 'Advances in Renewable Energy', 'content': """Solar and wind power capacity reached record levels in 2023, accounting for over 30% of global electricity generation. The cost of solar panels has fallen by 90% over the past decade, making renewable energy competitive with fossil fuels. Offshore wind farms are being built at scale, with turbines now reaching heights of over 250 meters. Energy storage solutions, particularly lithium-ion batteries, are addressing intermittency challenges. Countries like Denmark and Scotland have achieved periods of 100% renewable electricity. Corporate power purchase agreements are accelerating the transition, with tech giants committing to carbon-neutral operations.""", }, ] articles.insert(sample_articles) ```
  Inserting rows into \`articles\`: 2 rows \[00:00, 316.21 rows/s]
  Inserted 2 rows with 0 errors.
  2 rows inserted, 4 values computed.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # View articles articles.select(articles.title, articles.content).collect() ```
### Generate summaries Add a computed column that generates summaries using GPT: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Create prompt template for summarization prompt = ( 'Summarize the following article in 2-3 sentences:\n\n' + articles.content ) # Add computed column for LLM response articles.add_computed_column( response=openai.chat_completions( messages=[{'role': 'user', 'content': prompt}], model='gpt-4o-mini', ) ) ```
  Added 2 column values with 0 errors.
  2 rows updated, 2 values computed.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Extract the summary text from the response articles.add_computed_column( summary=articles.response.choices[0].message.content ) ```
  Added 2 column values with 0 errors.
  2 rows updated, 2 values computed.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # View titles and summaries articles.select(articles.title, articles.summary).collect() ```
### Custom summary styles You can customize the summary format by changing the prompt: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Add bullet-point summary bullet_prompt = ( 'List the 3 key points from this article as bullet points:\n\n' + articles.content ) articles.add_computed_column( bullet_response=openai.chat_completions( messages=[{'role': 'user', 'content': bullet_prompt}], model='gpt-4o-mini', ) ) articles.add_computed_column( key_points=articles.bullet_response.choices[0].message.content ) ```
  Added 2 column values with 0 errors.
  Added 2 column values with 0 errors.
  2 rows updated, 2 values computed.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # View bullet-point summaries articles.select(articles.title, articles.key_points).collect() ```
### Automatic processing New articles are automatically summarized when inserted: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Insert a new article - summaries are generated automatically articles.insert( [ { 'title': 'AI in Healthcare', 'content': """Artificial intelligence is revolutionizing healthcare diagnostics and treatment planning. Machine learning models can now detect diseases from medical images with accuracy matching or exceeding human specialists. AI-powered drug discovery is accelerating the development of new treatments. Natural language processing is being used to extract insights from clinical notes and research papers.""", } ] ) ```
  Inserting rows into \`articles\`: 1 rows \[00:00, 411.57 rows/s]
  Inserted 1 row with 0 errors.
  1 row inserted, 6 values computed.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # View all summaries including the new article articles.select(articles.title, articles.summary).collect() ```
## Explanation **Prompt engineering for summaries:**
**Cost optimization:** * Use `gpt-4o-mini` for most summarization tasks (fast and affordable) * Use `gpt-4o` for complex documents requiring deeper understanding * Summaries are cached—you only pay once per article and stuand toofor trL para ## See also * [Split documents for RAG](/howto/cookbooks/text/doc-chunk-for-rag) - Process long documents * [Extract fields from JSON](/howto/cookbooks/core/workflow-json-extraction) - Parse structured LLM output * [Configure API keys](/howto/cookbooks/core/workflow-api-keys) - Set up OpenAI credentials # Translate text between languages Source: https://docs.pixeltable.com/howto/cookbooks/text/text-translate Open in Kaggle  Open in Colab  Download Notebook This documentation page is also available as an interactive notebook. You can launch the notebook in Kaggle or Colab, or download it for use with an IDE or local Jupyter installation, by clicking one of the above links. Automatically translate content into multiple languages using LLMs. ## Problem You have content that needs to be available in multiple languages—product descriptions, documentation, user-generated content. Manual translation is slow and expensive.
## Solution **What’s in this recipe:** * Translate text using OpenAI models * Create multiple language columns from one source * Handle batch translation efficiently You add computed columns for each target language. Translations are generated automatically when you insert new content and cached for future queries. ### Setup ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} %pip install -qU pixeltable openai ``` ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} import getpass import os if 'OPENAI_API_KEY' not in os.environ: os.environ['OPENAI_API_KEY'] = getpass.getpass('OpenAI API Key: ') ``` ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} import pixeltable as pxt from pixeltable.functions.openai import chat_completions # Create a fresh directory pxt.drop_dir('translate_demo', force=True) pxt.create_dir('translate_demo') ```
  Connected to Pixeltable database at: postgresql+psycopg://postgres:@/pixeltable?host=/Users/pjlb/.pixeltable/pgdata
  Created directory 'translate\_demo'.
  \
### Create translation pipeline ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Create table for content content = pxt.create_table( 'translate_demo/content', {'title': pxt.String, 'text_en': pxt.String} ) ```
  Created table 'content'.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Add Spanish translation column spanish_prompt = ( 'Translate the following text to Spanish. Return only the translation, no explanations:\n\n' + content.text_en ) content.add_computed_column( response_es=chat_completions( messages=[{'role': 'user', 'content': spanish_prompt}], model='gpt-4o-mini', ) ) content.add_computed_column( text_es=content.response_es.choices[0].message.content ) ```
  Added 0 column values with 0 errors.
  Added 0 column values with 0 errors.
  No rows affected.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Add French translation column french_prompt = ( 'Translate the following text to French. Return only the translation, no explanations:\n\n' + content.text_en ) content.add_computed_column( response_fr=chat_completions( messages=[{'role': 'user', 'content': french_prompt}], model='gpt-4o-mini', ) ) content.add_computed_column( text_fr=content.response_fr.choices[0].message.content ) ```
  Added 0 column values with 0 errors.
  Added 0 column values with 0 errors.
  No rows affected.
### Translate content ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Insert sample content sample_content = [ { 'title': 'Welcome Message', 'text_en': 'Welcome to our platform! We are excited to have you here.', }, { 'title': 'Product Description', 'text_en': 'This lightweight laptop features a 14-inch display and all-day battery life.', }, { 'title': 'Support Article', 'text_en': 'To reset your password, click the forgot password link on the login page.', }, ] content.insert(sample_content) ```
  Inserting rows into \`content\`: 3 rows \[00:00, 198.43 rows/s]
  Inserted 3 rows with 0 errors.
  3 rows inserted, 18 values computed.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # View all translations content.select( content.title, content.text_en, content.text_es, content.text_fr ).collect() ```
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Pretty print one example row = content.where(content.title == 'Welcome Message').collect()[0] ``` ## Explanation **How it works:** Each target language is a computed column with a translation prompt. When you insert new content: 1. The English text is processed 2. Translation prompts are generated for each language 3. All translations run in parallel 4. Results are cached—no re-translation needed **Adding more languages:** ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Add German translation german_prompt = 'Translate to German:\n\n' + content.text_en content.add_computed_column( response_de=chat_completions(messages=[{'role': 'user', 'content': german_prompt}], model='gpt-4o-mini') ) content.add_computed_column(text_de=content.response_de.choices[0].message.content) ``` **Cost optimization:**
## See also * [Summarize text](/howto/cookbooks/text/text-summarize) - Text summarization with LLMs * [Extract structured data](/howto/cookbooks/images/vision-structured-output) - Get JSON from LLM responses # Add text overlays to videos Source: https://docs.pixeltable.com/howto/cookbooks/video/video-add-text-overlay Open in Kaggle  Open in Colab  Download Notebook This documentation page is also available as an interactive notebook. You can launch the notebook in Kaggle or Colab, or download it for use with an IDE or local Jupyter installation, by clicking one of the above links. Burn text, captions, or watermarks directly into video files. ## Problem You need to add text to videos—captions, watermarks, titles, or dynamic labels. Manual video editing doesn’t scale for batch processing.
## Solution **What’s in this recipe:** * Add simple text overlays * Create styled captions with backgrounds * Position text with alignment options * Crop a rectangular region from a video Use `video.overlay_text()` to burn text into videos with full control over styling and position, and `video.crop()` to extract a rectangular region. ### Setup ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} %pip install -qU pixeltable ``` ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} import pixeltable as pxt # Create a fresh directory pxt.drop_dir('overlay_demo', force=True) pxt.create_dir('overlay_demo') ```
  Connected to Pixeltable database at: postgresql+psycopg://postgres:@/pixeltable?host=/Users/pjlb/.pixeltable/pgdata
  Created directory 'overlay\_demo'.
  \
### Load sample videos ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Create a video table videos = pxt.create_table( 'overlay_demo/videos', {'video': pxt.Video, 'title': pxt.String} ) # Insert a sample video videos.insert( [ { 'video': 's3://multimedia-commons/data/videos/mp4/ffe/ffb/ffeffbef41bbc269810b2a1a888de.mp4', 'title': 'Sample Video', } ] ) ```
  Created table 'videos'.
  Inserted 1 row with 0 errors in 3.21 s (0.31 rows/s)
  1 row inserted.
### Add a simple text overlay ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Add a simple watermark in the corner videos.add_computed_column( watermarked=videos.video.overlay_text( 'My Brand', font_size=24, color='white', opacity=0.7, horizontal_align='right', horizontal_margin=20, vertical_align='top', vertical_margin=20, ) ) ```
  Added 1 column value with 0 errors in 1.25 s (0.80 rows/s)
  1 row updated.
### Add YouTube-style captions ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Add a caption with a semi-transparent background box videos.add_computed_column( captioned=videos.video.overlay_text( 'This is a sample caption', font_size=32, color='white', box=True, # Add background box box_color='black', box_opacity=0.8, box_border=[6, 14], # Padding: [top/bottom, left/right] horizontal_align='center', vertical_align='bottom', vertical_margin=70, # Distance from bottom ) ) ```
  Added 1 column value with 0 errors in 1.08 s (0.92 rows/s)
  1 row updated.
### Add dynamic titles from table columns ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Add video title as an overlay (dynamic per video) videos.add_computed_column( titled=videos.video.overlay_text( videos.title, # Use the title column! font_size=48, color='yellow', opacity=1.0, horizontal_align='center', vertical_align='top', vertical_margin=30, ) ) ```
  Added 1 column value with 0 errors in 1.15 s (0.87 rows/s)
  1 row updated.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # View all versions videos.select( videos.title, videos.video, videos.watermarked, videos.captioned, videos.titled, ).collect() ```
### Crop a region from a video Use `video.crop()` to extract a rectangular region from a video. This is useful for focusing on a specific area of interest, removing borders, or preparing clips for object-specific analysis. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Crop using xywh format (default): [x, y, width, height] videos.add_computed_column(cropped=videos.video.crop([100, 50, 320, 240])) # Crop using xyxy format (common in object detection pipelines): # videos.add_computed_column( # cropped_xyxy=videos.video.crop([100, 50, 420, 290], bbox_format='xyxy') # ) ```
  Added 1 column value with 0 errors in 0.56 s (1.78 rows/s)
  1 row updated.
## Explanation **Positioning options:**
**Styling options:**
**Background box options:**
**Requirements:** * FFmpeg must be installed and in PATH ## See also * [Generate thumbnails](/howto/cookbooks/video/video-generate-thumbnails) - Create preview images * [Detect scene changes](/howto/cookbooks/video/video-scene-detection) - Find cuts and transitions # Extract frames from videos Source: https://docs.pixeltable.com/howto/cookbooks/video/video-extract-frames Open in Kaggle  Open in Colab  Download Notebook This documentation page is also available as an interactive notebook. You can launch the notebook in Kaggle or Colab, or download it for use with an IDE or local Jupyter installation, by clicking one of the above links. Pull frames from video files at specified intervals for analysis, thumbnails, or training data. This notebook uses `pxt.create_table()` so you can run cells without a project. An app puts the same columns on a `TableModel` in `app.py` and creates the tables with `pxt schema update`. ## Problem You have video files and need to extract frames for: * Object detection on video content * Creating thumbnails or previews * Building training datasets * Scene analysis and classification
## Solution **What’s in this recipe:** * Extract frames at a fixed rate (FPS) * Extract a specific number of frames * Extract only keyframes for efficiency You create a view with a `frame_iterator` that automatically extracts frames from each video. New videos are processed without extra code. ### Setup ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} %pip install -qU pixeltable ``` ### Load videos ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} import pixeltable as pxt from pixeltable.functions.video import frame_iterator # Create a fresh directory pxt.drop_dir('video_demo', force=True) pxt.create_dir('video_demo') ```
  Connected to Pixeltable database at: postgresql+psycopg://postgres:@/pixeltable?host=/Users/pjlb/.pixeltable/pgdata
  Created directory 'video\_demo'.
  \
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Create table for videos videos = pxt.create_table('video_demo/videos', {'video': pxt.Video}) ```
  Created table 'videos'.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Insert a sample video videos.insert( [ { 'video': 'https://raw.githubusercontent.com/pixeltable/pixeltable/main/docs/resources/bangkok.mp4' } ] ) ```
  Inserting rows into \`videos\`: 1 rows \[00:00, 212.90 rows/s]
  Inserted 1 row with 0 errors.
  1 row inserted, 2 values computed.
### Extract frames at fixed rate Create a view that extracts 1 frame per second: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Extract 1 frame per second frames = pxt.create_view( 'video_demo/frames', videos, iterator=frame_iterator( videos.video, fps=1.0, # 1 frame per second ), ) ```
  Inserting rows into \`frames\`: 19 rows \[00:00, 8687.65 rows/s]
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # View extracted frames frames.select(frames.frame, frames.pos).head(3) ```
### Extract keyframes only For faster processing, extract only keyframes (I-frames): ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Extract only keyframes (much faster for long videos) keyframes = pxt.create_view( 'video_demo/keyframes', videos, iterator=frame_iterator(videos.video, keyframes_only=True), ) keyframes.select(keyframes.frame).head(3) ```
  Inserting rows into \`keyframes\`: 7 rows \[00:00, 3277.53 rows/s]
## Explanation **Extraction options:**
Only one of `fps`, `num_frames`, or `keyframes_only` can be specified. **When to use keyframes:** * Quick video scanning and thumbnails * Initial content classification * Processing very long videos **Frame metadata:** Each frame includes: * `frame`: The extracted image * `pos`: Frame position in the video * `pts`: Presentation timestamp ## See also * [Iterators documentation](/platform/iterators) * [Analyze images in batch](/howto/cookbooks/images/vision-batch-analysis) # Generate videos with AI Source: https://docs.pixeltable.com/howto/cookbooks/video/video-generate-ai Open in Kaggle  Open in Colab  Download Notebook This documentation page is also available as an interactive notebook. You can launch the notebook in Kaggle or Colab, or download it for use with an IDE or local Jupyter installation, by clicking one of the above links. Create videos from text prompts or animate images using Google’s Veo model. ## Problem You need to generate video content programmatically—for social media, product demos, or creative applications.
## Solution **What’s in this recipe:** * Generate videos from text prompts * Animate existing images into videos * Store prompts and generated videos together Use Google’s Veo model to generate videos. Videos are cached—regeneration only happens if the prompt changes. ### Setup ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} %pip install -qU pixeltable google-genai ``` ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} import getpass import os if 'GEMINI_API_KEY' not in os.environ: os.environ['GEMINI_API_KEY'] = getpass.getpass( 'Google AI Studio API Key: ' ) ``` ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} import pixeltable as pxt from pixeltable.functions import gemini # Create a fresh directory pxt.drop_dir('video_gen_demo', force=True) pxt.create_dir('video_gen_demo') ```
  Connected to Pixeltable database at: postgresql+psycopg://postgres:@/pixeltable?host=/Users/pjlb/.pixeltable/pgdata
  Created directory 'video\_gen\_demo'.
  \
### Generate videos from text prompts ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Create a table for text-to-video generation videos = pxt.create_table( 'video_gen_demo/text_to_video', {'prompt': pxt.String} ) # Add computed column that generates videos videos.add_computed_column( video=gemini.generate_videos( videos.prompt, model='veo-3.1-fast-generate-preview' ) ) ```
  Created table 'text\_to\_video'.
  Added 0 column values with 0 errors.
  No rows affected.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Generate a video from a text prompt videos.insert( [ { 'prompt': 'A serene mountain lake at sunrise with mist rising from the water' } ] ) # View the result videos.select(videos.prompt, videos.video).collect() ```
  Inserting rows into \`text\_to\_video\`: 1 rows \[00:00, 190.68 rows/s]
  Inserted 1 row with 0 errors.
### Animate images into videos ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Create a table for image-to-video generation animated = pxt.create_table( 'video_gen_demo/image_to_video', {'image': pxt.Image, 'description': pxt.String}, ) # Add computed column that animates images animated.add_computed_column( video=gemini.generate_videos( image=animated.image, model='veo-3.1-fast-generate-preview' ) ) ```
  Created table 'image\_to\_video'.
  Added 0 column values with 0 errors.
  No rows affected.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Animate an image base_url = 'https://raw.githubusercontent.com/pixeltable/pixeltable/main/docs/resources/images' animated.insert( [ { 'image': f'{base_url}/000000000030.jpg', 'description': 'Beach scene', } ] ) # View the animated result animated.select(animated.image, animated.video).collect() ```
  Inserting rows into \`image\_to\_video\`: 1 rows \[00:00, 291.88 rows/s]
  Inserted 1 row with 0 errors.
## Explanation **Generation modes:**
**Veo model options:**
**Tips:** * Prompts work best when descriptive and specific * Generated videos are cached - same prompt returns cached result * Image-to-video preserves the composition of the input image * New rows automatically generate videos on insert **Requirements:** * Google AI Studio API key (set `GEMINI_API_KEY`) * `pip install google-genai` ## See also * [Extract frames from videos](/howto/cookbooks/video/video-extract-frames) - Pull frames from generated videos * [Add text overlays](/howto/cookbooks/video/video-add-text-overlay) - Add captions to videos # Generate thumbnails from videos Source: https://docs.pixeltable.com/howto/cookbooks/video/video-generate-thumbnails Open in Kaggle  Open in Colab  Download Notebook This documentation page is also available as an interactive notebook. You can launch the notebook in Kaggle or Colab, or download it for use with an IDE or local Jupyter installation, by clicking one of the above links. Automatically create preview thumbnails from video files at specific timestamps or intervals. ## Problem You have video files that need preview thumbnails for galleries, search results, or video players. Manually extracting frames doesn’t scale.
## Solution **What’s in this recipe:** * Extract thumbnail at a specific timestamp * Generate multiple thumbnails per video * Resize thumbnails to standard dimensions You add computed columns that extract frames from videos. Thumbnails are generated automatically when you insert new videos. ### Setup ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} %pip install -qU pixeltable ``` ### Load videos ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} import pixeltable as pxt import pixeltable.functions as pxtf # Create a fresh directory pxt.drop_dir('thumbnail_demo', force=True) pxt.create_dir('thumbnail_demo') ```
  Connected to Pixeltable database at: postgresql+psycopg://postgres:@/pixeltable?host=/Users/pjlb/.pixeltable/pgdata
  Created directory 'thumbnail\_demo'.
  \
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Create table for videos videos = pxt.create_table('thumbnail_demo/videos', {'video': pxt.Video}) ```
  Created table 'videos'.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Insert sample videos from public S3 bucket s3_prefix = 's3://multimedia-commons/' video_paths = [ 'data/videos/mp4/ffe/ffb/ffeffbef41bbc269810b2a1a888de.mp4', 'data/videos/mp4/ffe/feb/ffefebb41485539f964760e6115fbc44.mp4', ] videos.insert([{'video': s3_prefix + path} for path in video_paths]) ```
  Inserting rows into \`videos\`: 2 rows \[00:00, 382.20 rows/s]
  Inserted 2 rows with 0 errors.
  2 rows inserted, 4 values computed.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # View videos videos.collect() ```
### Extract thumbnail at timestamp Extract a single frame at a specific time (e.g., 1 second into the video): ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Extract frame at 1 second as thumbnail videos.add_computed_column( thumbnail=pxtf.video.extract_frame(videos.video, timestamp=1.0) ) ```
  Added 2 column values with 0 errors.
  2 rows updated, 2 values computed.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # View thumbnails videos.select(videos.video, videos.thumbnail).collect() ```
### Resize thumbnails Create standard-sized thumbnails for consistent display: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Resize thumbnail to 320x180 (16:9 aspect ratio) videos.add_computed_column( thumbnail_small=videos.thumbnail.resize((320, 180)) ) ```
  Added 2 column values with 0 errors.
  2 rows updated, 2 values computed.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # View resized thumbnails with dimensions videos.select( videos.thumbnail_small, videos.thumbnail_small.width, videos.thumbnail_small.height, ).collect() ```
### Multiple thumbnails with `frame_iterator` For preview strips or timeline thumbnails, use `frame_iterator` to extract multiple frames: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Create a view with frames extracted at 0.5 fps (one frame every 2 seconds) frames = pxt.create_view( 'thumbnail_demo/frames', videos, iterator=pxtf.video.frame_iterator(videos.video, fps=0.5), ) ```
  Inserting rows into \`frames\`: 17 rows \[00:00, 9736.88 rows/s]
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # View extracted frames (multiple per video) frames.select(frames.frame, frames.pos).head(10) ```
## Explanation **Thumbnail extraction methods:**
**Common thumbnail sizes:**
## See also * [Extract frames from videos](/howto/cookbooks/video/video-extract-frames) - Detailed frame extraction guide * [Load media from S3](/howto/cookbooks/data/data-import-s3) - Import videos from cloud storage * [Transform images with PIL](/howto/cookbooks/images/img-pil-transforms) - Resize and crop images # Create a video slideshow from images Source: https://docs.pixeltable.com/howto/cookbooks/video/video-image-slideshow Open in Kaggle  Open in Colab  Download Notebook This documentation page is also available as an interactive notebook. You can launch the notebook in Kaggle or Colab, or download it for use with an IDE or local Jupyter installation, by clicking one of the above links. Turn a collection of images into an animated video with Ken Burns effects, text overlays, logos, and background music — using a fully declarative Pixeltable pipeline. ## Problem You have a set of images and want to produce a polished video — an ad, a product reel, a social media clip. Typically this means a video editor or a complex ffmpeg scripting pipeline.
## Solution **What’s in this recipe:** * Convert still images into animated video clips with pan effects * Control pan direction per image from table data (no Python loops) * Add per-image captions and a logo overlay * Concatenate all clips and add background music The key insight: store per-clip metadata (caption text, pan direction, logo) as **table columns**. One chained computed column expression handles the entire pipeline, and Pixeltable evaluates it per row. ### Setup ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} %pip install -qU pixeltable ```
  Note: you may need to restart the kernel to use updated packages.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} import pixeltable as pxt from pixeltable.functions.video import concat_videos_agg, with_audio pxt.drop_dir('slideshow_demo', force=True) pxt.create_dir('slideshow_demo') ```
  Connected to Pixeltable database at: postgresql+psycopg://postgres:@/pixeltable?host=/Users/pjlb/.pixeltable/pgdata
  Pixeltable dashboard available at: [http://localhost:22089](http://localhost:22089)
  Created directory 'slideshow\_demo'.
  \
## Step 1: Define the data Each row is one clip in the final video. Per-clip variation comes from table columns: * `caption`: text overlay for each clip * `pan_sign`: `+1.0` for pan-right, `-1.0` for pan-left * `logo`: image to overlay in the corner This is what makes the pipeline fully declarative — no Python loops, no conditional logic. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} LOGO_URL = 'https://raw.githubusercontent.com/pixeltable/pixeltable/main/docs/resources/pixeltable-logo-large.png' t = pxt.create_table( 'slideshow_demo/clips', { 'image': pxt.Image, 'seq': pxt.Int, 'caption': pxt.String, 'logo': pxt.Image, 'pan_sign': pxt.Int, }, ) t.insert( [ { 'image': 'https://images.unsplash.com/photo-1506744038136-46273834b3fb?w=1920&q=80', 'seq': 0, 'caption': 'DISCOVER NATURE', 'logo': LOGO_URL, 'pan_sign': 1, }, { 'image': 'https://images.unsplash.com/photo-1470071459604-3b5ec3a7fe05?w=1920&q=80', 'seq': 1, 'caption': 'WILD FORESTS', 'logo': LOGO_URL, 'pan_sign': -1, }, { 'image': 'https://images.unsplash.com/photo-1441974231531-c6227db76b6e?w=1920&q=80', 'seq': 2, 'caption': 'SUNLIT CANOPY', 'logo': LOGO_URL, 'pan_sign': 1, }, { 'image': 'https://images.unsplash.com/photo-1507525428034-b723cf961d3e?w=1920&q=80', 'seq': 3, 'caption': 'OCEAN BREEZE', 'logo': LOGO_URL, 'pan_sign': -1, }, { 'image': 'https://images.unsplash.com/photo-1519681393784-d120267933ba?w=1920&q=80', 'seq': 4, 'caption': 'EXPLORE MORE', 'logo': LOGO_URL, 'pan_sign': 1, }, ] ) ```
  Inserted 5 rows with 0 errors in 1.04 s (4.81 rows/s)
  5 rows inserted.
## Step 2: Build the video pipeline One computed column chains the entire transformation:
  image → static video → resize → pan effect → resize → logo overlay → text overlay
`pan()` is a convenience wrapper around `scroll()` that computes viewport size, start position, and speed automatically. It accepts column expressions for per-row direction: * `pan_sign = +1` → pans right * `pan_sign = -1` → pans left For lower-level control (custom speed, diagonal pans, asymmetric crops), use `scroll()` directly. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} W, H, DUR, CROP = 1280, 720, 4.0, 0.25 # Base: still image → video → uniform resolution t.add_computed_column( base=t.image.to_video(duration=DUR).resize(width=W, height=H) ) # Full pipeline: pan → resize → logo → caption # pan() accepts column expressions for per-row direction t.add_computed_column( clip=t.base.pan(x_sign=t.pan_sign, crop_pct=CROP) .resize(width=W, height=H) .overlay_image( t.logo, scale=0.10, opacity=0.85, horizontal_align='right', vertical_align='top', horizontal_margin=15, vertical_margin=15, ) .overlay_text( t.caption, font_size=44, color='white', horizontal_align='center', vertical_align='bottom', vertical_margin=50, box=True, box_color='black', box_opacity=0.5, box_border=[8, 16], start_time=0.5, end_time=3.0, ) ) ```
  Added 5 column values with 0 errors in 6.43 s (0.78 rows/s)
  5 rows updated.
## Step 3: Preview individual clips Each row now has a fully rendered video clip. Let’s inspect them. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} t.select( t.seq, t.caption, t.pan_sign, dur=t.clip.get_duration() ).order_by(t.seq).collect() ```
## Step 4: Concatenate into final video `concat_videos_agg` merges all clips in `seq` order into a single video. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} video_path = ( t.group_by() .select(v=concat_videos_agg(t.seq, t.clip)) .collect()[0]['v'] ) ``` ## Step 5: Add background music `with_audio()` replaces (or adds) the audio track on a video. The audio is trimmed to match the video duration automatically. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} MUSIC_URL = 'https://raw.githubusercontent.com/pixeltable/pixeltable/main/docs/resources/sample-background-music.m4a' final = pxt.create_table( 'slideshow_demo/final', {'video': pxt.Video, 'music': pxt.Audio} ) final.insert([{'video': video_path, 'music': MUSIC_URL}]) final.add_computed_column(out=with_audio(final.video, final.music)) final.select(final.out).collect() ``` ## How it works The entire pipeline is declarative — per-clip variation comes from **data**, not code:
One computed column expression handles the full transformation chain. Pixeltable evaluates it per row, pulling caption, logo, and pan direction from each row’s data. ### Alternative effects Swap the pan for other built-in effects: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Zoom instead of pan t.add_computed_column(clip=t.base.zoom(start_scale=1.0, end_scale=1.3)) # Fade-through-black transitions (add to each clip before concat) t.add_computed_column(clip=t.base.fade_in(duration=0.5).fade_out(duration=0.5)) # Combine: pan + fade t.add_computed_column( clip=t.base.pan(x_sign=1) .resize(width=1280, height=720) .fade_in(duration=0.5) .fade_out(duration=0.5) ) ``` ### Audio This recipe uses `with_audio()` to replace the soundtrack. To **blend** a second audio track into a video that already has audio (e.g. add background music under narration), use `mix_audio()`: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} t.video.mix_audio(t.music, audio_volume=0.3, original_volume=1.0) ``` # Detect scene changes in videos Source: https://docs.pixeltable.com/howto/cookbooks/video/video-scene-detection Open in Kaggle  Open in Colab  Download Notebook This documentation page is also available as an interactive notebook. You can launch the notebook in Kaggle or Colab, or download it for use with an IDE or local Jupyter installation, by clicking one of the above links. Automatically find scene cuts, transitions, and fades in video files. ## Problem You have video files and need to identify scene boundaries for:
## Solution **What’s in this recipe:** * Detect hard cuts with `scene_detect_content()` * Find fade transitions with `scene_detect_threshold()` * Use adaptive detection with `scene_detect_adaptive()` Three built-in detection methods handle different transition types using PySceneDetect. ### Setup ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} %pip install -qU pixeltable scenedetect opencv-python ``` ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} import pixeltable as pxt # Create a fresh directory pxt.drop_dir('scene_demo', force=True) pxt.create_dir('scene_demo') ```
  Connected to Pixeltable database at: postgresql+psycopg://postgres:@/pixeltable?host=/Users/pjlb/.pixeltable/pgdata
  Created directory 'scene\_demo'.
  \
### Load sample videos ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Create a video table videos = pxt.create_table( 'scene_demo/videos', {'video': pxt.Video, 'title': pxt.String} ) # Insert sample videos from S3 videos.insert( [ { 'video': 's3://multimedia-commons/data/videos/mp4/ffe/ffb/ffeffbef41bbc269810b2a1a888de.mp4', 'title': 'Sample video 1', } ] ) ```
  Created table 'videos'.
  Inserting rows into \`videos\`: 1 rows \[00:00, 200.53 rows/s]
  Inserted 1 row with 0 errors.
  1 row inserted, 3 values computed.
### Detect scenes with content-based detection ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Detect scenes using content-based detection (best for hard cuts) videos.add_computed_column( scenes_content=videos.video.scene_detect_content( threshold=27.0, # Lower = more sensitive min_scene_len=15, # Minimum frames between cuts ) ) # View detected scenes videos.select(videos.title, videos.scenes_content).collect() ```
  Added 1 column value with 0 errors.
### Detect fade transitions ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Detect fade-to-black/white transitions videos.add_computed_column( scenes_fade=videos.video.scene_detect_threshold( threshold=12.0, # Brightness threshold for fades min_scene_len=15, ) ) # View fade-detected scenes videos.select(videos.title, videos.scenes_fade).collect() ```
  Added 1 column value with 0 errors.
### Adaptive detection for complex videos ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Adaptive detection adjusts to video content dynamically videos.add_computed_column( scenes_adaptive=videos.video.scene_detect_adaptive( adaptive_threshold=3.0, # Lower = more scenes detected min_scene_len=15, fps=2.0, # Analyze at 2 FPS for speed ) ) # View adaptively-detected scenes videos.select(videos.title, videos.scenes_adaptive).collect() ```
  Added 1 column value with 0 errors.
## Explanation **Detection methods:**
**Output format:** Each method returns a list of scene dictionaries: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} { 'start_time': 5.2, # Scene start in seconds 'start_pts': 156, # Presentation timestamp 'duration': 3.8 # Scene duration in seconds } ``` **Tuning tips:**
## See also * [Extract frames from videos](/howto/cookbooks/video/video-extract-frames) - Get frames at scene boundaries * [Generate thumbnails](/howto/cookbooks/video/video-generate-thumbnails) - Create preview images # Infrastructure Source: https://docs.pixeltable.com/howto/deployment/infrastructure One application file, PIXELTABLE_HOME, and where media files live. Put `TableModel` classes and `FastAPIRouter` in `app.py`. Put `app.py` in the directory you run `pxt init` in. `pxt init` writes `pixeltable.toml`, or appends `[[tool.pixeltable.database]]` to an existing `pyproject.toml`. `pxt schema update` and `pxt service update` error if you have not run `pxt init` in this directory or a parent. `pixeltable.toml` makes `proj` the root. `app.py` imports as `app`. `from functions import excerpt` works because `functions.py` is a sibling. A nested file (`ads/app.py`) imports as `ads.app`. Every directory name on that path has to be a Python identifier. `pxt init` reports names that are not. A `pyproject.toml` is a project root only if it has `[tool.pixeltable]`. If both files sit in the same directory, `pixeltable.toml` wins. `pxt init` writes one or the other, not both. Nested `pxt init` is refused: one project, one root. Create tables, then start HTTP: ```bash theme={"theme":{"light":"light-plus","dark":"dark-plus"}} pxt init pxt schema update app.py my_app pxt service update app.py my_app ``` `my_app` is a namespace inside Pixeltable, not a folder in this repo. A second file under the same project root can use a different last-argument name: ```bash theme={"theme":{"light":"light-plus","dark":"dark-plus"}} pxt schema update ads/app.py ads pxt schema update docs/app.py docs_app ``` Add another `[[pixeltable.database]]` block for a second database. Leave `name` off for the local database. For Cloud, set `name = "pxt://org:db"` and list the files `pxt db update` should upload: ```toml theme={"theme":{"light":"light-plus","dark":"dark-plus"}} [[pixeltable.database]] [[pixeltable.database]] name = "pxt://org:prod" include = ["app.py", "functions.py"] ``` Same `app.py`, different last argument: ```bash theme={"theme":{"light":"light-plus","dark":"dark-plus"}} pxt schema update app.py my_app pxt schema update app.py pxt://org:prod ``` If `app.py` only declares models and a `FastAPIRouter` for Pixeltable, start HTTP with `pxt service update`, not `python app.py`. If you mount the router on your own FastAPI app, start that app as in [HTTP serving](/howto/deployment/serving). Minimum Python and Pixeltable versions: ```toml theme={"theme":{"light":"light-plus","dark":"dark-plus"}} [project] requires-python = ">=3.11" dependencies = [ "pixeltable>=0.7.4", ] ``` Pixeltable records each UDF by its import path from the project root, so renaming or moving the file breaks computed columns that call it. [Custom functions](/platform/udfs-in-pixeltable). To return computed columns from an insert, see [HTTP serving](/howto/deployment/serving#return-computed-columns). Run `pxt schema update` once before you start HTTP processes. Two apps. Each ships a Dockerfile and docker-compose.yml. ## Where files live Default home is `~/.pixeltable`: `pgdata` (PostgreSQL), `media` (generated files), `file_cache` (downloads). Names like `my_app` are Pixeltable namespaces (you can also create one with `pxt.create_dir`), not folders in your repo. * Inserted local files: the path is stored; the original stays put. * Inserted URLs: the URL is stored; first access downloads into the file cache. * Generated media: local disk, or the Cloud database's home bucket (`pxtfs://org:db/home`). To use another bucket, set `destination=`, `PIXELTABLE_OUTPUT_MEDIA_DEST`, or `db_output_media_dest` in the database entry. File cache size is `file_cache_size_g` in `config.toml` only. [Configuration](/platform/configuration). ```toml theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # ~/.pixeltable/config.toml file_cache_size_g = 50 ``` If you only insert and export, the Pixeltable data directory can be temporary: write the result out with `export_sql`. [Self-hosting](/howto/deployment/overview). A long-running service needs a persistent volume at `PIXELTABLE_HOME` (pgdata, media, file\_cache). Starter-kit Dockerfiles set `PIXELTABLE_HOME=/data/pixeltable`. ```bash theme={"theme":{"light":"light-plus","dark":"dark-plus"}} PIXELTABLE_INPUT_MEDIA_DEST=s3://your-bucket/input PIXELTABLE_OUTPUT_MEDIA_DEST=s3://your-bucket/output ``` Or on the database's entry: ```toml theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # pixeltable.toml [[pixeltable.database]] db_input_media_dest = 's3://your-bucket/input' db_output_media_dest = 's3://your-bucket/output' ``` # Operations Source: https://docs.pixeltable.com/howto/deployment/operations Sync request handlers, one process writing pgdata, on_error, and pxt schema update after you change the file. ## Concurrent requests Use sync (`def`) endpoint handlers, not `async def`. FastAPI puts `def` on a thread pool. Pixeltable gives each thread its own connection. `async def` that calls Pixeltable blocks the event loop. Use `FastAPIRouter` unless you need custom FastAPI handlers. If you write your own, use `def`, not `async def`: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} from pydantic import BaseModel from fastapi import FastAPI import pixeltable as pxt app = FastAPI() class SearchResult(BaseModel): text: str score: float @app.post('/ingest') def ingest(text: str): t = pxt.get_table('myapp.documents') status = t.insert([{'text': text}]) return {'inserted': status.num_rows} @app.get('/search') def search(query: str, limit: int = 10) -> list[SearchResult]: t = pxt.get_table('myapp.documents') sim = t.text.similarity(string=query) results = ( t.order_by(sim, asc=False) .limit(limit) .select(t.text, score=sim) .collect() ) return list(results.to_pydantic(SearchResult)) ``` `.collect()` returns a `ResultSet`; call `to_pydantic()` before FastAPI returns the value. Or `to_pandas().to_dict(orient='records')`. One Python process writes to `~/.pixeltable/pgdata`. Several API workers need a shared volume and still one writer. Two pods on the same `pgdata` corrupt the database. | Limit | What you get | | - | - | | Metadata | One embedded PostgreSQL | | Compute | Several workers, one volume | | Failover | Detach and reattach the volume | If you need more than one process writing the database, use [Pixeltable Cloud](/cloud). ## GPU Local Hugging Face / Ollama models use CUDA when present, otherwise CPU. Restrict devices with `CUDA_VISIBLE_DEVICES`. ## Errors | | Mode | Effect | | - | - | - | | Computed column | `on_error='abort'` (default) | The operation fails | | | `on_error='ignore'` | That row stores `None` plus error metadata | | Media | `media_validation='on_write'` (default) | Invalid media fails the insert | | | `media_validation='on_read'` | Insert succeeds; failure on first access | Read failures with `table.column.errortype` and `table.column.errormsg`. ## Schema after the first `pxt schema update` Adding a column or index computes values only for that new object. Replacing a computed column recomputes it. Dropping a column deletes its data. Change the class in `app.py` and run `pxt schema update` again. Notebooks: [Iterative workflow cookbook](/howto/cookbooks/core/dev-iterative-workflow). `table.revert()` undoes the last change on that table. [Version control](/platform/version-control) for what counts as one change. ## Run it Each starter-kit app ships a `Dockerfile` and `docker-compose.yml`. In the container, run `pxt schema update app.py agent -f` once (`-f` skips the yes/no prompt), then start HTTP. Pass `agent` or `videointel` as the last argument, matching the starter app you copied. `pxt service run` starts HTTP in this container's foreground. Locally you usually run `pxt service update` instead: ```dockerfile theme={"theme":{"light":"light-plus","dark":"dark-plus"}} ENV PIXELTABLE_HOME=/data/pixeltable CMD sh -c "uv run pxt schema update app.py agent -f \ && uv run pxt service run app.py agent --host 0.0.0.0 --port 8000" ``` Use `pxt.create_dir(...)` for per-user namespaces in one database. Use separate containers if each tenant must not share `pgdata`. Only one process writes `~/.pixeltable/pgdata`. Do not mount the same `pgdata` on two pods. ## Monitoring Insert/compute traces: `pip install 'pixeltable[otel]'`, then [Observability](/platform/observability). ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} import logging import time import pixeltable as pxt logger = logging.getLogger(__name__) @pxt.udf def process_video(video: pxt.Video) -> pxt.Json: start = time.time() try: result = {'processed': True} logger.info('Processed in %.2fs', time.time() - start) return result except Exception as e: logger.error('Processing failed: %s', e) raise ``` ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} from pixeltable.func import Batch @pxt.udf(batch_size=32) def embed_batch(texts: Batch[str]) -> Batch[list[float]]: return model.encode(texts) ``` Provider limits in `config.toml`. [Configuration](/platform/configuration#rate-limit-configuration). ```toml theme={"theme":{"light":"light-plus","dark":"dark-plus"}} [openai.rate_limits] gpt-4o = 500 ``` Throttle a custom endpoint with `resource_pool`. Default is 600 requests per minute. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf(resource_pool='request-rate:my_service') async def call_custom_api(prompt: str) -> dict: return await custom_api_call(prompt) ``` ## Troubleshooting Development reset (deletes data): ```bash theme={"theme":{"light":"light-plus","dark":"dark-plus"}} rm -rf ~/.pixeltable/pgdata \ ~/.pixeltable/media \ ~/.pixeltable/file_cache ``` | Symptom | What to do | | - | - | | Cannot connect | If no process is running, remove `~/.pixeltable/pgdata/postmaster.pid` | | Slow first query | First access downloads into the file cache | | Table not found | `pxt.list_tables()`; check the directory name | | OOM on large media | Use [iterators](/platform/iterators) | # Self-hosting Source: https://docs.pixeltable.com/howto/deployment/overview Choose what runs locally: Python only, your own FastAPI app, or pxt service. Hosted catalog: [Cloud](/cloud). This page is which local process to start after `pxt schema update`. | If you want | What you run | What starts | | - | - | - | | Python only | `pxt schema update`, `bind_all()`, insert from Python, then `pxt.io.export_sql()` | Nothing listening | | Your FastAPI | `pxt schema update`, then `app.include_router(...)` | Your process. [HTTP serving](/howto/deployment/serving) | | `pxt service` | `pxt schema update`, then `pxt service update` | A process Pixeltable starts. [Quickstart](/overview/quick-start) | `uvx pixeltable-new myapp` copies the chat agent; pass `agent` as the last argument to `pxt schema update`. `--video` copies video search; pass `videointel`. ## Python only This example expects the starter-kit chat agent (`Knowledge` in that repo's `app.py`). Insert rows, then export them to SQLite. Same snippet: [chat-agent README](https://github.com/pixeltable/pixeltable-starter-kit/blob/main/chat-agent/README.md). ```bash theme={"theme":{"light":"light-plus","dark":"dark-plus"}} pxt schema update app.py agent ``` Importing `app.py` declares the models; it does not attach them to the tables `pxt schema update` created. Call `bind_all()` with the same last argument first, or the model raises `NOT_BOUND`. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} import pixeltable as pxt from app import Knowledge, TableModel TableModel.bind_all('agent') Knowledge.insert( [{'body': 'One application file. Insert runs compute.', 'title': 'intro', 'source': 'docs'}] ) pxt.io.export_sql( Knowledge.select(Knowledge.title, Knowledge.body, Knowledge.uuid), 'knowledge', db_connect_str='sqlite:///agent.db', if_exists='replace', ) ``` For Docker and disks, see [Infrastructure](/howto/deployment/infrastructure) and [Operations](/howto/deployment/operations). ## Next Insert, compute, update, delete, query, uploads, background jobs. Use the same `app.py` against `pxt://org:main`. # Security and backup Source: https://docs.pixeltable.com/howto/deployment/security pg_dump the catalog, restore, keep secrets out of app.py. Hosted databases are backed up by Pixeltable Cloud. For a local database, back up `~/.pixeltable/pgdata` and `~/.pixeltable/media`. | Setup | Backup | Restore | | - | - | - | | No HTTP | If you exported with `export_sql`, keep that SQL copy and `app.py` in git | Run `pxt schema update` again, then insert the data again | | Long-running service | `pg_dump` of `~/.pixeltable/pgdata` plus `~/.pixeltable/media/` (or bucket versioning) | Restore `pgdata` and media, restart | ```bash theme={"theme":{"light":"light-plus","dark":"dark-plus"}} pg_dump -h ~/.pixeltable/pgdata -U postgres pixeltable > backup.sql tar -czf media_backup.tar.gz ~/.pixeltable/media/ ``` Stop Pixeltable, restore Postgres from the dump, copy `media` back, then start Pixeltable again. Pixeltable does not manage end-user accounts. Put auth on your HTTP layer. Do not expose PostgreSQL port 5432. Do not put long-lived cloud keys in `config.toml` if the host can use its own IAM role. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} import os OPENAI_API_KEY = os.getenv('OPENAI_API_KEY') ``` On Cloud, store secrets with `pxt secret set` ([CLI](/platform/cli#pxt-secret)). Table history: [Version control](/platform/version-control). # HTTP serving Source: https://docs.pixeltable.com/howto/deployment/serving HTTP routes for insert, compute, update, delete, and query, plus uploads, background jobs, and SqlExport. Install FastAPI and uvicorn first: ```bash theme={"theme":{"light":"light-plus","dark":"dark-plus"}} pip install 'pixeltable[serve]' ``` How the process starts is on [Self-hosting](/howto/deployment/overview). This page covers the route API once you have chosen which process serves it. A local service exposes FastAPI's interactive docs at `/docs` and its OpenAPI schema at `/openapi.json`. `pxt service update` assigns a local port; look up the endpoint by catalog path and service name rather than assuming port 8000. For the README's `my_app/ingest` example: ```bash theme={"theme":{"light":"light-plus","dark":"dark-plus"}} SERVICE_URL=$(pxt service list my_app --json | jq -r '.[] | select(.catalog_path == "my_app" and .name == "ingest") | .endpoint') curl -fsS "${SERVICE_URL%/}/openapi.json" -o openapi.json npx openapi-typescript openapi.json -o src/pixeltable.d.ts ``` Replace `my_app` and `ingest` when using another target or service. Each column named in a route's `outputs` becomes a typed field, computed columns included. Regenerate types after changing the Python routes. For a hosted service, copy its full endpoint URL from the Cloud dashboard or find it with `pxt service list pxt://org:main --json` using a credential that can list services. A key scoped only to invoke a service cannot list services, but can download that service's schema when granted access. The gateway requires a credential to download the schema: ```bash theme={"theme":{"light":"light-plus","dark":"dark-plus"}} curl -fsS -H "X-api-key: $PIXELTABLE_API_KEY" "${SERVICE_URL%/}/openapi.json" -o openapi.json npx openapi-typescript openapi.json -o src/pixeltable.d.ts ``` The hosted schema declares the gateway's `X-api-key` header on every route except `/health`; local service schemas declare no key. Cloud hosts the same `app.py`. Calls send `X-api-key`. If a request also sends `Authorization: Bearer`, Cloud authenticates the API key. `pxt service run` only starts endpoints in your local terminal. After `pxt secret set`, run `pxt db restart` and `pxt service restart` so running processes read the new value. [Pixeltable Cloud](/cloud). In the Cloud dashboard, expand **API docs** on the service detail page to inspect and try routes as your signed-in user, without an API key. Trying an insert, update, or delete route can change data. Routes with `return_fileresponse=True` advertise a binary response for any media type. Check the response's `Content-Type` when handling the returned image, video, audio, or document. ## Call from a TypeScript app server Keep a Cloud API key in your application server's environment, never in browser code. Generated types do not check raw `fetch` calls, so call routes through `openapi-fetch` with the generated `paths`. The same module calls a local service (no key) or a hosted one. Set `PIXELTABLE_SERVICE_URL` to the full service endpoint, as above, run `npm install openapi-fetch server-only`, and put this in `src/lib/pixeltable-client.ts`. With the README's `/titles` compute route: ```typescript theme={"theme":{"light":"light-plus","dark":"dark-plus"}} import 'server-only'; import createClient from 'openapi-fetch'; import type { paths } from '../pixeltable'; const apiKey = process.env.PIXELTABLE_API_KEY; export const pixeltable = createClient({ baseUrl: process.env.PIXELTABLE_SERVICE_URL, headers: apiKey ? { 'X-api-key': apiKey } : {}, cache: 'no-store', }); export class PixeltableError extends Error { status: number; body: unknown; constructor(status: number, body: unknown) { super(`Pixeltable service returned HTTP ${status}`); this.status = status; this.body = body; } } export async function computeTitle(title: string) { const { data, error, response } = await pixeltable.POST('/titles', { body: { title } }); if (error !== undefined || !response.ok) throw new PixeltableError(response.status, error); return data; // typed from the service schema } ``` Call it from a Server Component, or from your own Route Handler or Server Action; protect any that expose writes to browser users with your application's authorization checks. Regenerate `src/pixeltable.d.ts` when routes change, and run your TypeScript check so request and response mismatches fail before deployment. The Cloud dashboard's **Use this endpoint** TypeScript and JavaScript snippets are server-side examples. A `return_fileresponse=True` route needs `parseAs: 'blob'`, or `parseAs: 'stream'` to pipe `response.body` through your Route Handler with the upstream `Content-Type`; without it the client parses the file as JSON and throws. Generated types describe upload fields as strings, so send an upload with `fetch`, a `FormData` body, and the same `X-api-key` header, and let `fetch` set the multipart boundary. `PixeltableError.body` is the parsed error response. Its `detail` is an object with `error_code`, `message`, and `retryable` (sometimes `retry_after`) for a Pixeltable runtime error and for an error the Cloud gateway answers itself: 401 for a missing or invalid key, 403 for a key without access to the service, 404 for an unknown service, 429 when the key is rate limited, and 503 when the service is not running. It is a string for a missing row and an array for a request-validation error, so branch on its shape, not the status. Retry a write only when `detail.retryable` is true, after `retry_after` seconds when present. ## Mount on your own FastAPI app ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} import fastapi import uvicorn import pixeltable as pxt from pixeltable.serving import FastAPIRouter t = pxt.get_table('my_app.docs') app = fastapi.FastAPI() router = FastAPIRouter() router.add_insert_route( t, path='/insert', inputs=['prompt'], outputs=['prompt', 'result'], ) router.add_update_route( t, path='/update', inputs=['prompt'], outputs=['id', 'prompt', 'result'], ) app.include_router(router) uvicorn.run(app, host='0.0.0.0', port=8000) ``` `@pxt.query` runs the function body when the file is imported. A query over a `TableModel` stays unbound until the models are bound, so put it in the same `app.py` as the model, before `pxt schema update`. Use a plain FastAPI `@app.post()` when one `FastAPIRouter` helper cannot express the request. ## Compute without inserting [`Table.compute()`](/sdk/latest/table#method-compute) runs computed columns and returns the values without writing a row. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} rows = t.compute([{'prompt': 'hello'}]) print(rows[0]['result']) ``` Same over HTTP: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} router.add_compute_route( t, path='/preview', inputs=['prompt'], outputs=['prompt', 'result'], ) ``` ## Decorator-style routes `add_insert_route()` builds the response model from the column schema. To return a custom JSON body, use `@router.insert_route` instead of `add_insert_route()`. The function receives `outputs` as keyword arguments and returns a `pydantic.BaseModel`. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} import pydantic from pixeltable.serving import FastAPIRouter router = FastAPIRouter() class GenerateResponse(pydantic.BaseModel): caption: str score: float @router.insert_route( t, path='/generate', inputs=['prompt'], outputs=['caption', 'score'], ) def format_insert( *, caption: str, score: float ) -> GenerateResponse: return GenerateResponse( caption=caption.strip(), score=round(score, 3) ) @router.update_route( t, path='/update', inputs=['prompt'], outputs=['id', 'caption', 'score'], ) def format_update( *, id: int, caption: str, score: float ) -> GenerateResponse: return GenerateResponse( caption=caption.strip(), score=round(score, 3) ) ``` At registration: * Every parameter is keyword-only and annotated. * Parameter names match `outputs` exactly. * Annotations match column types (nullable column: `T | None`). Media columns arrive as URL strings: annotate `str`. * Return type is a `pydantic.BaseModel` subclass. `background=True` works the same as the non-decorator forms. Decorator routes are Python-only. ## Export to an external database `SqlExport` writes each successful insert or update to another SQL table. Pixeltable commits first; then the external write. If the external write fails, the request is HTTP 500. No rollback. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} from pixeltable.serving import FastAPIRouter, SqlExport router = FastAPIRouter() router.add_insert_route( t, path='/generate', inputs=['prompt'], outputs=['prompt', 'result'], export_sql=SqlExport( db_connect='postgresql+psycopg://user:pw@host/analytics', table='generations', ), ) ``` The row is the response body (`outputs`). Media columns are URL strings. The target table must already exist. `SqlExport.method`: * `'insert'` (default): append. Replaying the request duplicates the row. * `'update'`: match on the target primary key. Not an upsert. No match: HTTP 500. Response columns must include every target PK plus at least one non-PK. * `'merge'`: not supported. A Pixeltable insert with `method='update'` is allowed: append-only here, current-state there. `export_sql=` cannot combine with `return_fileresponse=True`. It works with `background=True` (the SQL write runs in the worker). [`SqlExport`](https://docs.pixeltable.com/sdk/latest/pixeltable/serving/SqlExport) A connection string with an embedded password is plaintext in the application file. Pull credentials from the environment, a `.pgpass`-style file, or `pxt secret set`. ## Full example ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # app.py import pixeltable as pxt from pixeltable.serving import FastAPIRouter TableModel = pxt.model_base() class Images(TableModel, name='images'): image: pxt.Image width: pxt.Int height: pxt.Int tag: pxt.String | None thumbnail = image.resize(size=(128, 128)) @pxt.query def search_images(text: str) -> pxt.Query: return Images.where(Images.tag == text).select(Images.thumbnail) images = FastAPIRouter(name='image-processing') images.add_insert_route( Images, path='/process', inputs=[Images.width, Images.height], uploadfile_inputs=['image'], outputs=[Images.thumbnail], ) images.add_insert_route(Images, path='/ingest', background=True) images.add_update_route( Images, path='/images/update', inputs=[Images.tag], outputs=[Images.thumbnail], ) images.add_delete_route(Images, path='/images/delete') images.add_delete_route( Images, path='/images/delete-by-tag', match_columns=['tag'] ) images.add_query_route(path='/search', query=search_images) images.add_query_route( path='/thumbnail', query=search_images, one_row=True, method='get', return_fileresponse=True, ) ``` ```bash theme={"theme":{"light":"light-plus","dark":"dark-plus"}} pxt schema update app.py images pxt service update app.py images ``` ## Return computed columns `insert()`, `update()`, and `batch_update()` can return computed columns without a follow-up query: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} status = table.insert([row], return_rows=True) data = status.rows[0] ``` `status.rows` is a list of dicts. For typed access, `model_validate()` with `extra="ignore"`: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} from pydantic import BaseModel class AgentResult(BaseModel): model_config = {'extra': 'ignore'} answer: str | None = None status = agent_table.insert( [{'prompt': user_input}], return_rows=True ) result = AgentResult.model_validate(status.rows[0]) ``` After `.collect()`, use `to_pydantic()`. After `return_rows=True`, use `model_validate()`. ## Background jobs `background=True` returns a job handle immediately: ```json theme={"theme":{"light":"light-plus","dark":"dark-plus"}} { "id": "abc123", "job_url": "http://127.0.0.1:/_pxt/jobs/abc123" } ``` Poll `job_url` (`pxt service list` prints the base URL): ```bash theme={"theme":{"light":"light-plus","dark":"dark-plus"}} curl http://127.0.0.1:/_pxt/jobs/abc123 # {"status": "pending"} # {"status": "done", "result": {...}} # {"status": "error", "error": "...", "error_detail": {...}} ``` Poll through your application server with the same Cloud key when hosted. Cloud returns an HTTPS `job_url`. The polling path is in the generated schema, so polling is a typed call on the same [TypeScript client](#call-from-a-typescript-app-server): `pixeltable.GET('/_pxt/jobs/{job_id}', { params: { path: { job_id: id } } })`. A failed job's `error_detail` has the same shape as an HTTP error's `detail`, so the same retry rule applies. `background` cannot combine with `return_fileresponse`. Flags: [CLI](/platform/cli). # Working with Anthropic in Pixeltable Source: https://docs.pixeltable.com/howto/providers/working-with-anthropic Open in Kaggle  Open in Colab  Download Notebook This documentation page is also available as an interactive notebook. You can launch the notebook in Kaggle or Colab, or download it for use with an IDE or local Jupyter installation, by clicking one of the above links. Pixeltable’s Anthropic integration enables you to access Anthropic’s Claude LLM via the Anthropic API. ### Prerequisites * An Anthropic account with an API key ([https://docs.anthropic.com/en/api/getting-started](https://docs.anthropic.com/en/api/getting-started)) ### Important notes * Anthropic usage may incur costs based on your Anthropic plan. * Be mindful of sensitive data and consider security measures when integrating with external services. First you’ll need to install required libraries and enter an Anthropic API key. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} %pip install -qU pixeltable anthropic ``` ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} import getpass import os if 'ANTHROPIC_API_KEY' not in os.environ: os.environ['ANTHROPIC_API_KEY'] = getpass.getpass( 'Anthropic API Key:' ) ``` Now let’s create a Pixeltable directory to hold the tables for our demo. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} import pixeltable as pxt # Remove the 'anthropic_demo' directory and its contents, if it exists pxt.drop_dir('anthropic_demo', force=True) pxt.create_dir('anthropic_demo') ```
  Created directory 'anthropic\_demo'.
  \
## Messages Create a Table: In Pixeltable, create a table with columns to represent your input data and the columns where you want to store the results from Anthropic. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} from pixeltable.functions import anthropic # Create a table in Pixeltable and add a computed column that calls Anthropic t = pxt.create_table('anthropic_demo/chat', {'input': pxt.String}) msgs = [{'role': 'user', 'content': t.input}] t.add_computed_column( output=anthropic.messages( messages=msgs, model='claude-haiku-4-5-20251001', max_tokens=300, model_kwargs={ # Optional dict with parameters for the Anthropic API 'system': 'Respond to the prompt with detailed historical information.' }, ) ) ```
  Created table 'chat'.
  Added 0 column values with 0 errors.
  No rows affected.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Parse the response into a new column t.add_computed_column(response=t.output.content[0].text) ```
  Added 0 column values with 0 errors.
  No rows affected.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Start a conversation t.insert( [ { 'input': 'What was the outcome of the 1904 US Presidential election?' } ] ) t.select(t.input, t.response).show() ```
  Inserting rows into \`chat\`: 1 rows \[00:00, 203.87 rows/s]
  Inserted 1 row with 0 errors.
### Learn More To learn more about advanced techniques like RAG operations in Pixeltable, check out the [RAG Operations in Pixeltable](/howto/use-cases/rag-operations) tutorial. # Working with Bedrock in Pixeltable Source: https://docs.pixeltable.com/howto/providers/working-with-bedrock Open in Kaggle  Open in Colab  Download Notebook This documentation page is also available as an interactive notebook. You can launch the notebook in Kaggle or Colab, or download it for use with an IDE or local Jupyter installation, by clicking one of the above links. Pixeltable’s Bedrock integration enables you to access AWS Bedrock foundation models directly from your tables. ### Prerequisites * Activate Bedrock in your AWS account. * Request access to your desired models (e.g. Claude Sonnet 3.7, Amazon Nova Pro). * Obtain a **Bedrock API Key** from the AWS console (under Bedrock > API keys), or configure standard AWS IAM credentials. ### Important notes * Bedrock usage may incur costs based on your Bedrock plan. * Be mindful of sensitive data and consider security measures when integrating with external services. First you’ll need to install required libraries and configure your Bedrock credentials. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} %pip install -qU pixeltable ``` ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} import getpass import os if 'BEDROCK_API_KEY' not in os.environ: os.environ['BEDROCK_API_KEY'] = getpass.getpass( 'Enter your Bedrock API Key: ' ) # Optional: set the region if your Bedrock endpoint is not in us-east-1 # os.environ['BEDROCK_REGION_NAME'] = 'us-west-2' ``` Now let’s create a Pixeltable directory to hold the tables for our demo. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} import pixeltable as pxt # Remove the `bedrock_demo` directory and its contents, if it exists pxt.drop_dir('bedrock_demo', force=True) pxt.create_dir('bedrock_demo') ```
  Created directory 'bedrock\_demo'.
  \
## Messages Create a Table: In Pixeltable, create a table with columns to represent your input data and the columns where you want to store the results from Bedrock. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} from pixeltable.functions import bedrock # Create a table in Pixeltable and add a computed column that calls Bedrock t = pxt.create_table('bedrock_demo/chat', {'input': pxt.String}) t.add_computed_column( output=bedrock.converse( model_id='amazon.nova-pro-v1:0', messages=[{'role': 'user', 'content': [{'text': t.input}]}], ) ) ```
  Created table 'chat'.
  Added 0 column values with 0 errors in 0.01 s
  No rows affected.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Parse the response into a new column t.add_computed_column(response=t.output.output.message.content[0].text) ```
  Added 0 column values with 0 errors in 0.01 s
  No rows affected.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Start a conversation t.insert( [ { 'input': 'What was the outcome of the 1904 US Presidential election?' } ] ) t.select(t.input, t.response).show() ```
  Inserted 1 row with 0 errors in 2.75 s (0.36 rows/s)
### Learn more To learn more about advanced techniques like RAG operations in Pixeltable, check out the [RAG Operations in Pixeltable](/howto/use-cases/rag-operations) tutorial. # Working with BFL FLUX in Pixeltable Source: https://docs.pixeltable.com/howto/providers/working-with-bfl Open in Kaggle  Open in Colab  Download Notebook This documentation page is also available as an interactive notebook. You can launch the notebook in Kaggle or Colab, or download it for use with an IDE or local Jupyter installation, by clicking one of the above links. This notebook demonstrates how to use Black Forest Labs (BFL) FLUX models for image generation and editing through Pixeltable. [BFL FLUX](https://docs.bfl.ai/) offers state-of-the-art text-to-image generation with models like FLUX.2 and FLUX 1.1, featuring: * High-fidelity image generation with accurate hands, faces, and textures * Multi-reference image editing * Precise hex color control * Typography and text rendering ## Prerequisites 1. A BFL API key from [dashboard.bfl.ai](https://dashboard.bfl.ai) 2. Pixeltable installed ## Setup First, install Pixeltable and set up your API key: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} %pip install -qU pixeltable ``` ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} import getpass import os # Set your BFL API key - get one from https://dashboard.bfl.ai if 'BFL_API_KEY' not in os.environ: os.environ['BFL_API_KEY'] = getpass.getpass( 'Enter your BFL API key: ' ) ``` ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} import pixeltable as pxt from pixeltable.functions import bfl # Create a directory for our examples pxt.drop_dir('bfl_demo', force=True) pxt.create_dir('bfl_demo') ```
  Connected to Pixeltable database at: postgresql+psycopg://postgres:@/pixeltable?host=/Users/pjlb/.pixeltable/pgdata
  Created directory 'bfl\_demo'.
  \
## Text-to-Image Generation Generate images from text prompts using FLUX models. FLUX 1.1 \[pro] offers fast, reliable results. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Create a table for image generation prompts_t = pxt.create_table( 'bfl_demo.prompts', {'prompt': pxt.String, 'style': pxt.String}, if_exists='replace', ) # Add a computed column that generates images from prompts prompts_t.add_computed_column( image=bfl.generate( prompts_t.prompt, model='flux-pro-1.1', width=1024, height=1024 ) ) ```
  Created table 'prompts'.
  Added 0 column values with 0 errors.
  No rows affected.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Insert some prompts to generate images prompts_t.insert( [ { 'prompt': 'A majestic mountain landscape at golden hour, photorealistic', 'style': 'landscape', }, { 'prompt': 'A futuristic city with flying cars and neon lights, cyberpunk style', 'style': 'sci-fi', }, ] ) ```
  Inserting rows into \`prompts\`: 2 rows \[00:00, 442.18 rows/s]
  Inserted 2 rows with 0 errors.
  2 rows inserted, 6 values computed.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # View the generated images prompts_t.select(prompts_t.prompt, prompts_t.image).show() ```
## Image Editing Edit existing images with text prompts using FLUX models. This is powerful for: * Changing backgrounds * Adding or removing objects * Style transfer * Multi-reference editing ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Create a table for image editing edit_t = pxt.create_table( 'bfl_demo.edits', {'original': pxt.Image, 'edit_prompt': pxt.String}, if_exists='replace', ) # Add computed column for edited images edit_t.add_computed_column( edited=bfl.edit( edit_t.edit_prompt, edit_t.original, model='flux-2-pro' ) ) ```
  Created table 'edits'.
  Added 0 column values with 0 errors.
  No rows affected.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Insert an image and edit prompt # Replace with your own image URL or path edit_t.insert( original='https://images.unsplash.com/photo-1506744038136-46273834b3fb?w=1024&h=768&fit=crop', edit_prompt='Add a dramatic sunset with vibrant orange and purple colors', ) ```
  Inserting rows into \`edits\`: 1 rows \[00:00, 600.99 rows/s]
  Inserted 1 row with 0 errors.
  1 row inserted, 4 values computed.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # View original and edited images edit_t.select(edit_t.original, edit_t.edited, edit_t.edit_prompt).show() ```
## Using Seeds for Reproducibility Use the `seed` parameter to get reproducible results: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Create a table with seed support seed_t = pxt.create_table( 'bfl_demo.seeded', {'prompt': pxt.String, 'seed': pxt.Int}, if_exists='replace', ) seed_t.add_computed_column( image=bfl.generate( seed_t.prompt, model='flux-pro-1.1', width=512, height=512, seed=seed_t.seed, ) ) # Same seed = same image seed_t.insert( [ {'prompt': 'A red rose in a crystal vase', 'seed': 42}, { 'prompt': 'A red rose in a crystal vase', 'seed': 42, }, # Same result { 'prompt': 'A red rose in a crystal vase', 'seed': 123, }, # Different result ] ) ```
  Created table 'seeded'.
  Added 0 column values with 0 errors.
  Inserting rows into \`seeded\`: 3 rows \[00:00, 1211.18 rows/s]
  Inserted 3 rows with 0 errors.
  3 rows inserted, 6 values computed.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} seed_t.collect() ```
## Image Expansion (Outpainting) Expand an image beyond its original boundaries using `bfl.expand()`. This is perfect for: * Making images wider or taller * Adapting content for different aspect ratios * Extending scenes naturally ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Create a table for image expansion expand_t = pxt.create_table( 'bfl_demo.expanded', {'image': pxt.Image, 'expand_prompt': pxt.String}, if_exists='replace', ) # Add computed column to expand images horizontally expand_t.add_computed_column( wide=bfl.expand( expand_t.expand_prompt, expand_t.image, left=256, # Add 256 pixels to left right=256, # Add 256 pixels to right ) ) ```
  Created table 'expanded'.
  Added 0 column values with 0 errors.
  No rows affected.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Insert an image to expand - using a known small image expand_t.insert( image='https://replicate.delivery/pbxt/HtGQBfA5TrqFYZBf0UL18NTqHrzt8UiSIsAkUuMHtjvFDO6p/overture-creations-5sI6fQgYIuo.png', expand_prompt='Continue the room interior with similar decor and lighting', ) ```
  Inserting rows into \`expanded\`: 1 rows \[00:00, 379.78 rows/s]
  Inserted 1 row with 0 errors.
  1 row inserted, 4 values computed.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # View original and expanded images expand_t.select(expand_t.image, expand_t.wide).show() ```
## Inpainting with Fill `bfl.fill()` allows you to inpaint specific regions of an image using a mask: * **Black areas** in the mask are preserved * **White areas** in the mask are inpainted based on the prompt Use cases: remove unwanted objects, replace backgrounds, edit text in images, restore damaged areas. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Create a table for inpainting fill_t = pxt.create_table( 'bfl_demo.filled', {'image': pxt.Image, 'mask': pxt.Image, 'fill_prompt': pxt.String}, if_exists='replace', ) # Add computed column to fill masked regions fill_t.add_computed_column( filled=bfl.fill( fill_t.fill_prompt, fill_t.image, fill_t.mask, steps=50, guidance=30, ) ) ```
  Created table 'filled'.
  Added 0 column values with 0 errors.
  No rows affected.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Insert image and mask - the white area in the mask will be inpainted fill_t.insert( image='https://replicate.delivery/pbxt/HtGQBfA5TrqFYZBf0UL18NTqHrzt8UiSIsAkUuMHtjvFDO6p/overture-creations-5sI6fQgYIuo.png', mask='https://replicate.delivery/pbxt/HtGQBqO9MtVbPm0G0K43nsvvjBB0E0PaWOhuNRrRBBT4ttbf/mask.png', fill_prompt='A fluffy golden retriever sitting on the couch', ) ```
  Inserting rows into \`filled\`: 1 rows \[00:00, 531.87 rows/s]
  Inserted 1 row with 0 errors.
  1 row inserted, 6 values computed.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # View original image, mask, and filled result fill_t.select(fill_t.image, fill_t.mask, fill_t.filled).show() ```
## Best Practices 1. **Use specific prompts**: Include details about style, lighting, composition, and subject 2. **Start with flux-pro-1.1**: Fast and reliable for most use cases 3. **Use seeds for reproducibility**: When you need consistent results 4. **Resolution**: Minimum 64x64, max 4MP (2048x2048), dimensions must be multiples of 16 5. **Input image limits**: Max 20MP for editing/expand/fill operations 6. **Safety tolerance**: Default is 2; lower = stricter moderation (0-6 scale) ## Learn More * [BFL Documentation](https://docs.bfl.ai/) * [FLUX.2 Overview](https://docs.bfl.ai/flux_2/flux2_overview) * [Text-to-Image API](https://docs.bfl.ai/flux_2/flux2_text_to_image) * [Image Editing API](https://docs.bfl.ai/flux_2/flux2_image_editing) * [Pricing](https://docs.bfl.ai/quick_start/pricing) # Working with Deepseek in Pixeltable Source: https://docs.pixeltable.com/howto/providers/working-with-deepseek Open in Kaggle  Open in Colab  Download Notebook This documentation page is also available as an interactive notebook. You can launch the notebook in Kaggle or Colab, or download it for use with an IDE or local Jupyter installation, by clicking one of the above links. Pixeltable’s Deepseek integration enables you to access Deepseek’s LLM via the Deepseek API. ### Prerequisites * A Deepseek account with an API key ([https://api-docs.deepseek.com/](https://api-docs.deepseek.com/)) ### Important notes * Deepseek usage may incur costs based on your Deepseek plan. * Be mindful of sensitive data and consider security measures when integrating with external services. First you’ll need to install the required libraries and enter a Deepseek API key. Deepseek uses the OpenAI SDK as its Python API, so we need to install it in addition to Pixeltable. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} %pip install -qU pixeltable openai ``` ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} import getpass import os if 'DEEPSEEK_API_KEY' not in os.environ: os.environ['DEEPSEEK_API_KEY'] = getpass.getpass('Deepseek API Key:') ```
  Deepseek API Key: ········
Now let’s create a Pixeltable directory to hold the tables for our demo. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} import pixeltable as pxt # Remove the 'deepseek_demo' directory and its contents, if it exists pxt.drop_dir('deepseek_demo', force=True) pxt.create_dir('deepseek_demo') ```
  Connected to Pixeltable database at: postgresql+psycopg://postgres:@/pixeltable?host=/Users/sergeymkhitaryan/.pixeltable/pgdata
  Created directory 'deepseek\_demo'.
  \
## Messages Create a Table: In Pixeltable, create a table with columns to represent your input data and the columns where you want to store the results from Deepseek. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} from pixeltable.functions import deepseek # Create a table in Pixeltable and add a computed column that calls Deepseek t = pxt.create_table('deepseek_demo/chat', {'input': pxt.String}) msgs = [{'role': 'user', 'content': t.input}] t.add_computed_column( output=deepseek.chat_completions( messages=msgs, model='deepseek-v4-flash' ) ) ```
  Created table 'chat'.
  Added 0 column values with 0 errors in 0.00 s
  No rows affected.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Parse the response into a new column t.add_computed_column(response=t.output.choices[0].message.content) ```
  Added 0 column values with 0 errors in 0.01 s
  No rows affected.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Start a conversation t.insert( [ { 'input': 'What was the outcome of the 1904 US Presidential election?' } ] ) t.select(t.input, t.response).show() ```
  Inserted 1 row with 0 errors in 3.99 s (0.25 rows/s)
### Learn more To learn more about advanced techniques like RAG operations in Pixeltable, check out the [RAG Operations in Pixeltable](/howto/use-cases/rag-operations) tutorial. # Working with Microsoft Fabric Source: https://docs.pixeltable.com/howto/providers/working-with-fabric Open in Kaggle  Open in Colab  Download Notebook This documentation page is also available as an interactive notebook. You can launch the notebook in Kaggle or Colab, or download it for use with an IDE or local Jupyter installation, by clicking one of the above links. Pixeltable’s Microsoft Fabric integration enables you to access Azure OpenAI models within Microsoft Fabric notebook environments with automatic authentication. ## Prerequisites * A Microsoft Fabric workspace with access to AI services * Running in a Microsoft Fabric notebook environment ## Important notes * This integration only works within Microsoft Fabric notebook environments * Authentication is handled automatically - no API keys required * Azure OpenAI usage in Fabric is subject to your organization’s Fabric capacity and policies For more information about Fabric AI services, see the [Microsoft Fabric AI Services documentation](https://learn.microsoft.com/en-us/fabric/data-science/ai-services/ai-services-overview). First, install Pixeltable in your Fabric notebook: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} %pip install -qU pixeltable ``` Now let’s create a Pixeltable directory to hold the tables for our demo. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} import pixeltable as pxt # Remove the 'fabric_demo' directory and its contents, if it exists pxt.drop_dir('fabric_demo', force=True) pxt.create_dir('fabric_demo') ``` ## Chat Completions with Standard Models Let’s start by using a standard chat model (gpt-4.1) for a simple Q\&A application. Create a table in Pixeltable with a computed column that calls Azure OpenAI via Fabric: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} from pixeltable.functions import fabric # Create a table for customer support tickets tickets = pxt.create_table( 'fabric_demo.support_tickets', { 'ticket_id': pxt.Int, 'customer_message': pxt.String, 'priority': pxt.String, }, ) # Add a computed column that automatically generates AI responses # No API keys needed - Fabric handles authentication! messages = [ { 'role': 'system', 'content': 'You are a helpful customer support agent. Be concise and professional.', }, {'role': 'user', 'content': tickets.customer_message}, ] tickets.add_computed_column( ai_response=fabric.chat_completions( messages, model='gpt-4.1', model_kwargs={'max_tokens': 200, 'temperature': 0.7}, ) ) ``` ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Parse the response to extract just the message content tickets.add_computed_column( response_text=tickets.ai_response.choices[0].message.content ) ``` ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Insert data - AI responses are generated automatically tickets.insert( [ { 'ticket_id': 1, 'customer_message': 'How do I reset my password?', 'priority': 'low', }, { 'ticket_id': 2, 'customer_message': "My order hasn't arrived after 2 weeks", 'priority': 'high', }, { 'ticket_id': 3, 'customer_message': 'Can I change my subscription plan?', 'priority': 'medium', }, ] ) # Query results with AI-generated responses tickets.select( tickets.ticket_id, tickets.customer_message, tickets.response_text ).show() ``` ## Chat Completions with Reasoning Models Fabric also supports reasoning models like gpt-5, which are optimized for complex reasoning tasks. **Note:** Reasoning models have different parameter requirements: * Use `max_completion_tokens` instead of `max_tokens` * Don’t support the `temperature` parameter ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Create a table for complex reasoning tasks reasoning_tasks = pxt.create_table( 'fabric_demo.reasoning', {'task_id': pxt.Int, 'problem': pxt.String} ) messages = [{'role': 'user', 'content': reasoning_tasks.problem}] reasoning_tasks.add_computed_column( reasoning_output=fabric.chat_completions( messages, model='gpt-5', # Reasoning model model_kwargs={ 'max_completion_tokens': 1000 # Note: max_completion_tokens, not max_tokens }, ) ) reasoning_tasks.add_computed_column( solution=reasoning_tasks.reasoning_output.choices[0].message.content ) ``` ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Insert a complex reasoning task reasoning_tasks.insert( [ { 'task_id': 1, 'problem': 'Explain how to implement a binary search tree with self-balancing capabilities. Include time complexity analysis.', } ] ) reasoning_tasks.select( reasoning_tasks.problem, reasoning_tasks.solution ).show() ``` ## Embeddings for Semantic Search Fabric also supports embedding models for semantic search and similarity operations. Let’s create a knowledge base with semantic search capabilities: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Create a knowledge base table knowledge_base = pxt.create_table( 'fabric_demo.knowledge', {'doc_id': pxt.Int, 'content': pxt.String, 'category': pxt.String}, ) # Add embeddings column knowledge_base.add_computed_column( embedding=fabric.embeddings( knowledge_base.content, model='text-embedding-3-small' ) ) # Insert some documents knowledge_base.insert( [ { 'doc_id': 1, 'content': 'Pixeltable is a Python library for AI data workflows with built-in versioning.', 'category': 'product', }, { 'doc_id': 2, 'content': 'Microsoft Fabric provides a unified analytics platform for data engineering and AI.', 'category': 'platform', }, { 'doc_id': 3, 'content': 'Azure OpenAI Service offers powerful language models through REST APIs.', 'category': 'service', }, ] ) ``` ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Add an embedding index for fast similarity search knowledge_base.add_embedding_index( 'content', embedding=fabric.embeddings.using(model='text-embedding-3-small'), ) ``` ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Perform similarity search sim = knowledge_base.content.similarity('AI platform for data science') knowledge_base.select( knowledge_base.content, knowledge_base.category, sim=sim ).order_by(sim, asc=False).limit(2).show() ``` ## Combining Chat and Embeddings: RAG Pattern Let’s combine embeddings and chat completions to build a simple Retrieval-Augmented Generation (RAG) system: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Create a table for questions questions = pxt.create_table( 'fabric_demo.questions', {'question_id': pxt.Int, 'question': pxt.String}, ) # Find similar documents using similarity search @pxt.query def retrieve_context(question: str, top_k: int = 2) -> list[dict]: sim = knowledge_base.content.similarity(question) return ( knowledge_base.select(knowledge_base.content) .order_by(sim, asc=False) .limit(top_k) .collect()['content'] ) # Add context retrieval questions.add_computed_column( context=retrieve_context(questions.question, top_k=2) ) # Build RAG prompt with retrieved context questions.add_computed_column( rag_messages=[ { 'role': 'system', 'content': "Answer the question based on the provided context. If the context doesn't contain relevant information, say so.", }, { 'role': 'user', 'content': f'Context: {questions.context}\n\nQuestion: {questions.question}', }, ] ) # Generate answer using gpt-4.1 questions.add_computed_column( answer_response=fabric.chat_completions( questions.rag_messages, model='gpt-4.1', model_kwargs={'max_tokens': 300}, ) ) questions.add_computed_column( answer=questions.answer_response.choices[0].message.content ) ``` ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Ask a question questions.insert( [{'question_id': 1, 'question': 'What is Microsoft Fabric used for?'}] ) questions.select( questions.question, questions.context, questions.answer ).show() ``` ## Available Models in Fabric The following models are currently available in Microsoft Fabric: **Chat Models:** * `gpt-5` (reasoning model) * `gpt-4.1` * `gpt-4.1-mini` **Embedding Models:** * `text-embedding-ada-002` * `text-embedding-3-small` * `text-embedding-3-large` For the latest information on available models, see the [Fabric AI Services documentation](https://learn.microsoft.com/en-us/fabric/data-science/ai-services/ai-services-overview). ## Key Features * **Automatic Authentication**: No API keys required - authentication is handled by Fabric * **Rate Limiting**: Pixeltable automatically handles rate limiting based on Azure OpenAI response headers * **Batching**: Embedding requests are automatically batched for efficiency (up to 32 inputs per request) * **Incremental Processing**: Computed columns only run on new or updated data * **Versioning**: All data and transformations are automatically versioned ### Learn More To learn more about advanced techniques in Pixeltable: * [RAG Operations in Pixeltable](/howto/use-cases/rag-operations) * [Working with Embeddings](/platform/embedding-indexes) * [Microsoft Fabric AI Services](https://learn.microsoft.com/en-us/fabric/data-science/ai-services/ai-services-overview) # Working with fal.ai in Pixeltable Source: https://docs.pixeltable.com/howto/providers/working-with-fal Open in Kaggle  Open in Colab  Download Notebook This documentation page is also available as an interactive notebook. You can launch the notebook in Kaggle or Colab, or download it for use with an IDE or local Jupyter installation, by clicking one of the above links. Pixeltable’s fal.ai integration enables you to access fal.ai’s fast inference models via the fal.ai API. ### Prerequisites * A fal.ai account with an API key ([https://fal.ai/dashboard/keys](https://fal.ai/dashboard/keys)) ### Important notes * fal.ai usage may incur costs based on your fal.ai plan. * Be mindful of sensitive data and consider security measures when integrating with external services. First you’ll need to install required libraries and enter a fal.ai API key. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} %pip install -qU fal-client ``` ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} import getpass import os if 'FAL_API_KEY' not in os.environ: os.environ['FAL_API_KEY'] = getpass.getpass('fal.ai API Key: ') ``` Now let’s create a Pixeltable directory to hold the tables for our demo. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} import pixeltable as pxt # Remove the 'fal_demo' directory and its contents, if it exists pxt.drop_dir('fal_demo', force=True) pxt.create_dir('fal_demo') ```
  Connected to Pixeltable database at: postgresql+psycopg://postgres:@/pixeltable?host=/Users/pjlb/.pixeltable/pgdata
  Created directory 'fal\_demo'.
  \
## Text-to-image generation with FLUX Schnell Let’s start by using fal.ai’s FLUX Schnell model, which is optimized for fast image generation. We’ll create a table to store prompts and generated images. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} from pixeltable.functions import fal # Create a table for image generation t = pxt.create_table('fal_demo/images', {'prompt': pxt.String}) # Add a computed column that calls the FLUX Schnell model t.add_computed_column( response=fal.run( input={'prompt': t.prompt}, app='fal-ai/flux/schnell' ) ) ```
  Created table 'images'.
  Added 0 column values with 0 errors in 0.01 s
  No rows affected.
Now let’s insert some prompts and see the results: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Insert a few prompts t.insert( [ { 'prompt': 'A serene mountain landscape at sunset with a crystal clear lake' }, { 'prompt': 'A friendly robot teaching a class of kittens to code' }, {'prompt': 'An underwater city with bioluminescent architecture'}, ] ) ```
  Inserted 3 rows with 0 errors in 1.77 s (1.70 rows/s)
  3 rows inserted.
Let’s examine the structure of the response: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} t.select(t.prompt, t.response).head(1) ```
We can see that fal.ai returns a JSON response with an `images` array. Each image has a `url` field. Let’s extract and display the images: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Add a computed column to extract the image URL and convert it to an Image type t.add_computed_column( image=t.response['images'][0]['url'].astype(pxt.Image) ) # Display the prompts and images t.select(t.prompt, t.image).head() ```
  Added 3 column values with 0 errors in 0.04 s (85.38 rows/s)
## Advanced image generation with Fast SDXL fal.ai also offers Fast SDXL, which provides more control over image generation parameters. Let’s create a new table to explore these capabilities. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Create a table with more parameters sdxl_t = pxt.create_table( 'fal_demo/sdxl_images', { 'prompt': pxt.String, 'negative_prompt': pxt.String, 'steps': pxt.Int, }, ) # Add a computed column with more parameters sdxl_t.add_computed_column( response=fal.run( input={ 'prompt': sdxl_t.prompt, 'negative_prompt': sdxl_t.negative_prompt, 'image_size': 'square_hd', # 1024x1024 'num_inference_steps': sdxl_t.steps, }, app='fal-ai/fast-sdxl', ) ) # Extract the image sdxl_t.add_computed_column( image=sdxl_t.response['images'][0]['url'].astype(pxt.Image) ) ```
  Created table 'sdxl\_images'.
  Added 0 column values with 0 errors in 0.01 s
  Added 0 column values with 0 errors in 0.01 s
  No rows affected.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Insert prompts with different parameters sdxl_t.insert( [ { 'prompt': 'A majestic lion in a savanna at golden hour, photorealistic', 'negative_prompt': 'cartoon, illustration, drawing', 'steps': 25, }, { 'prompt': 'A futuristic cityscape with flying cars and neon lights', 'negative_prompt': 'blurry, low quality', 'steps': 30, }, ] ) ```
  Inserted 2 rows with 0 errors in 5.23 s (0.38 rows/s)
  2 rows inserted.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Display the results sdxl_t.select(sdxl_t.prompt, sdxl_t.image).head() ```
## Generating multiple images per prompt You can also generate multiple variations of the same prompt in a single request: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Create a table for multiple image generation multi_t = pxt.create_table( 'fal_demo/multi_images', {'prompt': pxt.String} ) # Generate 3 variations of each prompt multi_t.add_computed_column( response=fal.run( input={'prompt': multi_t.prompt, 'num_images': 3}, app='fal-ai/flux/schnell', ) ) # Extract the first image (you could create columns for all three) multi_t.add_computed_column( image_1=multi_t.response['images'][0]['url'].astype(pxt.Image) ) multi_t.add_computed_column( image_2=multi_t.response['images'][1]['url'].astype(pxt.Image) ) multi_t.add_computed_column( image_3=multi_t.response['images'][2]['url'].astype(pxt.Image) ) ```
  Created table 'multi\_images'.
  Added 0 column values with 0 errors in 0.01 s
  Added 0 column values with 0 errors in 0.01 s
  Added 0 column values with 0 errors in 0.01 s
  Added 0 column values with 0 errors in 0.01 s
  No rows affected.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Insert a prompt multi_t.insert( [{'prompt': 'A steampunk mechanical butterfly on a brass flower'}] ) ```
  Inserted 1 row with 0 errors in 1.14 s (0.88 rows/s)
  1 row inserted.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Display all three variations multi_t.select(multi_t.image_1, multi_t.image_2, multi_t.image_3).head() ```
## Using Higher Quality Models For higher quality generation, you can use models like `fal-ai/flux/dev` which produce better results but take more time: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Create a table using FLUX Dev dev_t = pxt.create_table('fal_demo/flux_dev', {'prompt': pxt.String}) # Use FLUX Dev model for higher quality dev_t.add_computed_column( response=fal.run( input={'prompt': dev_t.prompt}, app='fal-ai/flux/dev' ) ) dev_t.add_computed_column( image=dev_t.response['images'][0]['url'].astype(pxt.Image) ) ```
  Created table 'flux\_dev'.
  Added 0 column values with 0 errors in 0.01 s
  Added 0 column values with 0 errors in 0.01 s
  No rows affected.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Insert a prompt (note: FLUX Dev may take longer but produces higher quality results) dev_t.insert( [ { 'prompt': 'A highly detailed oil painting of a wizard casting a spell in an ancient library' } ] ) ```
  Inserted 1 row with 0 errors in 1.74 s (0.58 rows/s)
  1 row inserted.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Display the result dev_t.select(dev_t.prompt, dev_t.image).head() ```
## Exploring Available Models fal.ai offers a wide variety of models. Here are some popular ones you can try: ### Image Generation Models * `fal-ai/flux/schnell` - Fast FLUX model for quick image generation * `fal-ai/flux/dev` - Higher quality FLUX model (slower) * `fal-ai/fast-sdxl` - Fast Stable Diffusion XL * `fal-ai/stable-diffusion-v3-medium` - Stable Diffusion 3 Medium ### Other Models * `fal-ai/fast-lightning-sdxl` - Ultra-fast SDXL variant * `fal-ai/recraft-v3` - Recraft V3 for design-focused generation To use a different model, simply change the `app` parameter in your `fal.run()` call. ## Working with Batch Processing Pixeltable’s computed columns make it easy to process multiple images in batch. Let’s create a larger dataset: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Create a batch processing table batch_t = pxt.create_table( 'fal_demo/batch', {'category': pxt.String, 'description': pxt.String} ) # Create a prompt by combining category and description batch_t.add_computed_column( prompt=pxt.functions.string.format( 'A {} that is {}', batch_t.category, batch_t.description ) ) # Generate images batch_t.add_computed_column( response=fal.run( input={'prompt': batch_t.prompt}, app='fal-ai/flux/schnell' ) ) batch_t.add_computed_column( image=batch_t.response['images'][0]['url'].astype(pxt.Image) ) ```
  Created table 'batch'.
  Added 0 column values with 0 errors in 0.02 s
  Added 0 column values with 0 errors in 0.01 s
  Added 0 column values with 0 errors in 0.01 s
  No rows affected.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Insert a batch of prompts batch_t.insert( [ {'category': 'landscape', 'description': 'peaceful and zen-like'}, { 'category': 'portrait', 'description': 'mysterious and ethereal', }, { 'category': 'abstract art', 'description': 'colorful and energetic', }, { 'category': 'architecture', 'description': 'modern and minimalist', }, {'category': 'animal', 'description': 'cute and fluffy'}, ] ) ```
  Inserted 5 rows with 0 errors in 1.69 s (2.96 rows/s)
  5 rows inserted.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # View all results batch_t.select( batch_t.category, batch_t.description, batch_t.image ).show() ```
## Tips and Best Practices 1. **Rate Limiting**: fal.ai has rate limits. Pixeltable respects these limits by default. You can configure custom rate limits in your Pixeltable config. 2. **Model Selection**: * Use `flux/schnell` for fast prototyping and when speed is critical * Use `flux/dev` when you need higher quality and can afford longer generation times * Use `fast-sdxl` for a good balance of speed and quality 3. **Prompt Engineering**: Good prompts lead to better results. Be specific and descriptive. 4. **Negative Prompts**: Use negative prompts to exclude unwanted elements from your images. 5. **Caching**: Pixeltable automatically caches results, so re-running the same prompt won’t incur additional costs. ### Learn more * fal.ai Documentation: [https://fal.ai/docs](https://fal.ai/docs) * Pixeltable Documentation: [https://docs.pixeltable.com](https://docs.pixeltable.com) * To learn more about advanced techniques like RAG operations in Pixeltable, check out the [RAG Operations in Pixeltable](/howto/use-cases/rag-operations) tutorial. # Working with Fireworks AI in Pixeltable Source: https://docs.pixeltable.com/howto/providers/working-with-fireworks Open in Kaggle  Open in Colab  Download Notebook This documentation page is also available as an interactive notebook. You can launch the notebook in Kaggle or Colab, or download it for use with an IDE or local Jupyter installation, by clicking one of the above links. Pixeltable’s Fireworks integration enables you to access LLMs hosted on the Fireworks platform. ### Prerequisites * A Fireworks account with an API key ([https://fireworks.ai/api-keys](https://fireworks.ai/api-keys)) ### Important notes * Fireworks usage may incur costs based on your Fireworks plan. * Be mindful of sensitive data and consider security measures when integrating with external services. First you’ll need to install required libraries and enter a Fireworks API key. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} %pip install -qU pixeltable fireworks-ai ``` ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} import getpass import os if 'FIREWORKS_API_KEY' not in os.environ: os.environ['FIREWORKS_API_KEY'] = getpass.getpass( 'Fireworks API Key:' ) ``` Now let’s create a Pixeltable directory to hold the tables for our demo. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} import pixeltable as pxt # Remove the 'fireworks_demo' directory and its contents, if it exists pxt.drop_dir('fireworks_demo', force=True) pxt.create_dir('fireworks_demo') ```
  Connected to Pixeltable database at: postgresql+psycopg://postgres:@/pixeltable?host=/Users/asiegel/.pixeltable/pgdata
  Created directory 'fireworks\_demo'.
  \
## Completions Create a Table: In Pixeltable, create a table with columns to represent your input data and the columns where you want to store the results from Fireworks. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} from pixeltable.functions.fireworks import chat_completions # Create a table in Pixeltable and pick a model hosted on Fireworks with some parameters t = pxt.create_table('fireworks_demo/chat', {'input': pxt.String}) messages = [{'role': 'user', 'content': t.input}] t.add_computed_column( output=chat_completions( messages=messages, model='accounts/fireworks/models/nemotron-lightning-3p5-30b-a3b', model_kwargs={ # Optional dict with parameters for the Fireworks API 'max_tokens': 300, 'top_k': 40, 'top_p': 0.9, 'temperature': 0.7, }, ) ) ```
  Created table 'chat'.
  Added 0 column values with 0 errors in 0.01 s
  No rows affected.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Parse the bot_response into a new column t.add_computed_column(response=t.output.choices[0].message.content) ```
  Added 0 column values with 0 errors in 0.01 s
  No rows affected.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Start a conversation t.insert( [{'input': 'Can you tell me who was President of the US in 1961?'}] ) t.select(t.input, t.response).show() ```
  Inserted 1 row with 0 errors in 2.15 s (0.47 rows/s)
### Learn more To learn more about advanced techniques like RAG operations in Pixeltable, check out the [RAG Operations in Pixeltable](/howto/use-cases/rag-operations) tutorial. # Working with Gemini in Pixeltable Source: https://docs.pixeltable.com/howto/providers/working-with-gemini Open in Kaggle  Open in Colab  Download Notebook This documentation page is also available as an interactive notebook. You can launch the notebook in Kaggle or Colab, or download it for use with an IDE or local Jupyter installation, by clicking one of the above links. Pixeltable’s Gemini integration supports two authentication methods: * Google AI Studio: requires an API key from [https://aistudio.google.com/app/apikey](https://aistudio.google.com/app/apikey) * Vertex AI: uses Application Default Credentials via the Google Cloud SDK ### Prerequisites For Google AI Studio: * A Google AI Studio account with an API key ([https://aistudio.google.com/app/apikey](https://aistudio.google.com/app/apikey)) For Vertex AI: * A Google Cloud project with the Vertex AI API enabled * The Google Cloud SDK installed and configured (`gcloud auth application-default login`) ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} %pip install -qU pixeltable google-genai ``` ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} import os vertex_enabled = ( os.environ.get('GOOGLE_GENAI_USE_VERTEXAI', '').lower() == 'true' ) # Option 1: Google AI Studio (API key) # Set GEMINI_API_KEY or GOOGLE_API_KEY in your environment, # or add api_key to the [gemini] section of $PIXELTABLE_HOME/config.toml if ( not vertex_enabled and 'GEMINI_API_KEY' not in os.environ and 'GOOGLE_API_KEY' not in os.environ ): import getpass os.environ['GEMINI_API_KEY'] = getpass.getpass( 'Google AI Studio API Key:' ) # Option 2: Vertex AI (Application Default Credentials) # Uncomment and set the following environment variables to use Vertex AI instead: # os.environ['GOOGLE_GENAI_USE_VERTEXAI'] = 'true' # os.environ['GOOGLE_CLOUD_PROJECT'] = 'your-project-id' # os.environ['GOOGLE_CLOUD_LOCATION'] = 'us-central1' # optional, defaults to us-central1 # Then authenticate via: gcloud auth application-default login ``` Create a Pixeltable directory for this notebook’s demo tables. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} import pixeltable as pxt import pixeltable.functions as pxtf pxt.create_dir('gemini_demo', if_exists='replace_force') ```
  Created directory 'gemini\_demo'.
  \
## Generate content Create a table with an input column, then add a computed column that calls Gemini on each row. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} from google.genai.types import GenerateContentConfigDict # Create a table in Pixeltable and pick a model hosted on Google AI Studio with some parameters t = pxt.create_table('gemini_demo/text', {'input': pxt.String}) config = GenerateContentConfigDict( max_output_tokens=500, temperature=1.0, top_p=0.95, top_k=40 ) t.add_computed_column( output=pxtf.gemini.generate_content( t.input, model='gemini-2.5-flash', config=config ) ) ```
  Created table 'text'.
  Added 0 column values with 0 errors in 0.00 s
  No rows affected.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Ask Gemini for short responses so results are easy to inspect t.insert( [ {'input': 'Write a two-sentence story about a magic backpack.'}, {'input': 'Tell a one-line science joke.'}, ] ) ```
  Inserted 2 rows with 0 errors in 5.90 s (0.34 rows/s)
  2 rows inserted.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Parse the response text into its own column t.add_computed_column( response=t.output['candidates'][0]['content']['parts'][0]['text'], if_exists='replace', ) ```
  Added 2 column values with 0 errors in 0.01 s (184.71 rows/s)
  2 rows updated.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} t.select(t.input, t.response).collect() ```
## Generate images with Nano Banana Use `pxtf.gemini.generate_content` with model `gemini-2.5-flash-image`. This returns a structured response, so extract the image from `response['candidates'][0]['content']['parts'][0]['inline_data']['data']`, as shown below. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} nano_t = pxt.create_table( 'gemini_demo/nano_banana_images', {'prompt': pxt.String} ) config = GenerateContentConfigDict( response_modalities=['IMAGE'], image_config={'aspect_ratio': '16:9'} ) nano_t.add_computed_column( response=pxtf.gemini.generate_content( nano_t.prompt, model='gemini-2.5-flash-image', config=config ) ) nano_t.add_computed_column( generated_image=( nano_t.response.candidates[0] .content.parts[0] .inline_data.data.astype(pxt.Image) ) ) ```
  Created table 'nano\_banana\_images'.
  Added 0 column values with 0 errors in 0.00 s
  Added 0 column values with 0 errors in 0.00 s
  No rows affected.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} nano_t.insert( [{'prompt': 'A friendly dinosaur playing tennis in a cornfield'}] ) ```
  Inserted 1 row with 0 errors in 6.61 s (0.15 rows/s)
  1 row inserted.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} nano_t.select(nano_t.prompt, nano_t.generated_image).collect() ```
## Generate images with Imagen Use `pxtf.gemini.generate_images` with an Imagen model name such as `imagen-4.0-generate-001`. Imagen is available only through Vertex AI, so these cells require `GOOGLE_GENAI_USE_VERTEXAI=true` (see the setup cell above). This returns a `pxt.Image` directly, so you can use it in computed columns without JSON subscripting. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} from google.genai.types import GenerateImagesConfigDict images_t = pxt.create_table('gemini_demo/images', {'prompt': pxt.String}) config = GenerateImagesConfigDict(aspect_ratio='16:9') images_t.add_computed_column( generated_image=pxtf.gemini.generate_images( images_t.prompt, model='imagen-4.0-generate-001', config=config ) ) ```
  Created table 'images'.
  Added 0 column values with 0 errors in 0.00 s
  No rows affected.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} images_t.insert( [{'prompt': 'A friendly dinosaur playing tennis in a cornfield'}] ) ```
  Inserted 1 row with 0 errors in 11.87 s (0.08 rows/s)
  1 row inserted.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} images_t.select(images_t.prompt, images_t.generated_image).collect() ```
## Generate video with Veo ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} videos_t = pxt.create_table('gemini_demo/videos', {'prompt': pxt.String}) videos_t.add_computed_column( generated_video=pxtf.gemini.generate_videos( videos_t.prompt, model='veo-3.1-fast-generate-preview' ) ) ```
  Created table 'videos'.
  Added 0 column values with 0 errors in 0.00 s
  No rows affected.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} videos_t.insert( [ { 'prompt': 'A giant pixel floating over the open ocean in a sea of data' } ] ) ```
  Inserted 1 row with 0 errors in 46.34 s (0.02 rows/s)
  1 row inserted.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} videos_t.select(videos_t.prompt, videos_t.generated_video).collect() ```
## Generate video from an existing image We’ll add a computed column to our `nano_t` table to animate the generated image. You can pass `images_t.generated_image` from the Imagen section above the same way. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} nano_t.add_computed_column( generated_video=pxtf.gemini.generate_videos( image=nano_t.generated_image, model='veo-3.1-fast-generate-preview', ) ) ``` ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} nano_t.select(nano_t.prompt, nano_t.generated_video).collect() ```
### Learn more For a deeper end-to-end example, see [RAG Operations in Pixeltable](/howto/use-cases/rag-operations). # Working with Groq in Pixeltable Source: https://docs.pixeltable.com/howto/providers/working-with-groq Open in Kaggle  Open in Colab  Download Notebook This documentation page is also available as an interactive notebook. You can launch the notebook in Kaggle or Colab, or download it for use with an IDE or local Jupyter installation, by clicking one of the above links. Pixeltable’s Groq integration enables you to access Groq models via the Groq API. ### Prerequisites * A Groq account with an API key ([https://console.groq.com/docs/quickstart](https://console.groq.com/docs/quickstart)) ### Important notes * Groq usage may incur costs based on your Groq plan. * Be mindful of sensitive data and consider security measures when integrating with external services. First you’ll need to install required libraries and enter your OpenAI API key. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} %pip install -qU pixeltable groq ``` ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} import getpass import os if 'GROQ_API_KEY' not in os.environ: os.environ['GROQ_API_KEY'] = getpass.getpass( 'Enter your Groq API key:' ) ``` Now let’s create a Pixeltable directory to hold the tables for our demo. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} import pixeltable as pxt # Remove the 'groq_demo' directory and its contents, if it exists pxt.drop_dir('groq_demo', force=True) pxt.create_dir('groq_demo') ```
  Connected to Pixeltable database at: postgresql+psycopg://postgres:@/pixeltable?host=/Users/asiegel/.pixeltable/pgdata
  Created directory 'groq\_demo'.
  \
## Chat Completions Create a Table: In Pixeltable, create a table with columns to represent your input data and the columns where you want to store the results from Groq. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} from pixeltable.functions import groq # Create a table in Pixeltable and add a computed column that calls OpenAI t = pxt.create_table('groq_demo/chat', {'input': pxt.String}) messages = [{'role': 'user', 'content': t.input}] t.add_computed_column( output=groq.chat_completions( messages=messages, model='openai/gpt-oss-20b', model_kwargs={ # Optional dict with parameters for the Groq API 'max_tokens': 300, 'top_p': 0.9, 'temperature': 0.7, }, ) ) ```
  Created table 'chat'.
  Added 0 column values with 0 errors in 0.01 s
  No rows affected.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Parse the response into a new column t.add_computed_column(response=t.output.choices[0].message.content) ```
  Added 0 column values with 0 errors in 0.01 s
  No rows affected.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Start a conversation t.insert( [{'input': 'How many islands are in the Aleutian island chain?'}] ) t.select(t.input, t.response).head() ```
  Inserted 1 row with 0 errors in 1.16 s (0.86 rows/s)
### Learn more To learn more about advanced techniques like RAG operations in Pixeltable, check out the [RAG Operations in Pixeltable](/howto/use-cases/rag-operations) tutorial. # Working with Hugging Face Source: https://docs.pixeltable.com/howto/providers/working-with-hugging-face Open in Kaggle  Open in Colab  Download Notebook This documentation page is also available as an interactive notebook. You can launch the notebook in Kaggle or Colab, or download it for use with an IDE or local Jupyter installation, by clicking one of the above links. Pixeltable provides seamless integration with Hugging Face datasets and models. This tutorial covers: * Importing datasets directly into Pixeltable tables * Working with dataset splits (train/test/validation) * Streaming large datasets with `IterableDataset` * Type mappings from Hugging Face to Pixeltable * Using Hugging Face models for embeddings ## Setup ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} %pip install -qU pixeltable datasets torch transformers sentence-transformers ``` ## Import a Hugging Face Dataset Use `pxt.create_table()` with the `source=` parameter to import a Hugging Face dataset directly. Pixeltable automatically maps Hugging Face feature types to Pixeltable column types. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} import datasets import pixeltable as pxt pxt.drop_dir('hf_demo', force=True) pxt.create_dir('hf_demo') # Load a dataset with images padoru = datasets.load_dataset( 'not-lain/padoru', split='train' ).select_columns(['Image', 'ImageSize', 'Name', 'ImageSource']) # Import into Pixeltable images = pxt.create_table('hf_demo/images', source=padoru) ```
  Connected to Pixeltable database at: postgresql+psycopg://postgres:@/pixeltable?host=/Users/pjlb/.pixeltable/pgdata
  Created directory 'hf\_demo'.
  Created table 'images'.
  Inserting rows into \`images\`: 100 rows \[00:00, 310.24 rows/s]
  Inserting rows into \`images\`: 100 rows \[00:00, 353.22 rows/s]
  Inserting rows into \`images\`: 100 rows \[00:00, 368.40 rows/s]
  Inserting rows into \`images\`: 82 rows \[00:00, 567.89 rows/s]
  Inserted 382 rows with 0 errors.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} images.head(3) ```
## Working with Dataset Splits When importing a `DatasetDict` (which contains multiple splits like train/test), use `extra_args={'column_name_for_split': 'split'}` to preserve split information in a column. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Load a dataset with multiple splits imdb = datasets.load_dataset('stanfordnlp/imdb') # Import all splits, storing split info in 'split' column reviews = pxt.create_table( 'hf_demo/reviews', source=imdb, extra_args={'column_name_for_split': 'split'}, ) ``` ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Query by split reviews.where(reviews.split == 'train').limit(3).select( reviews.text, reviews.label, reviews.split ).collect() ```
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Count rows per split reviews.group_by(reviews.split).select( reviews.split, count=pxt.functions.count(reviews.text) ).collect() ```
Using `schema_overrides` for Embeddings When importing datasets with pre-computed embeddings (common in RAG), use `schema_overrides` to specify the exact array shape: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Wikipedia with pre-computed embeddings - specify array shape wiki_ds = ( datasets.load_dataset( 'Cohere/wikipedia-2023-11-embed-multilingual-v3', 'simple', split='train', streaming=True, ) .select_columns(['url', 'title', 'text', 'emb']) .take(50) ) wiki = pxt.create_table( 'hf_demo/wiki_embeddings', source=wiki_ds, schema_overrides={'emb': pxt.Array[(1024,), pxt.Float]}, ) ``` ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} wiki.select(wiki.title, wiki.emb).limit(2).collect() ```
## Streaming Large Datasets For very large datasets, use `streaming=True` to filter and sample before importing: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Stream, filter, and sample before importing streaming_ds = datasets.load_dataset( 'stanfordnlp/imdb', split='train', streaming=True ) positive_stream = streaming_ds.filter(lambda x: x['label'] == 1).take(50) ``` ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} positive_samples = pxt.create_table( 'hf_demo/positive_samples', source=positive_stream ) ``` ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} positive_samples.select( positive_samples.text, positive_samples.label ).limit(2).collect() ```
## Importing Audio Datasets Audio datasets work seamlessly - Pixeltable stores audio files locally: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Import a small audio dataset audio_ds = datasets.load_dataset( 'hf-internal-testing/librispeech_asr_dummy', 'clean', split='validation', ) audio_table = pxt.create_table('hf_demo/audio_samples', source=audio_ds) audio_table.select(audio_table.audio, audio_table.text).limit(2).collect() ```
  Created table 'audio\_samples'.
  Inserting rows into \`audio\_samples\`: 73 rows \[00:00, 3960.27 rows/s]
  Inserted 73 rows with 0 errors.
## Inserting More Data Use `table.insert()` to add more data from a HuggingFace dataset to an existing table: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Insert more data from the same or similar dataset more_audio = datasets.load_dataset( 'hf-internal-testing/librispeech_asr_dummy', 'clean', split='validation', ).select(range(5)) audio_table.insert(more_audio) audio_table.count() ```
  Inserting rows into \`audio\_samples\`: 5 rows \[00:00, 3186.68 rows/s]
  Inserted 5 rows with 0 errors.
  78
## Type Mappings Reference
## Using Hugging Face Models Pixeltable integrates with Hugging Face models for embeddings and inference, running locally without API keys. ### Image Embeddings with CLIP ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} from pixeltable.functions.huggingface import clip # Add CLIP embedding index for cross-modal image search images.add_embedding_index( 'Image', embedding=clip.using(model_id='openai/clip-vit-base-patch32') ) # Search images using text sim = images.Image.similarity(string='anime character with red clothes') images.order_by(sim, asc=False).limit(3).select( images.Image, images.Name, sim=sim ).collect() ```
### Text Embeddings with Sentence Transformers ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} from pixeltable.functions.huggingface import sentence_transformer # Create table with text embedding index sample_reviews = pxt.create_table( 'hf_demo/sample_reviews', source=datasets.load_dataset('stanfordnlp/imdb', split='test').select( range(100) ), ) sample_reviews.add_embedding_index( 'text', string_embed=sentence_transformer.using(model_id='all-MiniLM-L6-v2'), ) # Semantic search query = 'great acting and cinematography' sim = sample_reviews.text.similarity(string=query) sample_reviews.order_by(sim, asc=False).limit(3).select( sample_reviews.text, sim=sim ).collect() ```
  Created table 'sample\_reviews'.
  Inserting rows into \`sample\_reviews\`: 100 rows \[00:00, 21625.70 rows/s]
  Inserted 100 rows with 0 errors.
### More Hugging Face Models Pixeltable supports many more HuggingFace models including: * **ASR**: `automatic_speech_recognition()` - transcribe audio * **Translation**: `translation()` - translate between languages * **Text Generation**: `text_generation()` - generate text completions * **Image Classification**: `vit_for_image_classification()` - classify images * **Object Detection**: `detr_for_object_detection()` - detect objects in images See the SDK reference below for the complete list. ## See Also * [HuggingFace SDK Reference](/sdk/latest/huggingface) - Full list of models: ASR, translation, text generation, image classification, etc. * [Working with embedding indexes](../../platform/embedding-indexes) - Index configuration # Working with Jina AI in Pixeltable Source: https://docs.pixeltable.com/howto/providers/working-with-jina Open in Kaggle  Open in Colab  Download Notebook This documentation page is also available as an interactive notebook. You can launch the notebook in Kaggle or Colab, or download it for use with an IDE or local Jupyter installation, by clicking one of the above links. Pixeltable’s Jina AI integration enables you to access state-of-the-art embedding and reranker models via the Jina AI API. ### Prerequisites * A Jina AI account with an API key ([https://jina.ai/](https://jina.ai/)) ### Important notes * Jina AI usage may incur costs based on your Jina AI plan. * Be mindful of sensitive data and consider security measures when integrating with external services. First you’ll need to install Pixeltable and set up your Jina AI API key. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} %pip install -qU pixeltable ``` ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} import getpass import os if 'JINA_API_KEY' not in os.environ: os.environ['JINA_API_KEY'] = getpass.getpass( 'Enter your Jina AI API key: ' ) ``` Now let’s create a Pixeltable directory to hold the tables for our demo. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} import pixeltable as pxt # Remove the 'jina_demo' directory and its contents, if it exists pxt.drop_dir('jina_demo', force=True) pxt.create_dir('jina_demo') ```
  Created directory 'jina\_demo'.
  \
## Text Embeddings Jina AI provides frontier multilingual embedding models for semantic search and RAG applications. The `jina-embeddings-v3` model supports 89+ languages and achieves state-of-the-art performance. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} from pixeltable.functions import jina # Create a table for document embeddings docs_t = pxt.create_table('jina_demo.documents', {'text': pxt.String}) # Add computed column with Jina embeddings # task='retrieval.passage' optimizes embeddings for documents to be searched docs_t.add_computed_column( embedding=jina.embeddings( docs_t.text, model='jina-embeddings-v3', task='retrieval.passage' ) ) ```
  Created table 'documents'.
  Added 0 column values with 0 errors.
  No rows affected.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Insert some sample documents documents = [ 'The Mediterranean diet emphasizes fish, olive oil, and vegetables, believed to reduce chronic diseases.', 'Photosynthesis in plants converts light energy into glucose and produces essential oxygen.', '20th-century innovations, from radios to smartphones, centered on electronic advancements.', 'Rivers provide water, irrigation, and habitat for aquatic species, vital for ecosystems.', "Apple's conference call to discuss fourth fiscal quarter results is scheduled for Thursday, November 2, 2023.", "Shakespeare's works, like 'Hamlet' and 'A Midsummer Night's Dream,' endure in literature.", ] docs_t.insert({'text': doc} for doc in documents) ```
  Inserting rows into \`documents\`: 6 rows \[00:00, 1394.00 rows/s]
  Inserted 6 rows with 0 errors.
  6 rows inserted, 12 values computed.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # View the embeddings docs_t.select(docs_t.text, docs_t.embedding).head(3) ```
## Multilingual Embeddings Jina AI models excel at multilingual text. The same model can embed text in different languages into the same semantic space. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Create a table for multilingual content multilingual_t = pxt.create_table( 'jina_demo.multilingual', {'text': pxt.String, 'language': pxt.String} ) multilingual_t.add_computed_column( embedding=jina.embeddings( multilingual_t.text, model='jina-embeddings-v3', task='text-matching', ) ) # Insert texts in different languages (all about organic skincare) multilingual_t.insert( [ { 'text': 'Organic skincare for sensitive skin with aloe vera and chamomile.', 'language': 'English', }, { 'text': 'Bio-Hautpflege für empfindliche Haut mit Aloe Vera und Kamille.', 'language': 'German', }, { 'text': 'Cuidado de la piel orgánico para piel sensible con aloe vera y manzanilla.', 'language': 'Spanish', }, { 'text': '针对敏感肌专门设计的天然有机护肤产品', 'language': 'Chinese', }, ] ) multilingual_t.select( multilingual_t.language, multilingual_t.text ).collect() ```
  Created table 'multilingual'.
  Added 0 column values with 0 errors.
  Inserting rows into \`multilingual\`: 4 rows \[00:00, 736.23 rows/s]
  Inserted 4 rows with 0 errors.
## Embedding Index for Similarity Search You can use Jina AI embeddings with Pixeltable’s embedding index for efficient similarity search. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Create a table with an embedding index search_t = pxt.create_table('jina_demo.search', {'text': pxt.String}) # Add embedding index for similarity search embed_fn = jina.embeddings.using( model='jina-embeddings-v3', task='retrieval.passage' ) search_t.add_embedding_index('text', string_embed=embed_fn) # Insert documents search_t.insert({'text': doc} for doc in documents) ```
  Created table 'search'.
  Inserting rows into \`search\`: 6 rows \[00:00, 565.03 rows/s]
  Inserted 6 rows with 0 errors.
  6 rows inserted, 12 values computed.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Perform similarity search sim = search_t.text.similarity( string='What are the health benefits of Mediterranean food?' ) search_t.order_by(sim, asc=False).limit(3).select( search_t.text, score=sim ).collect() ```
## Reranking Jina AI’s reranker models can improve search relevance by reordering results based on semantic similarity to the query. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Create a table for reranking queries rerank_t = pxt.create_table( 'jina_demo.rerank', {'query': pxt.String, 'documents': pxt.Json}, if_exists='replace', ) # Add computed column for reranking rerank_t.add_computed_column( reranked=jina.rerank( rerank_t.query, rerank_t.documents, model='jina-reranker-v2-base-multilingual', top_n=3, return_documents=True, ) ) # Insert a query with candidate documents rerank_t.insert( query="When is Apple's conference call scheduled?", documents=documents, ) ```
  Created table 'rerank'.
  Added 0 column values with 0 errors.
  Inserting rows into \`rerank\`: 1 rows \[00:00, 543.16 rows/s]
  Inserted 1 row with 0 errors.
  1 row inserted, 2 values computed.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # View the reranked results result = rerank_t.select(rerank_t.reranked).collect() result['reranked'][0] ```
  \{'usage': \{'total\_tokens': 221},
   'results': \[\{'index': 4,
     'document': "Apple's conference call to discuss fourth fiscal quarter results is scheduled for Thursday, November 2, 2023.",
     'relevance\_score': 0.64511991},
    \{'index': 2,
     'document': '20th-century innovations, from radios to smartphones, centered on electronic advancements.',
     'relevance\_score': 0.03846619},
    \{'index': 5,
     'document': "Shakespeare's works, like 'Hamlet' and 'A Midsummer Night's Dream,' endure in literature.",
     'relevance\_score': 0.02517884}]}
## Learn More * [Jina AI Documentation](https://jina.ai/) * [Jina Embeddings](https://jina.ai/embeddings/) * [Jina Reranker](https://jina.ai/reranker/) * [API Rate Limits](https://jina.ai/api-dashboard/rate-limit) # Working with llama.cpp in Pixeltable Source: https://docs.pixeltable.com/howto/providers/working-with-llama-cpp Open in Kaggle  Open in Colab  Download Notebook This documentation page is also available as an interactive notebook. You can launch the notebook in Kaggle or Colab, or download it for use with an IDE or local Jupyter installation, by clicking one of the above links. This tutorial demonstrates how to use Pixeltable’s built-in `llama.cpp` integration to run local LLMs efficiently. ### Important notes * Models are automatically downloaded from Hugging Face and cached locally * Different quantization levels are available for performance/quality tradeoffs * Consider memory usage when choosing models and quantizations ## Set up environment First, let’s install Pixeltable with llama.cpp support: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} %pip install -qU pixeltable llama-cpp-python huggingface-hub ``` ## Create a table for chat completions Now let’s create a table that will contain our inputs and responses. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} import pixeltable as pxt from pixeltable.functions import llama_cpp pxt.drop_dir('llama_demo', force=True) pxt.create_dir('llama_demo') t = pxt.create_table('llama_demo/chat', {'input': pxt.String}) ```
  Connected to Pixeltable database at: postgresql+psycopg://postgres:@/pixeltable?host=/Users/asiegel/.pixeltable/pgdata
  Created directory 'llama\_demo'.
  Created table 'chat'.
Next, we add a computed column that calls the Pixeltable `create_chat_completion` UDF, which adapts the corresponding llama.cpp API call. In our examples, we’ll use pretrained models from the Hugging Face repository. llama.cpp makes it easy to do this by specifying a repo\_id (from the URL of the model) and filename from the model repo; the model will then be downloaded and cached automatically. (If this is your first time using Pixeltable, the Pixeltable Fundamentals tutorial contains more details about table creation, computed columns, and UDFs.) For this demo we’ll use `Qwen2.5-0.5B`, a very small (0.5-billion parameter) model that still produces decent results. We’ll use `Q5_K_M` (5-bit) quantization, which gives an excellent balance of quality and efficiency. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Add a computed column that uses llama.cpp for chat completion # against the input. messages = [ {'role': 'system', 'content': 'You are a helpful assistant.'}, {'role': 'user', 'content': t.input}, ] t.add_computed_column( result=llama_cpp.create_chat_completion( messages, repo_id='Qwen/Qwen2.5-0.5B-Instruct-GGUF', repo_filename='*q5_k_m.gguf', ) ) # Extract the output content from the JSON structure returned # by llama_cpp. t.add_computed_column(output=t.result.choices[0].message.content) ```
  Added 0 column values with 0 errors in 0.01 s
  Added 0 column values with 0 errors in 0.01 s
  No rows affected.
## Test chat completion Let’s try a simple query: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Test with a simple question t.insert( [ {'input': 'What is the capital of France?'}, {'input': 'What are some edible species of fish?'}, {'input': 'Who are the most prominent classical composers?'}, ] ) ```
  Inserted 3 rows with 0 errors in 6.74 s (0.44 rows/s)
  3 rows inserted.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} t.select(t.input, t.output).collect() ```
## Comparing models Local model frameworks like `llama.cpp` make it easy to compare the output of different models. Let’s try comparing the output from `Qwen` against a somewhat larger model, `Llama-3.2-1B`. As always, when we add a new computed column to our table, it’s automatically evaluated against the existing table rows. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} t.add_computed_column( result_l3=llama_cpp.create_chat_completion( messages, repo_id='bartowski/Llama-3.2-1B-Instruct-GGUF', repo_filename='*Q5_K_M.gguf', ) ) t.add_computed_column(output_l3=t.result_l3.choices[0].message.content) t.select(t.input, t.output, t.output_l3).collect() ```
  Added 3 column values with 0 errors in 6.32 s (0.47 rows/s)
  Added 3 column values with 0 errors in 0.03 s (113.79 rows/s)
Just for fun, let’s try running against a different system prompt with a different persona. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} messages_teacher = [ { 'role': 'system', 'content': 'You are a patient school teacher. ' 'Explain concepts simply and clearly.', }, {'role': 'user', 'content': t.input}, ] t.add_computed_column( result_teacher=llama_cpp.create_chat_completion( messages_teacher, repo_id='bartowski/Llama-3.2-1B-Instruct-GGUF', repo_filename='*Q5_K_M.gguf', ) ) t.add_computed_column( output_teacher=t.result_teacher.choices[0].message.content ) t.select(t.input, t.output_teacher).collect() ```
  Added 3 column values with 0 errors in 7.70 s (0.39 rows/s)
  Added 3 column values with 0 errors in 0.02 s (143.54 rows/s)
## Additional Resources * [Pixeltable Documentation](/) * [llama.cpp GitHub](https://github.com/ggerganov/llama.cpp) # Working with Mistral AI in Pixeltable Source: https://docs.pixeltable.com/howto/providers/working-with-mistralai Open in Kaggle  Open in Colab  Download Notebook This documentation page is also available as an interactive notebook. You can launch the notebook in Kaggle or Colab, or download it for use with an IDE or local Jupyter installation, by clicking one of the above links. Pixeltable’s Mistral AI integration enables you to access Mistral’s LLM and other models via the Mistral AI API. ### Prerequisites * A Mistral AI account with an API key ([https://console.mistral.ai/api-keys/](https://console.mistral.ai/api-keys/)) ### Important notes * Mistral AI usage may incur costs based on your Mistral AI plan. * Be mindful of sensitive data and consider security measures when integrating with external services. First you’ll need to install required libraries and enter a Mistral AI API key. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} %pip install -qU pixeltable mistralai ``` ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} import getpass import os if 'MISTRAL_API_KEY' not in os.environ: os.environ['MISTRAL_API_KEY'] = getpass.getpass('Mistral AI API Key:') ``` Now let’s create a Pixeltable directory to hold the tables for our demo. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} import pixeltable as pxt # Remove the 'mistralai_demo' directory and its contents, if it exists pxt.drop_dir('mistralai_demo', force=True) pxt.create_dir('mistralai_demo') ```
  Connected to Pixeltable database at: postgresql+psycopg://postgres:@/pixeltable?host=/Users/asiegel/.pixeltable/pgdata
  Created directory 'mistralai\_demo'.
  \
## Messages Create a Table: In Pixeltable, create a table with columns to represent your input data and the columns where you want to store the results from Mistral. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} from pixeltable.functions.mistralai import chat_completions # Create a table in Pixeltable and add a computed column that calls Mistral AI t = pxt.create_table('mistralai_demo/chat', {'input': pxt.String}) messages = [{'role': 'user', 'content': t.input}] t.add_computed_column( output=chat_completions( messages=messages, model='mistral-small-latest', model_kwargs={ # Optional dict with parameters for the Mistral API 'max_tokens': 300, 'top_p': 0.9, 'temperature': 0.7, }, ) ) ```
  Created table 'chat'.
  Added 0 column values with 0 errors in 0.01 s
  No rows affected.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Parse the response into a new column t.add_computed_column(response=t.output.choices[0].message.content) ```
  Added 0 column values with 0 errors in 0.01 s
  No rows affected.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Start a conversation t.insert( [ { 'input': 'What three species of fish have the highest mercury content?' } ] ) t.select(t.input, t.response).show() ```
  Inserted 1 row with 0 errors in 2.31 s (0.43 rows/s)
### Learn more To learn more about advanced techniques like RAG operations in Pixeltable, check out the [RAG Operations in Pixeltable](/howto/use-cases/rag-operations) tutorial. # Working with Nebius Source: https://docs.pixeltable.com/howto/providers/working-with-nebius Open in Kaggle  Open in Colab  Download Notebook This documentation page is also available as an interactive notebook. You can launch the notebook in Kaggle or Colab, or download it for use with an IDE or local Jupyter installation, by clicking one of the above links. Pixeltable’s Nebius Token Factory integration enables you to access Nebius language and embedding models via an OpenAI-compatible API. ### Prerequisites * A Nebius Token Factory account with an API key ([https://tokenfactory.nebius.com/](https://tokenfactory.nebius.com/)) ### Important notes * Nebius usage may incur costs based on your Nebius plan. * Be mindful of sensitive data and consider security measures when integrating with external services. First you’ll need to install the required libraries and enter a Nebius API key. Nebius uses the OpenAI SDK as its Python API, so we need to install it in addition to Pixeltable. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} %pip install -qU pixeltable openai ``` ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} import getpass import os if 'NEBIUS_API_KEY' not in os.environ: os.environ['NEBIUS_API_KEY'] = getpass.getpass('Nebius API Key:') ``` Now let’s create a Pixeltable directory to hold the tables for our demo. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} import pixeltable as pxt # Remove the 'nebius_demo' directory and its contents, if it exists pxt.drop_dir('nebius_demo', force=True) pxt.create_dir('nebius_demo') ``` ## Chat completions Create a Table: In Pixeltable, create a table with columns to represent your input data and the columns where you want to store the results from Nebius. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} from pixeltable.functions import nebius chat_t = pxt.create_table('nebius_demo/chat', {'input': pxt.String}) messages = [{'role': 'user', 'content': chat_t.input}] chat_t.add_computed_column( output=nebius.chat_completions( messages=messages, model='Qwen/Qwen3-30B-A3B-Instruct-2507', model_kwargs={ # Optional dict with parameters for the Nebius API 'max_tokens': 300, 'temperature': 0.7, }, ) ) chat_t.add_computed_column( response=chat_t.output.choices[0].message.content ) ```
  Created table 'chat'.
  Added 0 column values with 0 errors in 0.01 s
  Added 0 column values with 0 errors in 0.00 s
  No rows affected.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Start a conversation chat_t.insert( [ {'input': 'What is the capital of France?'}, {'input': 'Explain quantum computing in one sentence.'}, ] ) chat_t.select(chat_t.input, chat_t.response).head() ```
  Inserted 2 rows with 0 errors in 4.43 s (0.45 rows/s)
## Embeddings Nebius currently serves the embedding model `Qwen/Qwen3-Embedding-8B`. By default it produces 4096-dimensional embeddings, which exceed Pixeltable’s embedding-index limit of 4000 dimensions. Request a smaller size via `model_kwargs` when you need an indexable embedding. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} emb_t = pxt.create_table('nebius_demo/embeddings', {'input': pxt.String}) emb_t.add_computed_column( embedding=nebius.embeddings( input=emb_t.input, model='Qwen/Qwen3-Embedding-8B' ) ) ```
  Created table 'embeddings'.
  Added 0 column values with 0 errors in 0.00 s
  No rows affected.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} emb_t.insert( [ { 'input': 'Nebius Token Factory provides language and embedding models.' } ] ) ```
  Inserted 1 row with 0 errors in 8.15 s (0.12 rows/s)
  1 row inserted.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} emb_t.head() ```
To build an embedding index, truncate to an indexable size (for example 1024\) with `model_kwargs`: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} indexed = nebius.embeddings.using( model='Qwen/Qwen3-Embedding-8B', model_kwargs={'dimensions': 1024} ) emb_t.add_embedding_index('input', embedding=indexed) ``` ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} emb_t.insert([{'input': 'Another sentence for you to index.'}]) ```
  Inserted 1 row with 0 errors in 3.62 s (0.28 rows/s)
  1 row inserted.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} sim = emb_t.input.similarity(string='Indexing sentences is fun.') emb_t.select(emb_t.input, sim=sim).order_by(sim, asc=False).collect() ```
### Learn more To learn more about advanced techniques like RAG operations in Pixeltable, check out the [RAG Operations in Pixeltable](/howto/use-cases/rag-operations) tutorial. If you have any questions, don’t hesitate to reach out. # Working with Ollama in Pixeltable Source: https://docs.pixeltable.com/howto/providers/working-with-ollama Open in Kaggle  Open in Colab  Download Notebook This documentation page is also available as an interactive notebook. You can launch the notebook in Kaggle or Colab, or download it for use with an IDE or local Jupyter installation, by clicking one of the above links. Ollama is a popular platform for local serving of LLMs. In this tutorial, we’ll show how to integrate Ollama models into a Pixeltable workflow. ## Install Ollama You’ll need to have an Ollama server instance to query. There are several ways to do this. ### Running on a local machine If you’re running this notebook on your own machine, running Windows, Mac OS, or Linux, you can install Ollama at: [https://ollama.com/download](https://ollama.com/download) ### Running on Google Colab * OR, if you’re running on Colab, you can install Ollama by uncommenting and running the following code. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # To install Ollama on colab, uncomment and run the following # three lines (this will also work on a local Linux machine # if you don't already have Ollama installed). # !curl -fsSL https://ollama.com/install.sh | sh # import subprocess # ollama_process = subprocess.Popen(['ollama', 'serve'], stderr=subprocess.PIPE) ``` ### Running on a remote Ollama server * OR, if you have access to an Ollama server running remotely, you can uncomment and run the following line, replacing the default URL with the URL of your remote Ollama instance. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # To run the notebook against an instance of Ollama running on a # remote server, uncomment the following line and specify the URL. # os.environs['OLLAMA_HOST'] = 'https://127.0.0.1:11434' ``` Once you’ve completed the installation, run the following commands to verify that it’s been successfully installed. This may result in an LLM being downloaded, so it may take some time. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} %pip install -qU ollama ``` ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} import ollama ollama.pull('qwen3:0.6b') ollama.generate('qwen3:0.6b', 'What is the capital of Missouri?')[ 'response' ] ```
  'The capital of Missouri is Jefferson City. Jefferson City was originally named after the French explorer Pierre-Jacques Houget and the American statesman Thomas Jefferson, who lived in this city from 1764 to 1805. It became the seat of government for most of Jefferson County when it was established in 1836. In more recent times, the name has changed several times due to various political changes and legal changes.'
## Install Pixeltable Now, let’s install Pixeltable and create a table for the demo. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} %pip install -qU pixeltable ``` ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} import pixeltable as pxt from pixeltable.functions.ollama import chat pxt.drop_dir('ollama_demo', force=True) pxt.create_dir('ollama_demo') ```
  Connected to Pixeltable database at: postgresql+psycopg://postgres:@/pixeltable?host=/Users/asiegel/.pixeltable/pgdata
  Created directory 'ollama\_demo'.
  \
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} t = pxt.create_table('ollama_demo/chat', {'input': pxt.String}) messages = [{'role': 'user', 'content': t.input}] # Add a computed column that runs the model to generate responses t.add_computed_column( output=chat( messages=messages, model='qwen3:0.6b', # These parameters are optional and can be used to tune model behavior: options={'max_tokens': 300, 'top_p': 0.9, 'temperature': 0.5}, ) ) ```
  Created table 'chat'.
  Added 0 column values with 0 errors in 0.01 s
  No rows affected.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Extract the message content into a separate column t.add_computed_column(response=t.output.message.content) ```
  Added 0 column values with 0 errors in 0.01 s
  No rows affected.
We can insert our input prompts into the table now. As always, Pixeltable automatically updates the computed columns by calling the relevant Ollama endpoint. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Start a conversation t.insert(input='What are the most popular services for LLM inference?') t.select(t.input, t.response).show() ```
  Inserted 1 row with 0 errors in 1.28 s (0.78 rows/s)
### Learn More To learn more about advanced techniques like RAG operations in Pixeltable, check out the [RAG Operations in Pixeltable](/howto/use-cases/rag-operations) tutorial. # Working with OpenAI in Pixeltable Source: https://docs.pixeltable.com/howto/providers/working-with-openai Open in Kaggle  Open in Colab  Download Notebook This documentation page is also available as an interactive notebook. You can launch the notebook in Kaggle or Colab, or download it for use with an IDE or local Jupyter installation, by clicking one of the above links. Pixeltable’s OpenAI integration enables you to access OpenAI models via the OpenAI API. ### Prerequisites * An OpenAI account with an API key ([https://openai.com/index/openai-api/](https://openai.com/index/openai-api/)) ### Important notes * OpenAI usage may incur costs based on your OpenAI plan. * Be mindful of sensitive data and consider security measures when integrating with external services. First you’ll need to install required libraries and enter your OpenAI API key. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} %pip install -qU pixeltable openai ``` ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} import getpass import os if 'OPENAI_API_KEY' not in os.environ: os.environ['OPENAI_API_KEY'] = getpass.getpass( 'Enter your OpenAI API key:' ) ``` Now let’s create a Pixeltable directory to hold the tables for our demo. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} import pixeltable as pxt # Remove the 'openai_demo' directory and its contents, if it exists pxt.drop_dir('openai_demo', force=True) pxt.create_dir('openai_demo') ```
  Connected to Pixeltable database at: postgresql+psycopg://postgres:@/pixeltable?host=/Users/asiegel/.pixeltable/pgdata
  Pixeltable dashboard available at: [http://localhost:22089](http://localhost:22089)
  Created directory 'openai\_demo'.
  \
## Chat completions Create a Table: In Pixeltable, create a table with columns to represent your input data and the columns where you want to store the results from OpenAI. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} from pixeltable.functions import openai # Create a table in Pixeltable and add a computed column that calls OpenAI t = pxt.create_table('openai_demo/chat', {'input': pxt.String}) messages = [{'role': 'user', 'content': t.input}] t.add_computed_column( output=openai.chat_completions( messages=messages, model='gpt-4o-mini', model_kwargs={ # Optional dict with parameters for the OpenAI API 'max_tokens': 300, 'top_p': 0.9, 'temperature': 0.7, }, ) ) ```
  Created table 'chat'.
  Added 0 column values with 0 errors in 0.01 s
  No rows affected.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Parse the response into a new column t.add_computed_column(response=t.output.choices[0].message.content) ```
  Added 0 column values with 0 errors in 0.02 s
  No rows affected.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Start a conversation t.insert( [{'input': 'How many islands are in the Aleutian island chain?'}] ) t.select(t.input, t.response).head() ```
  Inserted 1 row with 0 errors in 2.17 s (0.46 rows/s)
## Responses API The [Responses API](https://developers.openai.com/api/docs/api-reference/responses) is OpenAI’s recommended API for new projects. It offers built-in tools (web search, file search, code interpreter), improved reasoning model performance, lower costs, and cleaner semantics compared to Chat Completions. Key benefits: * **`instructions` parameter**: Cleanly separates system-level guidance from user input * **`output_text` field**: Convenient access to the response text * **Built-in tools**: Use `web_search`, `file_search`, `code_interpreter` natively * **Multi-turn via `previous_response_id`**: Simpler conversation state management ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Create a table and add a computed column using the Responses API resp_t = pxt.create_table('openai_demo/responses', {'input': pxt.String}) messages = [{'role': 'user', 'content': resp_t.input}] resp_t.add_computed_column( output=openai.responses( input=messages, model='gpt-4o-mini', model_kwargs={ 'instructions': 'You are a helpful assistant. Be concise.', 'temperature': 0.7, 'max_output_tokens': 300, }, ) ) # The Responses API provides a convenient 'output_text' field resp_t.add_computed_column(response=resp_t.output.output_text) ```
  Created table 'responses'.
  Added 0 column values with 0 errors in 0.01 s
  Added 0 column values with 0 errors in 0.01 s
  No rows affected.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Insert data and view results resp_t.insert([{'input': 'What are the three largest moons of Jupiter?'}]) resp_t.select(resp_t.input, resp_t.response).head() ```
  Inserted 1 row with 0 errors in 1.22 s (0.82 rows/s)
## Embeddings Note: OpenAI Embeddings API is not available with free tier API keys ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} emb_t = pxt.create_table('openai_demo/embeddings', {'input': pxt.String}) emb_t.add_computed_column( embedding=openai.embeddings( input=emb_t.input, model='text-embedding-3-small' ) ) ```
  Created table 'embeddings'.
  Added 0 column values with 0 errors in 0.00 s
  No rows affected.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} emb_t.insert( [{'input': 'OpenAI provides a variety of embeddings models.'}] ) ```
  Inserted 1 row with 0 errors in 1.03 s (0.97 rows/s)
  1 row inserted.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} emb_t.head() ```
## Image generations ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} image_t = pxt.create_table('openai_demo/images', {'input': pxt.String}) image_t.add_computed_column( img=openai.image_generations(image_t.input, model='gpt-image-2') ) ```
  Created table 'images'.
  Added 0 column values with 0 errors in 0.01 s
  No rows affected.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} image_t.insert( [ { 'input': 'A giant Pixel floating in the open ocean in a sea of data' } ] ) ```
  Inserted 1 row with 0 errors in 12.21 s (0.08 rows/s)
  1 row inserted.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} image_t ```
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} image_t.head() ```
## Audio Transcription ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} audio_t = pxt.create_table('openai_demo/audio', {'input': pxt.Audio}) audio_t.add_computed_column( result=openai.transcriptions( audio_t.input, model='whisper-1', model_kwargs={ 'language': 'en', 'prompt': 'Transcribe the contents of this recording.', }, ) ) ```
  Created table 'audio'.
  Added 0 column values with 0 errors in 0.00 s
  No rows affected.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} url = ( 'https://github.com/pixeltable/pixeltable/raw/release/tests/data/audio/' 'jfk_1961_0109_cityuponahill-excerpt.flac' ) audio_t.insert([{'input': url}]) ```
  Inserted 1 row with 0 errors in 2.66 s (0.38 rows/s)
  1 row inserted.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} audio_t.head() ``` ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} audio_t.head()[0]['result']['text'] ```
  'Allow me to illustrate. During the last 60 days, I have been at the task of constructing an administration. It has been a long and deliberate process. Some have counseled greater speed. Others have counseled more expedient tests. But I have been guided by the standard John Winthrop set before his shipmates on the flagship Arabella 331 years ago, as they too faced the task of building a new government on a perilous frontier. We must always consider, he said, that we shall be as a city upon a hill. The eyes of all peoples are upon us. Today the eyes of all people are truly upon us. And our governments, in every branch, at every level,'
### Learn more To learn more about advanced techniques like RAG operations in Pixeltable, check out the [RAG Operations in Pixeltable](/howto/use-cases/rag-operations) tutorial. # Working with OpenRouter in Pixeltable Source: https://docs.pixeltable.com/howto/providers/working-with-openrouter Open in Kaggle  Open in Colab  Download Notebook This documentation page is also available as an interactive notebook. You can launch the notebook in Kaggle or Colab, or download it for use with an IDE or local Jupyter installation, by clicking one of the above links. Pixeltable’s OpenRouter integration enables you to access multiple LLM providers through a unified API via OpenRouter. ### Prerequisites * An OpenRouter account with an API key ([https://openrouter.ai](https://openrouter.ai)) ### Important notes * OpenRouter usage may incur costs based on the models you use and your usage volume. * Be mindful of sensitive data and consider security measures when integrating with external services. First you’ll need to install required libraries and enter your OpenRouter API key. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} %pip install -qU pixeltable openai ``` ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} import getpass import os if 'OPENROUTER_API_KEY' not in os.environ: os.environ['OPENROUTER_API_KEY'] = getpass.getpass( 'Enter your OpenRouter API key:' ) ``` Now let’s create a Pixeltable directory to hold the tables for our demo. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} import pixeltable as pxt # Remove the 'openrouter_demo' directory and its contents, if it exists pxt.drop_dir('openrouter_demo', force=True) pxt.create_dir('openrouter_demo') ```
  Connected to Pixeltable database at: postgresql+psycopg://postgres:@/pixeltable?host=/Users/pjlb/.pixeltable/pgdata
  Created directory 'openrouter\_demo'.
  \
## Chat completions Create a Table: In Pixeltable, create a table with columns to represent your input data and the columns where you want to store the results from OpenRouter. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} from pixeltable.functions import openrouter # Create a table in Pixeltable and add a computed column that calls OpenRouter t = pxt.create_table('openrouter_demo/chat', {'input': pxt.String}) messages = [{'role': 'user', 'content': t.input}] t.add_computed_column( output=openrouter.chat_completions( messages=messages, model='anthropic/claude-sonnet-4', model_kwargs={ # Optional dict with parameters compatible with the model 'max_tokens': 300, 'temperature': 0.7, }, ) ) ```
  Created table 'chat'.
  Added 0 column values with 0 errors in 0.01 s
  No rows affected.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Parse the response into a new column t.add_computed_column(response=t.output.choices[0].message.content) ```
  Added 0 column values with 0 errors in 0.01 s
  No rows affected.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Start a conversation t.insert( [ {'input': 'How many species of felids have been classified?'}, {'input': 'Can you make me a coffee?'}, ] ) t.select(t.input, t.response).head() ```
  Inserted 2 rows with 0 errors in 7.59 s (0.26 rows/s)
## Using different models One of OpenRouter’s key benefits is easy access to models from multiple providers. Let’s create a table that compares responses from Anthropic Claude, OpenAI GPT-4, and Meta Llama. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Create a table to compare different models compare_t = pxt.create_table( 'openrouter_demo/compare_models', {'prompt': pxt.String} ) messages = [{'role': 'user', 'content': compare_t.prompt}] # Add responses from different models compare_t.add_computed_column( claude=openrouter.chat_completions( messages=messages, model='anthropic/claude-sonnet-4', model_kwargs={'max_tokens': 150}, ) .choices[0] .message.content ) compare_t.add_computed_column( gpt4=openrouter.chat_completions( messages=messages, model='openai/gpt-4o-mini', model_kwargs={'max_tokens': 150}, ) .choices[0] .message.content ) compare_t.add_computed_column( llama=openrouter.chat_completions( messages=messages, model='meta-llama/llama-3.3-70b-instruct', model_kwargs={'max_tokens': 150}, ) .choices[0] .message.content ) ```
  Created table 'compare\_models'.
  Added 0 column values with 0 errors in 0.01 s
  Added 0 column values with 0 errors in 0.01 s
  Added 0 column values with 0 errors in 0.01 s
  No rows affected.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Insert a prompt and compare responses compare_t.insert( [{'prompt': 'Explain quantum entanglement in one sentence.'}] ) compare_t.select( compare_t.prompt, compare_t.claude, compare_t.gpt4, compare_t.llama ).head() ```
  Inserted 1 row with 0 errors in 1.27 s (0.79 rows/s)
## Advanced features: provider routing OpenRouter allows you to specify provider preferences for fallback behavior and cost optimization. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Create a table with provider routing routing_t = pxt.create_table( 'openrouter_demo/routing', {'input': pxt.String} ) messages = [{'role': 'user', 'content': routing_t.input}] routing_t.add_computed_column( output=openrouter.chat_completions( messages=messages, model='anthropic/claude-sonnet-4', model_kwargs={'max_tokens': 300}, # Specify provider preferences provider={ 'order': [ 'Anthropic', 'OpenAI', ], # Try Anthropic first, then OpenAI 'allow_fallbacks': True, }, ) ) routing_t.add_computed_column( response=routing_t.output.choices[0].message.content ) ```
  Created table 'routing'.
  Added 0 column values with 0 errors in 0.01 s
  Added 0 column values with 0 errors in 0.01 s
  No rows affected.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} routing_t.insert([{'input': 'What are the primary colors?'}]) routing_t.select(routing_t.input, routing_t.response).head() ```
  Inserted 1 row with 0 errors in 3.97 s (0.25 rows/s)
## Advanced Features: Context Window Optimization OpenRouter supports transforms like ‘middle-out’ to optimize handling of long contexts. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Create a table with transforms for long context optimization transform_t = pxt.create_table( 'openrouter_demo/transforms', {'long_context': pxt.String} ) messages = [{'role': 'user', 'content': transform_t.long_context}] transform_t.add_computed_column( output=openrouter.chat_completions( messages=messages, model='openai/gpt-4o-mini', model_kwargs={'max_tokens': 200}, # Apply middle-out transform for better long context handling transforms=['middle-out'], ) ) transform_t.add_computed_column( response=transform_t.output.choices[0].message.content ) ```
  Created table 'transforms'.
  Added 0 column values with 0 errors in 0.01 s
  Added 0 column values with 0 errors in 0.01 s
  No rows affected.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Example with longer context long_text = """ Artificial intelligence has transformed many industries. Machine learning algorithms can now detect patterns in data that humans might miss. Deep learning has revolutionized computer vision and natural language processing. The future of AI looks promising with developments in areas like reinforcement learning and generative models. Question: What are the main AI developments mentioned? """ transform_t.insert([{'long_context': long_text}]) transform_t.select(transform_t.response).head() ```
  Inserted 1 row with 0 errors in 1.82 s (0.55 rows/s)
### Learn more To learn more about advanced techniques like RAG operations in Pixeltable, check out the [RAG Operations in Pixeltable](/howto/use-cases/rag-operations) tutorial. For more information about OpenRouter’s features and available models, visit: * [OpenRouter Documentation](https://openrouter.ai/docs) * [Available Models](https://openrouter.ai/models) # Working with Pydantic in Pixeltable Source: https://docs.pixeltable.com/howto/providers/working-with-pydantic Open in Kaggle  Open in Colab  Download Notebook This documentation page is also available as an interactive notebook. You can launch the notebook in Kaggle or Colab, or download it for use with an IDE or local Jupyter installation, by clicking one of the above links. Pixeltable’s Pydantic integration enables type-safe data insertion using Pydantic models. Instead of inserting raw dictionaries, you can define structured models with validation and insert them directly into Pixeltable tables. ### Benefits * **Type Safety**: Pydantic validates data before insertion * **IDE Support**: Autocomplete and type hints for your data * **Self-Documenting**: Models serve as schema documentation * **Validation**: Built-in data validation via Pydantic ### Important notes * Pydantic model fields map to Pixeltable columns by name * Computed columns are automatically skipped during insertion * Nested Pydantic models map to JSON columns ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} %pip install -qU pixeltable pydantic ``` ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} import pixeltable as pxt pxt.drop_dir('pydantic_demo', force=True) pxt.create_dir('pydantic_demo') ```
  Connected to Pixeltable database at: postgresql+psycopg://postgres:@/pixeltable?host=/Users/asiegel/.pixeltable/pgdata
  Created directory 'pydantic\_demo'.
  \
## Basic usage: scalar types Define a Pydantic model with fields that match your table columns. Pixeltable automatically maps Python types to Pixeltable types:
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} import datetime import pydantic from enum import Enum from typing import Literal # Define an enum for product categories class Category(Enum): ELECTRONICS = 1 CLOTHING = 2 BOOKS = 3 # Define a Pydantic model class Product(pydantic.BaseModel): name: str price: float in_stock: bool category: Category rating: Literal['poor', 'average', 'good', 'excellent'] created_at: datetime.datetime description: str | None = None # Optional field ``` ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Create a table with matching schema products = pxt.create_table( 'pydantic_demo/products', { 'name': pxt.String, 'price': pxt.Float, 'in_stock': pxt.Bool, 'category': pxt.Int, # Enum values are integers 'rating': pxt.String, # Literal values 'created_at': pxt.Timestamp, 'description': pxt.String | None, # Nullable }, ) ```
  Created table 'products'.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Create Pydantic model instances now = datetime.datetime.now() product_data = [ Product( name='Wireless Headphones', price=79.99, in_stock=True, category=Category.ELECTRONICS, rating='excellent', created_at=now, description='High-quality wireless headphones with noise cancellation', ), Product( name='Python Cookbook', price=49.99, in_stock=True, category=Category.BOOKS, rating='good', created_at=now, ), Product( name='Running Shoes', price=129.99, in_stock=False, category=Category.CLOTHING, rating='average', created_at=now, description='Lightweight running shoes', ), ] # Insert Pydantic models directly products.insert(product_data) products.collect() ```
  Inserted 3 rows with 0 errors in 0.02 s (146.18 rows/s)
## Nested models and JSON columns Nested Pydantic models automatically map to Pixeltable JSON columns. This is useful for storing structured metadata. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Define nested models class Address(pydantic.BaseModel): street: str city: str country: str zip_code: str class ContactInfo(pydantic.BaseModel): email: str phone: str | None = None address: Address class Customer(pydantic.BaseModel): customer_id: str name: str contact: ContactInfo # Nested model → JSON column ``` ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Create table with JSON column for nested data customers = pxt.create_table( 'pydantic_demo/customers', { 'customer_id': pxt.String, 'name': pxt.String, 'contact': pxt.Json, # Nested model stored as JSON }, ) ```
  Created table 'customers'.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Insert nested data customer_data = [ Customer( customer_id='C001', name='Alice Johnson', contact=ContactInfo( email='alice@example.com', phone='+1-555-0101', address=Address( street='123 Main St', city='San Francisco', country='USA', zip_code='94102', ), ), ), Customer( customer_id='C002', name='Bob Smith', contact=ContactInfo( email='bob@example.com', address=Address( street='456 Oak Ave', city='New York', country='USA', zip_code='10001', ), ), ), ] customers.insert(customer_data) customers.collect() ```
  Inserted 2 rows with 0 errors in 0.01 s (227.55 rows/s)
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Query nested JSON fields using Pixeltable's JSON path syntax customers.select( customers.name, email=customers.contact.email, city=customers.contact.address.city, ).collect() ```
## Media files with Pydantic For media columns (Image, Video, Audio, Document), use `str` or `Path` fields in your Pydantic model to specify file paths or URLs. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} from pathlib import Path class ImageRecord(pydantic.BaseModel): title: str image_url: str # URLs or file paths as strings tags: list[str] # Create table with Image column images = pxt.create_table( 'pydantic_demo/images', { 'title': pxt.String, 'image_url': pxt.Image, # Media column 'tags': pxt.Json, }, ) ```
  Created table 'images'.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Insert image records with URLs base_url = 'https://raw.githubusercontent.com/pixeltable/pixeltable/main/docs/resources/images' image_data = [ ImageRecord( title='Sample Image', image_url=f'{base_url}/000000000036.jpg', tags=['sample', 'test', 'image'], ) ] images.insert(image_data) images.select(images.title, images.image_url, images.tags).collect() ```
  Inserted 1 row with 0 errors in 0.27 s (3.74 rows/s)
## Working with Computed Columns Pydantic models work seamlessly with computed columns. Simply omit computed column fields from your model - Pixeltable will skip them during insertion. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Model only includes input columns class Article(pydantic.BaseModel): title: str content: str # Create table with computed column articles = pxt.create_table( 'pydantic_demo/articles', {'title': pxt.String, 'content': pxt.String} ) # Add a computed column articles.add_computed_column( word_count=pxt.functions.string.count(articles.content, r'\S+') ) ```
  Created table 'articles'.
  Added 0 column values with 0 errors in 0.01 s
  No rows affected.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Insert data - computed columns are automatically calculated article_data = [ Article( title='Getting Started with Pixeltable', content='Pixeltable is a powerful tool for building AI applications. It provides automatic versioning and incremental computation.', ), Article( title='Type Safety in Python', content='Using Pydantic with Pixeltable provides type safety and validation for your data pipelines.', ), ] articles.insert(article_data) articles.select(articles.title, articles.word_count).collect() ```
  Inserted 2 rows with 0 errors in 0.01 s (186.43 rows/s)
## Optional Fields and Defaults Pydantic’s optional fields with defaults work naturally with Pixeltable’s nullable columns. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} class Task(pydantic.BaseModel): title: str priority: int = 1 # Default value due_date: datetime.datetime | None = None # Optional notes: str | None = None # Optional tasks = pxt.create_table( 'pydantic_demo/tasks', { 'title': pxt.String, 'priority': pxt.Int, 'due_date': pxt.Timestamp | None, # Nullable 'notes': pxt.String | None, # Nullable }, ) # Insert with and without optional fields tasks.insert( [ Task( title='Complete project', priority=3, due_date=datetime.datetime(2025, 12, 31), ), Task( title='Review code' ), # Uses default priority=1, None for optionals Task(title='Write docs', notes='Include examples'), ] ) tasks.collect() ```
  Created table 'tasks'.
  Inserted 3 rows with 0 errors in 0.01 s (408.88 rows/s)
## Type Mapping Reference Here’s the complete mapping between Pydantic/Python types and Pixeltable types:
## Learn More For more information about working with Pydantic in Pixeltable: * [Pixeltable Documentation](https://docs.pixeltable.com) * [Pydantic Documentation](https://docs.pydantic.dev) * [Type Safety Blog Post](https://www.pixeltable.com/blog/pydantic-integration-type-safety) # Working with Replicate in Pixeltable Source: https://docs.pixeltable.com/howto/providers/working-with-replicate Open in Kaggle  Open in Colab  Download Notebook This documentation page is also available as an interactive notebook. You can launch the notebook in Kaggle or Colab, or download it for use with an IDE or local Jupyter installation, by clicking one of the above links. Pixeltable’s Replicate integration enables you to access Replicate’s models via the Replicate API. ### Prerequisites * A Replicate account with an API token. ### Important notes * Replicate usage may incur costs based on your Replicate plan. * Be mindful of sensitive data and consider security measures when integrating with external services. First you’ll need to install required libraries and enter a Replicate API token. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} %pip install -qU pixeltable replicate ``` ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} import getpass import os if 'REPLICATE_API_TOKEN' not in os.environ: os.environ['REPLICATE_API_TOKEN'] = getpass.getpass( 'Replicate API Token:' ) ``` Now let’s create a Pixeltable directory to hold the tables for our demo. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} import pixeltable as pxt # Remove the `replicate_demo` directory and its contents, if it exists pxt.drop_dir('replicate_demo', force=True) pxt.create_dir('replicate_demo') ```
  Connected to Pixeltable database at: postgresql+psycopg://postgres:@/pixeltable?host=/Users/asiegel/.pixeltable/pgdata
  Created directory 'replicate\_demo'.
  \
## Chat completions Create a Table: In Pixeltable, create a table with columns to represent your input data and the columns where you want to store the results from Replicate. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} from pixeltable.functions.replicate import run # Create a table in Pixeltable and pick a model hosted on Replicate with some parameters t = pxt.create_table('replicate_demo/chat', {'prompt': pxt.String}) input = { 'system_prompt': 'You are a helpful assistant.', 'prompt': t.prompt, # These parameters are optional and can be used to tune model behavior: 'max_tokens': 300, 'top_p': 0.9, 'temperature': 0.8, } t.add_computed_column( output=run(input, ref='meta/meta-llama-3-8b-instruct') ) ```
  Created table 'chat'.
  Added 0 column values with 0 errors in 0.01 s
  No rows affected.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Parse the response into a new column t.add_computed_column(response=pxt.functions.string.join('', t.output)) ```
  Added 0 column values with 0 errors in 0.02 s
  No rows affected.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Start a conversation t.insert([{'prompt': 'What foods are rich in selenium?'}]) t.select(t.prompt, t.response).show() ```
  Inserted 1 row with 0 errors in 4.45 s (0.22 rows/s)
## Image generation Here’s an example that shows how to use Replicate’s image generation models with Pixeltable. We’ll use the FLUX Schnell model. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} t = pxt.create_table('replicate_demo/images', {'prompt': pxt.String}) input = {'prompt': t.prompt, 'go_fast': True, 'megapixels': '1'} t.add_computed_column( output=run(input, ref='black-forest-labs/flux-schnell') ) ```
  Created table 'images'.
  Added 0 column values with 0 errors in 0.01 s
  No rows affected.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} t.insert( [ { 'prompt': 'Draw a pencil sketch of a friendly dinosaur playing tennis in a cornfield.' } ] ) ```
  Inserted 1 row with 0 errors in 0.99 s (1.01 rows/s)
  1 row inserted.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} t.select(t.prompt, t.output).collect() ```
We see that Replicate returns our image as an array containing a single URL. To turn it into an actual image, we cast the string to type `pxt.Image` in a new computed column: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} t.add_computed_column(image=t.output[0].astype(pxt.Image)) t.select(t.image).collect() ```
  Added 1 column value with 0 errors in 0.02 s (53.36 rows/s)
### Learn more To learn more about advanced techniques like RAG operations in Pixeltable, check out the [RAG Operations in Pixeltable](/howto/use-cases/rag-operations) tutorial. # Working with RunwayML in Pixeltable Source: https://docs.pixeltable.com/howto/providers/working-with-runwayml Open in Kaggle  Open in Colab  Download Notebook This documentation page is also available as an interactive notebook. You can launch the notebook in Kaggle or Colab, or download it for use with an IDE or local Jupyter installation, by clicking one of the above links. Pixeltable’s RunwayML integration lets you generate and transform images and videos using RunwayML’s Gen-4.5, Veo, and other models, directly from your Pixeltable tables. ## What is RunwayML? [RunwayML](https://runwayml.com/) is an AI creative platform offering state-of-the-art generative models for images and video. Pixeltable wraps four endpoints: * **`text_to_image`**: Generate images from text prompts with optional reference images * **`text_to_video`**: Generate videos from text prompts * **`image_to_video`**: Animate a still image into a video * **`video_to_video`**: Transform an existing video with text guidance ### Documentation * [Pixeltable RunwayML SDK Reference](/sdk/latest/runwayml) * [RunwayML API Reference](https://docs.dev.runwayml.com/api/) ## Prerequisites 1. A RunwayML account with an API secret from [RunwayML](https://app.runwayml.com/) 2. `pip install runwayml` **Important:** RunwayML API calls consume credits based on your plan. Image and video generation can be expensive — monitor your usage to avoid unexpected charges. ## Setup ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} %pip install -qU pixeltable runwayml ``` ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} import getpass import os if 'RUNWAYML_API_SECRET' not in os.environ: os.environ['RUNWAYML_API_SECRET'] = getpass.getpass( 'RunwayML API Secret: ' ) ``` To read more about working with API keys in Pixeltable, see [Configuration](/platform/configuration). ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} import pixeltable as pxt from pixeltable.functions import runwayml pxt.drop_dir('runwayml_demo', force=True) pxt.create_dir('runwayml_demo') ```
  Created directory 'runwayml\_demo'.
  \
## Text-to-Video Generation Generate videos from text prompts. The `text_to_video` function returns a JSON response containing the output video URL. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} t2v = pxt.create_table( 'runwayml_demo.text_to_video', {'prompt': pxt.String} ) t2v.add_computed_column( response=runwayml.text_to_video( t2v.prompt, model='gen4.5', ratio='1280:720', duration=5 ) ) # Extract the video URL from the response t2v.add_computed_column(video=t2v.response['output'][0].astype(pxt.Video)) ```
  Created table 'text\_to\_video'.
  Added 0 column values with 0 errors in 0.00 s
  Added 0 column values with 0 errors in 0.00 s
  No rows affected.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} t2v.insert( [ { 'prompt': 'A cinematic aerial shot of a coastal city at golden hour, waves crashing against the shore' } ] ) ```
  Inserted 1 row with 0 errors in 105.52 s (0.01 rows/s)
  1 row inserted.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} t2v.select(t2v.prompt, t2v.video).collect() ``` ## Image-to-Video Generation Animate a still image into a video. You can optionally add a text prompt to guide the motion. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} i2v = pxt.create_table( 'runwayml_demo.image_to_video', {'image': pxt.Image, 'prompt': pxt.String}, ) i2v.add_computed_column( response=runwayml.image_to_video( i2v.image, model='gen4.5', ratio='1280:720', prompt_text=i2v.prompt, duration=5, ) ) i2v.add_computed_column(video=i2v.response['output'][0].astype(pxt.Video)) ```
  Created table 'image\_to\_video'.
  Added 0 column values with 0 errors in 0.01 s
  Added 0 column values with 0 errors in 0.01 s
  No rows affected.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} i2v.insert( [ { 'image': 'https://upload.wikimedia.org/wikipedia/commons/a/a7/Camponotus_flavomarginatus_ant.jpg', 'prompt': 'The ant slowly walks across the leaf', } ] ) ```
  Inserted 1 row with 0 errors in 145.73 s (0.01 rows/s)
  1 row inserted.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} i2v.select(i2v.image, i2v.video).collect() ``` ## Text-to-Image Generation Generate images from text prompts with reference images. The `text_to_image` function requires at least one reference image. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} t2i = pxt.create_table( 'runwayml_demo.text_to_image', {'prompt': pxt.String, 'ref_image': pxt.Image}, ) t2i.add_computed_column( response=runwayml.text_to_image( t2i.prompt, [t2i.ref_image], model='gen4_image', ratio='1280:720' ) ) # The response contains a list of output image URLs t2i.add_computed_column(image=t2i.response['output'][0].astype(pxt.Image)) ```
  Created table 'text\_to\_image'.
  Added 0 column values with 0 errors in 0.01 s
  Added 0 column values with 0 errors in 0.01 s
  No rows affected.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} t2i.insert( [ { 'prompt': 'A photorealistic painting in the same style as the reference', 'ref_image': 'https://upload.wikimedia.org/wikipedia/commons/6/6a/Mona_Lisa.jpg', } ] ) ```
  Inserted 1 row with 0 errors in 21.70 s (0.05 rows/s)
  1 row inserted.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} t2i.select(t2i.ref_image, t2i.image).collect() ``` ## Video-to-Video Transformation Transform an existing video with text guidance. Note that `video_to_video` requires a publicly accessible HTTPS URL for the input video (not a local file). ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} v2v = pxt.create_table( 'runwayml_demo.video_to_video', {'video_url': pxt.String, 'style_prompt': pxt.String}, ) v2v.add_computed_column( response=runwayml.video_to_video( v2v.video_url, v2v.style_prompt, model='aleph2', ratio='1280:720' ) ) v2v.add_computed_column(video=v2v.response['output'][0].astype(pxt.Video)) ``` ## Using `model_kwargs` for Advanced Parameters All RunwayML functions accept an optional `model_kwargs` parameter for passing additional API parameters not exposed as explicit arguments. Refer to the [RunwayML API docs](https://docs.dev.runwayml.com/api/) for the full list of supported parameters per endpoint. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Example: pass additional parameters via model_kwargs advanced = pxt.create_table( 'runwayml_demo.advanced', {'prompt': pxt.String} ) advanced.add_computed_column( response=runwayml.text_to_video( advanced.prompt, model='gen4.5', ratio='1280:720', duration=5, model_kwargs={'audio': True, 'seed': 42}, ) ) ``` ## Learn More * [RunwayML SDK Reference](/sdk/latest/runwayml) — Full API details for all RunwayML functions * [RunwayML API Documentation](https://docs.dev.runwayml.com/api/) — Official RunwayML API reference * [Working with fal.ai](/howto/providers/working-with-fal) — Another image/video generation integration # Working with Tigris in Pixeltable Source: https://docs.pixeltable.com/howto/providers/working-with-tigris Open in Kaggle  Open in Colab  Download Notebook This documentation page is also available as an interactive notebook. You can launch the notebook in Kaggle or Colab, or download it for use with an IDE or local Jupyter installation, by clicking one of the above links. This tutorial demonstrates how to configure Pixeltable to use [Tigris](https://tigrisdata.com) for storage. This lets you store unlimited amounts of images in Tigris’ global data plane, allowing your images to load fast everywhere. ## Prerequisites * A Tigris account, bucket, and access keypair ([https://storage.new](https://storage.new)) ## Important notes * Tigris usage may incur costs based on your plan. * Be mindful of sensitive data and consider security measures when integrating with external services. First you need to install required libraries and enter a Tigris access keypair obtained via the Tigris Admin Console. ## Set up environment First, let’s install Pixeltable: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} %pip install -qU pixeltable datasets ``` ## Configure authentication These steps will have you enter in your Tigris credentials: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} import os from getpass import getpass os.environ['AWS_ACCESS_KEY_ID'] = getpass('Tigris access key ID') os.environ['AWS_SECRET_ACCESS_KEY'] = getpass('Tigris secret access key') bucket_name = getpass('Tigris bucket name') os.environ['AWS_ENDPOINT_URL_S3'] = 'https://t3.storage.dev' os.environ['AWS_REGION'] = 'auto' os.environ['PIXELTABLE_INPUT_MEDIA_DEST'] = f's3://{bucket_name}/input/' os.environ['PIXELTABLE_OUTPUT_MEDIA_DEST'] = f's3://{bucket_name}/output/' ``` ## Create a table for images Now let’s create a table that will contain images from the [XeIaso/botw-screenshots-captioned](https://huggingface.co/datasets/XeIaso/botw-screenshots-captioned) dataset: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} import pixeltable as pxt from datasets import load_dataset # Create directory for this demo pxt.drop_dir('tigris', force=True) pxt.create_dir('tigris', if_exists='replace') # Load the dataset ds = load_dataset('XeIaso/botw-screenshots-captioned') # Import it to pixeltable with the name screenshots pxt.drop_table('tigris/screenshots', force=True) screenshots = pxt.create_table( 'tigris/screenshots', source=ds, if_exists='replace' ) ```
  Created directory 'tigris'.
  Created table 'screenshots'.
  Inserting rows into \`screenshots\`: 100 rows \[00:01, 51.72 rows/s]
  Inserting rows into \`screenshots\`: 100 rows \[00:01, 55.57 rows/s]
  Inserting rows into \`screenshots\`: 100 rows \[00:01, 52.74 rows/s]
  Inserting rows into \`screenshots\`: 100 rows \[00:02, 33.96 rows/s]
  Inserting rows into \`screenshots\`: 100 rows \[00:02, 42.64 rows/s]
  Inserting rows into \`screenshots\`: 100 rows \[00:02, 39.65 rows/s]
  Inserting rows into \`screenshots\`: 100 rows \[00:02, 47.36 rows/s]
  Inserting rows into \`screenshots\`: 28 rows \[00:00, 6786.12 rows/s]
  Inserted 728 rows with 0 errors.
Once the import is done, you can create thumbnails with a [computed column](/tutorials/computed-columns): ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Add a computed column for thumbnails # Uses output_media_dest by default, or specify a custom destination screenshots.add_computed_column( thumbnail=screenshots.image.resize((256, 256)), destination=f's3://{bucket_name}/botw-screenshots/thumbnails/', ) ```
  Added 728 column values with 0 errors.
  728 rows updated, 728 values computed.
And then inspect that with the `collect` method: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} results = screenshots.limit(1).collect() results ```
## Getting URLs for your files When your files are in object storage, you can get URLs that point directly to them. These URLs work in HTML, APIs, or any application you need to serve media with. Fetch them with the `.fileurl` property: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} screenshots.select( image=screenshots.image, image_url=screenshots.image.fileurl, thumbnail=screenshots.thumbnail, thumbnail_url=screenshots.thumbnail.fileurl, ).limit(1).collect() ```
## Generating Presigned URLs For private buckets or when you need time-limited access to files, use presigned URLs. These are temporary, authenticated URLs that allow anyone to access your files for a limited time without needing credentials. Use the `presigned_url` function from `pixeltable.functions.net`: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} from pixeltable.functions import net # Generate presigned URLs with 1-hour expiration (3600 seconds) screenshots.select( image=screenshots.image, image_url=screenshots.image.fileurl, image_presigned=net.presigned_url(screenshots.image.fileurl, 3600), thumbnail=screenshots.thumbnail, thumbnail_url=screenshots.thumbnail.fileurl, thumbnail_presigned=net.presigned_url( screenshots.thumbnail.fileurl, 3600 ), ).limit(1).collect() ```
### Common expiration times
## What you learned * When you configure Pixeltable to use Tigris to store images, adding images transparently uploads them into Tigris for global distribution. * You can override where images are stored in Tigris using the `destination=` kwarg when creating computed columns. * Use the `.fileurl` property in queries to get URLs for your stored files. * Use `net.presigned_url()` to generate time-limited, authenticated URLs for private bucket access. Pixeltable handles everything else for you. ## Next steps * See the [Cloud Storage documentation](/integrations/cloud-storage) for complete provider setup and authentication details. * Check out [Pixeltable Configuration](/platform/configuration) for all config options. ## Additional Resources * [Pixeltable Documentation](/) * [Tigris Documentation](https://www.tigrisdata.com/docs/) # Working with Together AI in Pixeltable Source: https://docs.pixeltable.com/howto/providers/working-with-together Open in Kaggle  Open in Colab  Download Notebook This documentation page is also available as an interactive notebook. You can launch the notebook in Kaggle or Colab, or download it for use with an IDE or local Jupyter installation, by clicking one of the above links. ### Prerequisites * A Together AI account with an API key ([https://api.together.ai/settings/api-keys](https://api.together.ai/settings/api-keys)) ### Important notes * Together.ai usage may incur costs based on your Together.ai plan. * Be mindful of sensitive data and consider security measures when integrating with external services. First you’ll need to install required libraries and enter your Together API key. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} %pip install -qU pixeltable together ``` ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} import getpass import os if 'TOGETHER_API_KEY' not in os.environ: os.environ['TOGETHER_API_KEY'] = getpass.getpass('Together API Key: ') ``` Now let’s create a Pixeltable directory to hold the tables for our demo. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} import pixeltable as pxt # Remove the 'together_demo' directory and its contents, if it exists pxt.drop_dir('together_demo', force=True) pxt.create_dir('together_demo') ```
  Connected to Pixeltable database at: postgresql+psycopg://postgres:@/pixeltable?host=/Users/asiegel/.pixeltable/pgdata
  Created directory 'together\_demo'.
  \
## Chat completions Create a Table: In Pixeltable, create a table with columns to represent your input data and the columns where you want to store the results from OpenAI. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} from pixeltable.functions import together chat_t = pxt.create_table('together_demo/chat', {'input': pxt.String}) messages = [{'role': 'user', 'content': chat_t.input}] chat_t.add_computed_column( output=together.chat_completions( messages=messages, model='Qwen/Qwen3.5-9B', model_kwargs={ # Optional dict with parameters for the Together API 'max_tokens': 300, 'stop': ['\n'], 'temperature': 0.7, 'top_p': 0.9, }, ) ) chat_t.add_computed_column( response=chat_t.output.choices[0].message.content ) ```
  Created table 'chat'.
  Added 0 column values with 0 errors in 0.01 s
  Added 0 column values with 0 errors in 0.01 s
  No rows affected.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Start a conversation chat_t.insert( [ {'input': 'How many species of felids have been classified?'}, {'input': 'Can you make me a coffee?'}, ] ) chat_t.select(chat_t.input, chat_t.response).head() ```
  Inserted 2 rows with 0 errors in 1.58 s (1.27 rows/s)
## Embeddings ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} emb_t = pxt.create_table( 'together_demo/embeddings', {'input': pxt.String} ) emb_t.add_computed_column( embedding=together.embeddings( input=emb_t.input, model='intfloat/multilingual-e5-large-instruct' ) ) ```
  Created table 'embeddings'.
  Added 0 column values with 0 errors in 0.01 s
  No rows affected.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} emb_t.insert( [{'input': 'Together AI provides a variety of embeddings models.'}] ) ```
  Inserted 1 row with 0 errors in 0.54 s (1.86 rows/s)
  1 row inserted.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} emb_t.head() ```
## Image generations ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} image_t = pxt.create_table('together_demo/images', {'input': pxt.String}) image_t.add_computed_column( img=together.image_generations( image_t.input, model='Rundiffusion/Juggernaut-Lightning-Flux', model_kwargs={'steps': 5}, ) ) ```
  Created table 'images'.
  Added 0 column values with 0 errors in 0.01 s
  No rows affected.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} image_t.insert( [{'input': 'A friendly dinosaur playing tennis in a cornfield'}] ) ```
  Inserted 1 row with 0 errors in 1.35 s (0.74 rows/s)
  1 row inserted.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} image_t ```
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} image_t.head() ```
### Learn more To learn more about advanced techniques like RAG operations in Pixeltable, check out the [RAG Operations in Pixeltable](/howto/use-cases/rag-operations) tutorial. # Working with Twelve Labs in Pixeltable Source: https://docs.pixeltable.com/howto/providers/working-with-twelvelabs Open in Kaggle  Open in Colab  Download Notebook This documentation page is also available as an interactive notebook. You can launch the notebook in Kaggle or Colab, or download it for use with an IDE or local Jupyter installation, by clicking one of the above links. Twelve Labs provides multimodal embeddings that project text, images, audio, and video into the **same semantic space**. This enables true **cross-modal search** - the most powerful feature of this integration. **What makes this special?** You can search a video index using *any* modality:
This notebook demonstrates this cross-modal capability with video, then shows how to apply the same embeddings to other modalities. ### Prerequisites * A Twelve Labs account with an API key ([playground.twelvelabs.io](https://playground.twelvelabs.io/)) * Audio and video must be at least 4 seconds long ## Setup ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} %pip install -qU pixeltable twelvelabs ``` ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} import getpass import os if 'TWELVELABS_API_KEY' not in os.environ: os.environ['TWELVELABS_API_KEY'] = getpass.getpass( 'Enter your Twelve Labs API key: ' ) ``` ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} import pixeltable as pxt import pixeltable.functions as pxtf # Create a fresh directory for our demo pxt.drop_dir('twelvelabs_demo', force=True) pxt.create_dir('twelvelabs_demo') ```
  Connected to Pixeltable database at: postgresql+psycopg://postgres:@/pixeltable?host=/Users/pjlb/.pixeltable/pgdata
  Created directory 'twelvelabs\_demo'.
  \
## Cross-Modal Video Search Let’s index a video and search it using text, images, audio, and other videos - all against the same index. ### Create Video Table and Index ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Create a table for videos video_t = pxt.create_table('twelvelabs_demo/videos', {'video': pxt.Video}) # Insert a sample video video_url = 'https://github.com/pixeltable/pixeltable/raw/main/docs/resources/The-Pursuit-of-Happiness.mp4' video_t.insert([{'video': video_url}]) ```
  Created table 'videos'.
  Inserted 1 row with 0 errors in 1.60 s (0.63 rows/s)
  1 row inserted.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Create a view that segments the video into searchable chunks # Twelve Labs requires minimum 4 second segments video_chunks = pxt.create_view( 'twelvelabs_demo/video_chunks', video_t, iterator=pxtf.video.video_splitter( video=video_t.video, duration=5.0, min_segment_duration=4.0 ), ) # Add embedding index for cross-modal search video_chunks.add_embedding_index( 'video_segment', embedding=pxtf.twelvelabs.embed.using(model_name='marengo3.0'), ) ``` Let’s look at the index we just added in the table metadata: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} video_chunks ```
The iterator created a larger table from our single video: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} video_chunks.count() ```
  51
### Text to Video Search Find video segments matching a text description. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} sim = video_chunks.video_segment.similarity(string='pink') video_chunks.order_by(sim, asc=False).limit(3).select( video_chunks.video_segment, score=sim ).collect() ```
### Image to Video Search Find video segments similar to an image. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} image_query = 'https://github.com/pixeltable/pixeltable/raw/main/docs/resources/The-Pursuit-of-Happiness-Screenshot.png' sim = video_chunks.video_segment.similarity(image=image_query) video_chunks.order_by(sim, asc=False).limit(2).select( video_chunks.video_segment, score=sim ).collect() ```
### Video to Video Search Find video segments similar to another video clip. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} video_query = 'https://github.com/pixeltable/pixeltable/raw/main/docs/resources/The-Pursuit-of-Happiness-Video-Extract.mp4' sim = video_chunks.video_segment.similarity(video=video_query) video_chunks.order_by(sim, asc=False).limit(2).select( video_chunks.video_segment, score=sim ).collect() ```
### Audio to Video Search Find video segments with similar audio/speech content. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} audio_query = 'https://github.com/pixeltable/pixeltable/raw/main/docs/resources/The-Pursuit-of-Happiness-Audio-Extract.m4a' sim = video_chunks.video_segment.similarity(audio=audio_query) video_chunks.order_by(sim, asc=False).limit(2).select( video_chunks.video_segment, score=sim ).collect() ```
## Embedding Options For video embeddings, you can focus on specific aspects: * `'visual'` - Focus on what you see * `'audio'` - Focus on what you hear * `'transcription'` - Focus on what is said ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Add a visual-only embedding column video_chunks.add_computed_column( visual_embedding=pxtf.twelvelabs.embed( video_chunks.video_segment, model_name='marengo3.0', embedding_option=['visual'], ) ) video_chunks.select( video_chunks.video_segment, video_chunks.visual_embedding ).limit(2).collect() ```
  Added 51 column values with 0 errors in 19.81 s (2.57 rows/s)
## Other Modalities: Text, Images, and Documents Twelve Labs embeddings also work for text, images, and documents. Here’s a compact example showing **multiple embedding indexes on a single table**. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Create a multimodal content table content_t = pxt.create_table( 'twelvelabs_demo/content', { 'title': pxt.String, 'description': pxt.String, 'thumbnail': pxt.Image, }, ) # Add computed column combining title and description content_t.add_computed_column( text_content=content_t.title + '. ' + content_t.description ) # Add embedding index on combined text column content_t.add_embedding_index( 'text_content', embedding=pxtf.twelvelabs.embed.using(model_name='marengo3.0'), ) # Add embedding index on image column content_t.add_embedding_index( 'thumbnail', embedding=pxtf.twelvelabs.embed.using(model_name='marengo3.0'), ) ```
  Created table 'content'.
  Added 0 column values with 0 errors in 0.01 s
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Insert sample content content_t.insert( [ { 'title': 'Beach Sunset', 'description': 'A beautiful sunset over the ocean with palm trees.', 'thumbnail': 'https://raw.githubusercontent.com/pixeltable/pixeltable/main/docs/resources/images/000000000025.jpg', }, { 'title': 'Mountain Hiking', 'description': 'Hikers climbing a steep mountain trail with scenic views.', 'thumbnail': 'https://raw.githubusercontent.com/pixeltable/pixeltable/main/docs/resources/images/000000000139.jpg', }, { 'title': 'City Street', 'description': 'Busy urban street with cars and pedestrians.', 'thumbnail': 'https://raw.githubusercontent.com/pixeltable/pixeltable/main/docs/resources/images/000000000042.jpg', }, { 'title': 'Wildlife Safari', 'description': 'Elephants and zebras on the African savanna.', 'thumbnail': 'https://raw.githubusercontent.com/pixeltable/pixeltable/main/docs/resources/images/000000000061.jpg', }, ] ) ```
  Inserted 4 rows with 0 errors in 1.97 s (2.03 rows/s)
  4 rows inserted.
We can see the two indexes we added in the schema: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} content_t ```
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Search by text description sim = content_t.text_content.similarity(string='outdoor nature adventure') content_t.order_by(sim, asc=False).limit(2).select( content_t.title, content_t.text_content, score=sim ).collect() ```
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Search by image similarity query_image = 'https://raw.githubusercontent.com/pixeltable/pixeltable/main/docs/resources/images/000000000001.jpg' sim = content_t.thumbnail.similarity(image=query_image) content_t.order_by(sim, asc=False).limit(2).select( content_t.title, content_t.thumbnail, score=sim ).collect() ```
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Cross-modal: Search images using text! sim = content_t.thumbnail.similarity(string='shoe rack') content_t.order_by(sim, asc=False).limit(2).select( content_t.title, content_t.thumbnail, score=sim ).collect() ```
## Summary **Twelve Labs + Pixeltable enables:** * **Cross-modal search**: Query video with text, images, audio, or other videos * **Multiple indexes per table**: Add embedding indexes on different columns * **Embedding options**: Focus on visual, audio, or transcription aspects * **All modalities**: Text, images, audio, video, and documents ### Learn More * [Twelve Labs Documentation](https://docs.twelvelabs.io/) * [Pixeltable Documentation](/) # Working with vLLM Source: https://docs.pixeltable.com/howto/providers/working-with-vllm Open in Kaggle  Open in Colab  Download Notebook This documentation page is also available as an interactive notebook. You can launch the notebook in Kaggle or Colab, or download it for use with an IDE or local Jupyter installation, by clicking one of the above links. This tutorial demonstrates how to use Pixeltable’s built-in `vLLM` integration to run local LLMs with high-throughput inference. ### Important notes * vLLM provides high-throughput inference with techniques like PagedAttention and continuous batching * Models are loaded from HuggingFace and cached in memory for reuse * vLLM currently requires a Linux environment with GPU support for best performance * Consider GPU memory when choosing model sizes ## Set up environment First, let’s install Pixeltable with vLLM support: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} %pip install -qU pixeltable vllm ``` ## Create a table for chat completions Now let’s create a table that will contain our inputs and responses. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} import pixeltable as pxt from pixeltable.functions import vllm pxt.drop_dir('vllm_demo', force=True) pxt.create_dir('vllm_demo') t = pxt.create_table('vllm_demo/chat', {'input': pxt.String}) ```
  Created directory 'vllm\_demo'.
  Created table 'chat'.
Next, we add a computed column that calls the Pixeltable `chat_completions` UDF, which uses vLLM’s high-throughput inference engine under the hood. We specify a HuggingFace model identifier, and vLLM will download and cache the model automatically. (If this is your first time using Pixeltable, the Pixeltable Fundamentals tutorial contains more details about table creation, computed columns, and UDFs.) For this demo we’ll use `Qwen2.5-0.5B-Instruct`, a very small (0.5-billion parameter) model that still produces decent results. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Add a computed column that uses vLLM for chat completion # against the input. messages = [ {'role': 'system', 'content': 'You are a helpful assistant.'}, {'role': 'user', 'content': t.input}, ] t.add_computed_column( result=vllm.chat_completions( messages, model='Qwen/Qwen2.5-0.5B-Instruct' ) ) # Extract the output content from the native vLLM response. t.add_computed_column(output=t.result.outputs[0].text) ```
  Added 0 column values with 0 errors in 0.01 s
  Added 0 column values with 0 errors in 0.00 s
  No rows affected.
## Test chat completion Let’s try a few queries: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Test with a few questions t.insert( [ {'input': 'What is the capital of France?'}, {'input': 'What are some edible species of fish?'}, {'input': 'Who are the most prominent classical composers?'}, ] ) ```
  Inserted 3 rows with 0 errors in 1.74 s (1.72 rows/s)
  3 rows inserted.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} t.select(t.input, t.output).collect() ```
## Comparing models vLLM makes it easy to compare the output of different models. Let’s try comparing the output from `Qwen2.5-0.5B` against a somewhat larger model, `Qwen2.5-1.5B-Instruct`. As always, when we add a new computed column to our table, it’s automatically evaluated against the existing table rows. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} t.add_computed_column( result_qwen15=vllm.chat_completions( messages, model='Qwen/Qwen2.5-1.5B-Instruct' ) ) t.add_computed_column(output_qwen15=t.result_qwen15.outputs[0].text) t.select(t.input, t.output, t.output_qwen15).collect() ```
  Added 3 column values with 0 errors in 3.45 s (0.87 rows/s)
  Added 3 column values with 0 errors in 0.01 s (225.06 rows/s)
## Using sampling parameters vLLM supports fine-grained control over generation through `sampling_params`. Parameters like `max_tokens`, `temperature`, `top_p`, and `top_k` control the decoding behavior. Engine-level settings (such as `max_model_len`) can be passed separately via `engine_args`. Let’s try running with a different system prompt and custom sampling settings. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} messages_teacher = [ { 'role': 'system', 'content': 'You are a patient school teacher. Explain concepts simply and clearly.', }, {'role': 'user', 'content': t.input}, ] t.add_computed_column( result_teacher=vllm.chat_completions( messages_teacher, model='Qwen/Qwen2.5-0.5B-Instruct', sampling_params={ 'max_tokens': 256, 'temperature': 0.7, 'top_p': 0.9, }, ) ) t.add_computed_column(output_teacher=t.result_teacher.outputs[0].text) t.select(t.input, t.output_teacher).collect() ```
  Added 3 column values with 0 errors in 14.16 s (0.21 rows/s)
  Added 3 column values with 0 errors in 0.01 s (271.08 rows/s)
## Text generation In addition to chat completions, vLLM also supports direct text generation with the `generate` UDF. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} gen_t = pxt.create_table('vllm_demo/generation', {'prompt': pxt.String}) gen_t.add_computed_column( result=vllm.generate( gen_t.prompt, model='Qwen/Qwen2.5-0.5B-Instruct', sampling_params={'max_tokens': 100}, ) ) gen_t.add_computed_column(output=gen_t.result.outputs[0].text) gen_t.insert( [ {'prompt': 'The capital of France is'}, {'prompt': 'Once upon a time, there was a'}, ] ) gen_t.select(gen_t.prompt, gen_t.output).collect() ```
  Created table 'generation'.
  Added 0 column values with 0 errors in 0.00 s
  Added 0 column values with 0 errors in 0.00 s
  Inserted 2 rows with 0 errors in 5.88 s (0.34 rows/s)
## Additional Resources * [Pixeltable Documentation](/) * [vLLM Documentation](https://docs.vllm.ai/) * [vLLM GitHub](https://github.com/vllm-project/vllm) # Working with Voyage AI in Pixeltable Source: https://docs.pixeltable.com/howto/providers/working-with-voyageai Open in Kaggle  Open in Colab  Download Notebook This documentation page is also available as an interactive notebook. You can launch the notebook in Kaggle or Colab, or download it for use with an IDE or local Jupyter installation, by clicking one of the above links. Pixeltable’s Voyage AI integration enables you to access state-of-the-art embedding and reranker models via the Voyage AI API. ### Prerequisites * A Voyage AI account with an API key ([https://www.voyageai.com/](https://www.voyageai.com/)) ### Important notes * Voyage AI usage may incur costs based on your Voyage AI plan. * Be mindful of sensitive data and consider security measures when integrating with external services. First you’ll need to install required libraries and enter your Voyage AI API key. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} %pip install -qU voyageai ``` ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} import getpass import os if 'VOYAGE_API_KEY' not in os.environ: os.environ['VOYAGE_API_KEY'] = getpass.getpass( 'Enter your Voyage AI API key:' ) ``` Now let’s create a Pixeltable directory to hold the tables for our demo. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} import pixeltable as pxt # Remove the 'voyageai_demo' directory and its contents, if it exists pxt.drop_dir('voyageai_demo', force=True) pxt.create_dir('voyageai_demo') ```
  Created directory 'voyageai\_demo'.
  \
## Text embeddings Voyage AI provides state-of-the-art embedding models for semantic search and RAG applications. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} from pixeltable.functions import voyageai # Create a table for document embeddings docs_t = pxt.create_table('voyageai_demo/documents', {'text': pxt.String}) # Add computed column with Voyage embeddings docs_t.add_computed_column( embedding=voyageai.embeddings( docs_t.text, model='voyage-3.5', input_type='document' ) ) ```
  Created table 'documents'.
  Added 0 column values with 0 errors.
  No rows affected.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Insert some sample documents documents = [ 'The Mediterranean diet emphasizes fish, olive oil, and vegetables, believed to reduce chronic diseases.', 'Photosynthesis in plants converts light energy into glucose and produces essential oxygen.', '20th-century innovations, from radios to smartphones, centered on electronic advancements.', 'Rivers provide water, irrigation, and habitat for aquatic species, vital for ecosystems.', "Apple's conference call to discuss fourth fiscal quarter results is scheduled for Thursday, November 2, 2023.", "Shakespeare's works, like 'Hamlet' and 'A Midsummer Night's Dream,' endure in literature.", ] docs_t.insert({'text': doc} for doc in documents) ```
  Inserting rows into \`documents\`: 6 rows \[00:00, 2561.67 rows/s]
  Inserted 6 rows with 0 errors.
  6 rows inserted, 12 values computed.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # View the embeddings docs_t.select(docs_t.text, docs_t.embedding).head(3) ```
## Embedding index for similarity search You can use Voyage AI embeddings with Pixeltable’s embedding index for efficient similarity search. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Create a table with an embedding index search_t = pxt.create_table('voyageai_demo/search', {'text': pxt.String}) # Add embedding index for similarity search embed_fn = voyageai.embeddings.using( model='voyage-3.5', input_type='document' ) search_t.add_embedding_index('text', string_embed=embed_fn) ```
  Created table 'search'.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Insert documents search_t.insert({'text': doc} for doc in documents) ```
  Inserting rows into \`search\`: 6 rows \[00:00, 973.68 rows/s]
  Inserted 6 rows with 0 errors.
  6 rows inserted, 12 values computed.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Perform similarity search sim = search_t.text.similarity( string='What are the health benefits of Mediterranean food?' ) search_t.order_by(sim, asc=False).limit(3).select( search_t.text, score=sim ).collect() ```
## Reranking Voyage AI’s rerankers can refine search results by providing more accurate relevance scores. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Create a table for reranking rerank_t = pxt.create_table( 'voyageai_demo/rerank', {'query': pxt.String, 'documents': pxt.Json} ) # Add computed column with reranking results rerank_t.add_computed_column( reranked=voyageai.rerank( rerank_t.query, rerank_t.documents, model='rerank-2.5', top_k=3 ) ) ```
  Created table 'rerank'.
  Added 0 column values with 0 errors.
  No rows affected.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Insert query and documents to rerank rerank_t.insert( [ { 'query': "When is Apple's conference call scheduled?", 'documents': documents, } ] ) ```
  Inserting rows into \`rerank\`: 1 rows \[00:00, 343.65 rows/s]
  Inserted 1 row with 0 errors.
  1 row inserted, 2 values computed.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Add computed column to extract top results using JSON path rerank_t.add_computed_column(top_results=rerank_t.reranked['results']) ```
  Added 1 column value with 0 errors.
  1 row updated, 1 value computed.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Extract the top result's document and score rerank_t.select( rerank_t.query, top_document=rerank_t.top_results[0]['document'], top_score=rerank_t.top_results[0]['relevance_score'], ).collect() ```
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # View reranking results rerank_t.select(rerank_t.query, rerank_t.top_results).collect() ```
## Multimodal Embeddings Voyage AI’s multimodal model (`voyage-multimodal-3`) can embed both images and text into the same vector space, enabling cross-modal similarity search. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Create a table for multimodal embeddings mm_t = pxt.create_table( 'voyageai_demo/multimodal', {'image': pxt.Image, 'caption': pxt.String}, if_exists='replace', ) # Add computed columns for image and text embeddings # multimodal_embed can embed either images or text independently mm_t.add_computed_column( image_embedding=voyageai.multimodal_embed( mm_t.image, model='voyage-multimodal-3.5', input_type='document' ) ) mm_t.add_computed_column( text_embedding=voyageai.multimodal_embed( mm_t.caption, model='voyage-multimodal-3.5', input_type='document' ) ) ```
  Created table 'multimodal'.
  Added 0 column values with 0 errors.
  Added 0 column values with 0 errors.
  No rows affected.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Insert a sample image with caption mm_t.insert( [ { 'image': 'https://raw.githubusercontent.com/pixeltable/pixeltable/main/docs/resources/images/000000000139.jpg', 'caption': 'A person standing next to an elephant', } ] ) ```
  Inserting rows into \`multimodal\`: 1 rows \[00:00, 520.00 rows/s]
  Inserted 1 row with 0 errors.
  1 row inserted, 5 values computed.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # View the multimodal embeddings mm_t.select( mm_t.image, mm_t.caption, mm_t.image_embedding, mm_t.text_embedding ).head() ```
### Learn more To learn more about RAG operations in Pixeltable, check out the [RAG Operations in Pixeltable](/howto/use-cases/rag-operations) tutorial. For more information about Voyage AI models and features, visit: * [Voyage AI Documentation](https://docs.voyageai.com/) * [Text Embeddings](https://docs.voyageai.com/docs/embeddings) * [Multimodal Embeddings](https://docs.voyageai.com/docs/multimodal-embeddings) * [Rerankers](https://docs.voyageai.com/docs/reranker) # Transcribing and Indexing Audio and Video in Pixeltable Source: https://docs.pixeltable.com/howto/use-cases/audio-transcriptions Open in Kaggle  Open in Colab  Download Notebook This documentation page is also available as an interactive notebook. You can launch the notebook in Kaggle or Colab, or download it for use with an IDE or local Jupyter installation, by clicking one of the above links. In this tutorial, we’ll build an end-to-end workflow for creating and indexing audio transcriptions of video data. We’ll demonstrate how Pixeltable can be used to: 1. Extract audio data from video files; 2. Transcribe the audio using OpenAI Whisper; 3. Build a semantic index of the transcriptions, using the Huggingface sentence\_transformers models; 4. Search this index. The tutorial assumes you’re already somewhat familiar with Pixeltable. If this is your first time using Pixeltable, the [10-Minute Tour](/overview/ten-minute-tour) tutorial is a great place to start. ## Create a Table for Video Data Let’s first install the Python packages we’ll need for the demo. We’re going to use the popular Whisper library, running locally. Later in the demo, we’ll see how to use the OpenAI API endpoints as an alternative. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} %pip install -q pixeltable openai openai-whisper sentence-transformers spacy !python -m spacy download en_core_web_sm -q ``` Now we create a Pixeltable table to hold our videos. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} import pixeltable as pxt pxt.drop_dir( 'transcription_demo', force=True ) # Ensure a clean slate for the demo pxt.create_dir('transcription_demo') # Create a table to store our videos and workflow video_table = pxt.create_table( 'transcription_demo/video_table', {'video': pxt.Video} ) video_table ```
  Connected to Pixeltable database at: postgresql+psycopg://postgres:@/pixeltable?host=/Users/asiegel/.pixeltable/pgdata
  Created directory 'transcription\_demo'.
  Created table 'video\_table'.
Next let’s insert some video files into the table. In this demo, we’ll be using one-minute excerpts from a Lex Fridman podcast. We’ll begin by inserting two of them into our new table. In this demo, our videos are given as `https` links, but Pixeltable also accepts local files and S3 URLs as input. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} videos = [ 'https://github.com/pixeltable/pixeltable/raw/release/docs/resources/audio-transcription-demo/' f'Lex-Fridman-Podcast-430-Excerpt-{n}.mp4' for n in range(3) ] video_table.insert({'video': video} for video in videos[:2]) video_table.show() ```
  Inserted 2 rows with 0 errors in 2.04 s (0.98 rows/s)
Now we’ll add another column to hold extracted audio from our videos. The new column is an example of a *computed column*: it’s updated automatically based on the contents of another column (or columns). In this case, the value of the `audio` column is defined to be the audio track extracted from whatever’s in the `video` column. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} from pixeltable.functions.video import extract_audio video_table.add_computed_column( audio=extract_audio(video_table.video, format='mp3') ) video_table.show() ```
  Added 2 column values with 0 errors in 0.91 s (2.19 rows/s)
If we look at the structure of the video table, we see that the new column is a computed column. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} video_table ```
We can also add another computed column to extract metadata from the audio streams. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} from pixeltable.functions.audio import get_metadata video_table.add_computed_column(metadata=get_metadata(video_table.audio)) video_table.show() ```
  Added 2 column values with 0 errors in 0.02 s (95.47 rows/s)
## Create Transcriptions Now we’ll add a step to create transcriptions of our videos. As mentioned above, we’re going to use the Whisper library for this, running locally. Pixeltable has a built-in function, `whisper.transcribe`, that serves as an adapter for the Whisper library’s transcription capability. All we have to do is add a computed column that calls this function: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} from pixeltable.functions import whisper video_table.add_computed_column( transcription=whisper.transcribe( audio=video_table.audio, model='base.en' ) ) video_table.select( video_table.video, video_table.transcription.text ).show() ```
  Added 2 column values with 0 errors in 4.63 s (0.43 rows/s)
In order to index the transcriptions, we’ll first need to split them into sentences. We can do this using Pixeltable’s built-in `string_splitter` iterator. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} from pixeltable.functions.string import string_splitter sentences_view = pxt.create_view( 'transcription_demo/sentences_view', video_table, iterator=string_splitter( video_table.transcription.text, separators='sentence' ), ) ``` The `string_splitter` creates a new view, with the audio transcriptions broken into individual, one-sentence chunks. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} sentences_view.select(sentences_view.pos, sentences_view.text).show(8) ```
## Add an Embedding Index Next, let’s use the Huggingface `sentence_transformers` library to create an embedding index of our sentences, attaching it to the `text` column of our `sentences_view`. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} from pixeltable.functions.huggingface import sentence_transformer sentences_view.add_embedding_index( 'text', embedding=sentence_transformer.using(model_id='intfloat/e5-large-v2'), ) ```
  modules.json:   0%|          | 0.00/387 \[00:00\

We can do a simple lookup to test our new index. The following snippet
returns the results of a nearest-neighbor search on the input “What is
happiness?”

```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}}
sim = sentences_view.text.similarity(string='What is happiness?')

(
    sentences_view.order_by(sim, asc=False)
    .limit(10)
    .select(sentences_view.text, similarity=sim)
    .collect()
)
```

## Incremental Updates *Incremental updates* are a key feature of Pixeltable. Whenever a new video is added to the original table, all of its downstream computed columns are updated automatically. Let’s demonstrate this by adding a third video to the table and seeing how the updates propagate through to the index. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} video_table.insert([{'video': videos[2]}]) ```
  Inserted 10 rows with 0 errors in 4.20 s (2.38 rows/s)
  10 rows inserted.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} video_table.select( video_table.video, video_table.metadata, video_table.transcription.text, ).show() ```
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} sim = sentences_view.text.similarity(string='What is happiness?') ( sentences_view.order_by(sim, asc=False) .limit(20) .select(sentences_view.text, similarity=sim) .collect() ) ```
We can see the new results showing up in `sentences_view`. ## Using the OpenAI API This concludes our tutorial using the locally installed Whisper library. Sometimes, it may be preferable to use the OpenAI API rather than a locally installed library. In this section we’ll show how this can be done in Pixeltable, simply by using a different function to construct our computed columns. Since this section relies on calling out to the OpenAI API, you’ll need to have an API key, which you can enter below. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} import getpass import os if 'OPENAI_API_KEY' not in os.environ: os.environ['OPENAI_API_KEY'] = getpass.getpass('OpenAI API Key:') ``` ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} from pixeltable.functions import openai video_table.add_computed_column( transcription_from_api=openai.transcriptions( video_table.audio, model='whisper-1' ) ) ```
  Added 3 column values with 0 errors in 6.49 s (0.46 rows/s)
  3 rows updated.
Now let’s compare the results from the local model and the API side-by-side. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} video_table.select( video_table.video, video_table.transcription.text, video_table.transcription_from_api.text, ).show() ```
They look pretty similar, which isn’t surprising, since the OpenAI transcriptions endpoint runs on Whisper. One difference is that the local library spits out a lot more information about the internal behavior of the model. Note that we’ve been selecting `video_table.transcription.text` in the preceding queries, which pulls out just the `text` field of the transcription results. The actual results are a sizable JSON structure that includes a lot of metadata. To see the full output, we can select `video_table.transcription` instead, to get the full JSON struct. Here’s what it looks like (we’ll select just one row, since it’s a lot of output): ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} video_table.select( video_table.transcription, video_table.transcription_from_api ).show(1) ```
# Object Detection in Videos Source: https://docs.pixeltable.com/howto/use-cases/object-detection-in-videos Open in Kaggle  Open in Colab  Download Notebook This documentation page is also available as an interactive notebook. You can launch the notebook in Kaggle or Colab, or download it for use with an IDE or local Jupyter installation, by clicking one of the above links. In this tutorial, we’ll demonstrate how to use Pixeltable to do frame-by-frame object detection, made simple through Pixeltable’s video-related functionality: * automatic frame extraction * running complex functions against frames (in this case, the YOLOX object detection models) * reassembling frames back into videos We’ll be working with a single video file from Pixeltable’s test data repository. This tutorial assumes you’re at least somewhat familiar with Pixeltable; a good place to learn more is the [Pixeltable Documentation](/overview/pixeltable). ## Creating a tutorial directory and table First, let’s make sure the packages we need for this tutorial are installed: Pixeltable itself, PyTorch, and the YOLOX object detection library. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} %pip install -qU pixeltable pixeltable-yolox ``` All data in Pixeltable is stored in tables, which in turn reside in directories. We’ll begin by creating a `detection_demo` directory and a table to hold our videos, with a single column of type `pxt.Video`. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} import pixeltable as pxt pxt.create_dir('detection_demo', if_exists='replace_force') videos_table = pxt.create_table( 'detection_demo/videos', {'video': pxt.Video} ) ```
  Connected to Pixeltable database at: postgresql+psycopg://postgres:@/pixeltable?host=/Users/asiegel/.pixeltable/pgdata
  Created directory 'detection\_demo'.
  Created table 'videos'.
In order to interact with the frames, we take advantage of Pixeltable’s component view concept: we create a “view” of our video table that contains one row for each frame of each video in the table. Pixeltable provides the built-in `frame_iterator` for this. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} from pixeltable.functions.video import frame_iterator frames_view = pxt.create_view( 'detection_demo/frames', videos_table, iterator=frame_iterator(videos_table.video), ) ``` You’ll see that neither the `videos` table nor the `frames` view has any actual data yet, because we haven’t yet added any videos to the table. However, the `frames` view is now configured to automatically track the `videos` table as new data shows up. The new view is automatically configured with six columns: * `pos` - a system column that is part of every component view * `video` - the column inherited from our base table (all base table columns are visible in any of its views) * `frame_idx`, `pos_msec`, `pos_frame`, `frame` - these four columns are created by the `frame_iterator`. Let’s have a look at the new view: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} frames_view ```
We’ll now insert a single row into the videos table, containing a video of a busy intersection in Bangkok. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} videos_table.insert( [ { 'video': 'https://raw.githubusercontent.com/pixeltable/pixeltable/release/docs/resources/bangkok.mp4' } ] ) ```
  Inserted 462 rows with 0 errors in 4.35 s (106.25 rows/s)
  462 rows inserted.
Notice that both the `videos` table and `frames` view were automatically updated, expanding the single video into 461 rows in the view. Let’s have a look at `videos` first. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} videos_table.show() ```
Now let’s peek at the first five rows of `frames`: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} frames_view.select( frames_view.pos, frames_view.frame, frames_view.frame.width, frames_view.frame.height, ).show(5) ```
One advantage of using Pixeltable’s component view mechanism is that Pixeltable does not physically store the frames. Instead, Pixeltable re-extracts the frames on retrieval using the frame index, which can be done very efficiently and avoids any storage overhead (which can be quite substantial for video frames). ## Object Detection with Pixeltable Now let’s apply an object detection model to our frames. Pixeltable includes built-in support for a number of models; we’re going to use the YOLOX family of models, which are lightweight models with solid performance. We first import the `yolox` Pixeltable function. Pixeltable functions operate on columns and expressions using standard Python function call syntax. Here’s an example that shows how we might experiment with applying one of the YOLOX models to the first few frames in our video, using Pixeltable’s powerful `select` comprehension. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} from pixeltable.functions.yolox import yolox # Show the results of applying the `yolox_tiny` model # to the first few frames in the table. frames_view.select( frames_view.frame, yolox(frames_view.frame, model_id='yolox_tiny') ).head(3) ```
It may appear that we just ran the YOLOX inference over the entire view of 461 frames, but remember that Pixeltable evaluates expressions lazily: in this case, it only ran inference over the 3 frames that we actually displayed. The inference output looks like what we’d expect, so let’s add a *computed column* that runs inference over the entire view (computed columns are discussed in detail in the [Computed Columns](/tutorials/computed-columns) tutorial). Remember that once a computed column is created, Pixeltable will update it incrementally any time new rows are added to the view. This is a convenient way to incorporate inference (and other operations) into data workflows. This *will* cause Pixeltable to run inference over all 461 frames, so please be patient. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Create a computed column to compute detections using the `yolox_tiny` # model. # We'll adjust the confidence threshold down a bit (the default is 0.5) # to pick up even more bounding boxes. frames_view.add_computed_column( detections_tiny=yolox( frames_view.frame, model_id='yolox_tiny', threshold=0.25 ) ) ```
  Added 461 column values with 0 errors in 15.09 s (30.55 rows/s)
  461 rows updated.
The new column is now part of the schema of the `frames` view: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} frames_view ```
The data in the computed column is now stored for fast retrieval. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} frames_view.select(frames_view.frame, frames_view.detections_tiny).show(3) ```
Now let’s create a new set of images, in which we superimpose the detected bounding boxes on top of the original images. We’ll use the handy built-in `bboxes_draw` UDF for this. We could create a new computed column to hold the superimposed images, but we don’t have to; sometimes it’s easier just to use a `select` comprehension, as we did when we were first experimenting with the detection model. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} import pixeltable.functions as pxtf frames_view.select( frames_view.frame, pxtf.vision.bboxes_draw( frames_view.frame, frames_view.detections_tiny.bboxes, width=4 ), ).show(1) ```
Our `select` comprehension ranged over the entire table, but just as before, Pixeltable computes the output lazily: image operations are performed at retrieval time, so in this case, Pixeltable drew the annotations just for the one frame that we actually displayed. Looking at individual frames gives us some idea of how well our detection algorithm works, but it would be more instructive to turn the visualization output back into a video. We do that with the built-in function `make_video()`, which is an aggregation function that takes a frame index (actually: any expression that can be used to order the frames; a timestamp would also work) and an image, and then assembles the sequence of images into a video. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} frames_view.group_by(videos_table).select( pxt.functions.video.make_video( frames_view.pos, pxtf.vision.bboxes_draw( frames_view.frame, frames_view.detections_tiny.bboxes, width=4 ), ) ).show(1) ```
## Comparing Object Detection Models The detections that we get out of `yolox_tiny` are passable, but a little choppy. Suppose we want to experiment with a more powerful object detection model, to see if there is any improvement in detection quality. We can create an additional column to hold the new inferences. The larger model takes longer to download and run, so please be patient. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Here we use the larger `yolox_m` (medium) model. frames_view.add_computed_column( detections_m=yolox( frames_view.frame, model_id='yolox_m', threshold=0.25 ) ) ```
  Added 461 column values with 0 errors in 65.94 s (6.99 rows/s)
  461 rows updated.
Let’s see the results of the two models side-by-side. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} frames_view.group_by(videos_table).select( pxt.functions.video.make_video( frames_view.pos, pxtf.vision.bboxes_draw( frames_view.frame, frames_view.detections_tiny.bboxes, width=4 ), ), pxt.functions.video.make_video( frames_view.pos, pxtf.vision.bboxes_draw( frames_view.frame, frames_view.detections_m.bboxes, width=4 ), ), ).show(1) ```
Running the videos side-by-side, we can see that the larger model is higher in quality: less flickering, with more stable boxes from frame to frame. ## Evaluating Models Against a Ground Truth In order to do a quantitative evaluation of model performance, we need a ground truth to compare them against. Let’s generate some (synthetic) “ground truth” data by running against the largest YOLOX model available. It will take even longer to cache and evaluate this model. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} frames_view.add_computed_column( detections_x=yolox( frames_view.frame, model_id='yolox_x', threshold=0.25 ) ) ```
  Added 461 column values with 0 errors in 156.55 s (2.94 rows/s)
  461 rows updated.
Let’s have a look at our enlarged view, now with three `detections` columns. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} frames_view ```
We’re going to be evaluating the generated detections with the commonly-used [mean average precision](https://learnopencv.com/mean-average-precision-map-object-detection-model-evaluation-metric/) metric (mAP). The mAP metric is based on per-frame metrics, such as true and false positives per detected class, which are then aggregated into a single (per-class) number. In Pixeltable, functionality is available via the `eval_detections()` and `mean_ap()` built-in functions. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} from pixeltable.functions.vision import eval_detections, mean_ap frames_view.add_computed_column( eval_yolox_tiny=eval_detections( pred_bboxes=frames_view.detections_tiny.bboxes, pred_labels=frames_view.detections_tiny.labels, pred_scores=frames_view.detections_tiny.scores, gt_bboxes=frames_view.detections_x.bboxes, gt_labels=frames_view.detections_x.labels, ) ) frames_view.add_computed_column( eval_yolox_m=eval_detections( pred_bboxes=frames_view.detections_m.bboxes, pred_labels=frames_view.detections_m.labels, pred_scores=frames_view.detections_m.scores, gt_bboxes=frames_view.detections_x.bboxes, gt_labels=frames_view.detections_x.labels, ) ) ```
  Added 461 column values with 0 errors in 0.29 s (1589.38 rows/s)
  Added 461 column values with 0 errors in 0.31 s (1475.98 rows/s)
  461 rows updated.
Let’s take a look at the output. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} frames_view.select( frames_view.eval_yolox_tiny, frames_view.eval_yolox_m ).show(1) ```
The computation of the mAP metric is now simply a query over the evaluation output, aggregated with the `mean_ap()` function. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} frames_view.select( mean_ap(frames_view.eval_yolox_tiny), mean_ap(frames_view.eval_yolox_m), ).show() ```
This two-step process allows you to compute mAP at every granularity: over your entire dataset, only for specific videos, only for videos that pass a certain filter, etc. Moreover, you can compute this metric any time, not just during training, and use it to guide your understanding of your dataset and how it affects the quality of your models. # Document Indexing and RAG Source: https://docs.pixeltable.com/howto/use-cases/rag-demo Open in Kaggle  Open in Colab  Download Notebook This documentation page is also available as an interactive notebook. You can launch the notebook in Kaggle or Colab, or download it for use with an IDE or local Jupyter installation, by clicking one of the above links. In this tutorial, we’ll demonstrate how RAG operations can be implemented in Pixeltable. In particular, we’ll develop a RAG application that summarizes a collection of PDF documents and uses ChatGPT to answer questions about them. In a traditional RAG workflow, such operations might be implemented as a Python script that runs on a periodic schedule or in response to certain events. In Pixeltable, they are implemented as persistent tables that are updated automatically and incrementally as new data becomes available. We first set up our OpenAI API key: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} import getpass import os if 'OPENAI_API_KEY' not in os.environ: os.environ['OPENAI_API_KEY'] = getpass.getpass('OpenAI API Key:') ``` We then install the packages we need for this tutorial and then set up our environment. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} %pip install -q pixeltable sentence-transformers tiktoken openai openpyxl ```
  Note: you may need to restart the kernel to use updated packages.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} import pixeltable as pxt # Ensure a clean slate for the demo pxt.drop_dir('rag_demo', force=True) pxt.create_dir('rag_demo') ```
  Connected to Pixeltable database at: postgresql+psycopg://postgres:@/pixeltable?host=/Users/sergeymkhitaryan/.pixeltable/pgdata
  Created directory 'rag\_demo'.
  \
Next we’ll create a table containing the sample questions we want to answer. The questions are stored in an Excel spreadsheet, along with a set of “ground truth” answers to help evaluate our model pipeline. We can use `create_table()` with the `source` parameter to load them. Note that we can pass the URL of the spreadsheet directly. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} base = 'https://github.com/pixeltable/pixeltable/raw/main/docs/resources/rag-demo/' qa_url = base + 'Q-A-Rag.xlsx' queries_t = pxt.create_table('rag_demo/queries', source=qa_url) ```
  Created table 'queries'.
  Inserting rows into \`queries\`: 8 rows \[00:00, 2469.96 rows/s]
  Inserted 8 rows with 0 errors.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} queries_t.head() ```
## Outline There are two major parts to our RAG application: 1. Document Indexing: Load the documents, split them into chunks, and index them using a vector embedding. 2. Querying: For each question on our list, do a top-k lookup for the most relevant chunks, use them to construct a ChatGPT prompt, and send the enriched prompt to an LLM. We’ll implement both parts in Pixeltable. ## Document Indexing All data in Pixeltable, including documents, resides in tables. Tables are persistent containers that can serve as the store of record for your data. Since we are starting from scratch, we will start with an empty table `rag_demo.documents` with a single column, `document`. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} documents_t = pxt.create_table( 'rag_demo/documents', {'document': pxt.Document} ) documents_t ```
  Created table 'documents'.
Next, we’ll insert our first few source documents into the new table. We’ll leave the rest for later, in order to show how to update the indexed document base incrementally. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} document_urls = [ base + 'Argus-Market-Digest-June-2024.pdf', base + 'Argus-Market-Watch-June-2024.pdf', base + 'Company-Research-Alphabet.pdf', base + 'Jefferson-Amazon.pdf', base + 'Mclean-Equity-Alphabet.pdf', base + 'Zacks-Nvidia-Report.pdf', ] ``` ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} documents_t.insert({'document': url} for url in document_urls[:3]) documents_t.show() ```
  Inserting rows into \`documents\`: 3 rows \[00:00, 491.31 rows/s]
  Inserted 3 rows with 0 errors.
In RAG applications, we often decompose documents into smaller units, or chunks, rather than treating each document as a single entity. In this example, we’ll use Pixeltable’s built-in `document_splitter`, but in general the chunking methodology is highly customizable. `document_splitter` has a variety of options for controlling the chunking behavior, and it’s also possible to replace it entirely with a user-defined iterator (or an adapter for a third-party document splitter). In Pixeltable, operations such as chunking can be automated by creating **views** of the base `documents` table. A view is a virtual derived table: rather than adding data directly to the view, we define it via a computation over the base table. In this example, the view is defined by iteration over the chunks of a `document_splitter`. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} from pixeltable.functions.document import document_splitter chunks_t = pxt.create_view( 'rag_demo/chunks', documents_t, iterator=document_splitter( documents_t.document, separators='token_limit', limit=300 ), ) ```
  Inserting rows into \`chunks\`: 41 rows \[00:00, 20799.04 rows/s]
Our `chunks` view now has 3 columns: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} chunks_t ```
* `text` is the chunk text produced by the `document_splitter` * `pos` is a system-generated integer column, starting at 0, that provides a sequence number for each row * `document`, which is simply the `document` column from the base table `documents`. We won’t need it here, but having access to the base table’s columns (in effect a parent-child join) can be quite useful. Notice that as soon as we created it, `chunks` was automatically populated with data from the existing documents in our base table. We can select the first 2 chunks from each document using common query operations, in order to get a feel for what was extracted: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} chunks_t.where(chunks_t.pos < 2).show() ```
Now let’s compute vector embeddings for the document chunks and store them in a vector index. Pixeltable has built-in support for vector indexing using a variety of embedding model families, and it’s easy for users to add new ones via UDFs. In this demo, we’re going to use the E5 model from the Huggingface `sentence_transformers` library, which runs locally. The following command creates a vector index on the `text` column in the `chunks` table, using the E5 embedding model. (For details on index creation, see the [Embedding and Vector Indices](/platform/embedding-indexes) guide.) Note that defining the index is sufficient in order to load it with the existing data (and also to update it when the underlying data changes, as we’ll see later). ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} from pixeltable.functions.huggingface import sentence_transformer chunks_t.add_embedding_index( 'text', embedding=sentence_transformer.using(model_id='intfloat/e5-large-v2'), ) ``` This completes the first part of our application, creating an indexed document base. Next, we’ll use it to run some queries. ## Querying In order to express a top-k lookup against our index, we use Pixeltable’s `similarity` operator in combination with the standard `order_by` and `limit` operations. Before building this into our application, let’s run a sample query to make sure it works. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} query_text = 'What is the expected EPS for Nvidia in Q1 2026?' sim = chunks_t.text.similarity(string=query_text) nvidia_eps_query = ( chunks_t.order_by(sim, asc=False) .select(similarity=sim, text=chunks_t.text) .limit(5) ) nvidia_eps_query.collect() ```
We perform this context retrieval for each row of our `queries` table by adding it as a computed column. In this case, the operation is a top-k similarity lookup against the data in the `chunks` table. To implement this operation, we’ll use Pixeltable’s `@query` decorator to enhance the capabilities of the `chunks` table. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # A @query is essentially a reusable, parameterized query that is attached to a table (or view), # which is a modular way of getting data from that table. @pxt.query def top_k(query_text: str): sim = chunks_t.text.similarity(string=query_text) return ( chunks_t.order_by(sim, asc=False) .select(chunks_t.text, sim=sim) .limit(5) ) # Now add a computed column to `queries_t`, calling the query # `top_k` that we just defined. queries_t.add_computed_column(question_context=top_k(queries_t.Question)) ```
  Added 8 column values with 0 errors.
  8 rows updated, 8 values computed.
Our `queries` table now looks like this: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} queries_t ```
The new column `question_context` now contains the result of executing the query for each row, formatted as a list of dictionaries: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} queries_t.select(queries_t.question_context).head(1) ```
### Asking the LLM Now it’s time for the final step in our application: feeding the document chunks and questions to an LLM for resolution. In this demo, we’ll use OpenAI for this, but any other inference cloud or local model could be used instead. We start by defining a UDF that takes a top-k list of context chunks and a question and turns them into a ChatGPT prompt. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Define a UDF to create an LLM prompt given a top-k list of # context chunks and a question. @pxt.udf def create_prompt(top_k_list: list[dict], question: str) -> str: concat_top_k = '\n\n'.join( elt['text'] for elt in reversed(top_k_list) ) return f""" PASSAGES: {concat_top_k} QUESTION: {question}""" ``` We then add that again as a computed column to `queries`: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} queries_t.add_computed_column( prompt=create_prompt(queries_t.question_context, queries_t.Question) ) ```
  Added 8 column values with 0 errors.
  8 rows updated, 16 values computed.
We now have a new string column containing the prompt: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} queries_t ```
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} queries_t.select(queries_t.prompt).head(1) ```
We now add another computed column to call OpenAI. For the `chat_completions()` call, we need to construct two messages, containing the instructions to the model and the prompt. For the latter, we can simply reference the `prompt` column we just added. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} from pixeltable.functions import openai # Assemble the prompt and instructions into OpenAI's message format messages = [ { 'role': 'system', 'content': 'Please read the following passages and answer the question based on their contents.', }, {'role': 'user', 'content': queries_t.prompt}, ] # Add a computed column that calls OpenAI queries_t.add_computed_column( response=openai.chat_completions( model='gpt-4o-mini', messages=messages ) ) ```
  Added 8 column values with 0 errors.
  8 rows updated, 8 values computed.
Our `queries` table now contains a JSON-structured column `response`, which holds the entire API response structure. At the moment, we’re only interested in the response content, which we can extract easily into another computed column: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} queries_t.add_computed_column( answer=queries_t.response.choices[0].message.content ) ```
  Added 8 column values with 0 errors.
  8 rows updated, 8 values computed.
We now have the following `queries` schema: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} queries_t ```
Let’s take a look at what we got back: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} queries_t.select( queries_t.Question, queries_t.correct_answer, queries_t.answer ).show() ```
The application works, but, as expected, a few questions couldn’t be answered due to the missing documents. As a final step, let’s add the remaining documents to our document base, and run the queries again. ## Incremental Updates Pixeltable’s views and computed columns update automatically in response to new data. We can see this when we add the remaining documents to our `documents` table. Watch how the `chunks` view is updated to stay in sync with `documents`: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} documents_t.insert({'document': p} for p in document_urls[3:]) ```
  Inserting rows into \`documents\`: 3 rows \[00:00, 569.05 rows/s]
  Inserting rows into \`chunks\`: 67 rows \[00:00, 325.91 rows/s]
  Inserted 70 rows with 0 errors.
  70 rows inserted, 6 values computed.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} documents_t.show() ```
(Note: although Pixeltable updates `documents` and `chunks`, it **does not** automatically update the `queries` table. This is by design: we don’t want all rows in `queries` to get automatically re-executed every time a single new document is added to the document base. However, newly-added rows will be run over the new, incrementally-updated index.) To confirm that the `chunks` index got updated, we’ll re-run the chunks retrieval query for the question `What is the expected EPS for Nvidia in Q1 2026?` Previously, our most similar chunk had a similarity score of \~0.8. Let’s see what we get now: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} nvidia_eps_query.collect() ```
Our most similar chunk now has a score of \~0.855 and pulls in more relevant chunks from the newly-inserted documents. Let’s recompute the `question_context` column of the `queries_t` table, which will automatically recompute the `answer` column as well. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} queries_t.recompute_columns('question_context') ```
  Inserting rows into \`queries\`: 8 rows \[00:00, 580.60 rows/s]
  8 rows updated, 40 values computed.
As a final step, let’s confirm that all the queries now have answers: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} queries_t.select( queries_t.Question, queries_t.correct_answer, queries_t.answer ).show() ```
# RAG Operations in Pixeltable Source: https://docs.pixeltable.com/howto/use-cases/rag-operations Open in Kaggle  Open in Colab  Download Notebook This documentation page is also available as an interactive notebook. You can launch the notebook in Kaggle or Colab, or download it for use with an IDE or local Jupyter installation, by clicking one of the above links. In this tutorial, we’ll explore Pixeltable’s flexible handling of RAG operations on unstructured text. In a traditional AI workflow, such operations might be implemented as a Python script that runs on a periodic schedule or in response to certain events. In Pixeltable, as with everything else, they are implemented as persistent table operations that update incrementally as new data becomes available. In our tutorial workflow, we’ll chunk PDF documents in various ways with a document splitter, then apply several kinds of embeddings to the chunks. ## Set Up the Table Structure We start by installing the necessary dependencies, creating a Pixeltable directory `rag_ops_demo` (if it doesn’t already exist), and setting up the table structure for our new workflow. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} %pip install -qU pixeltable sentence-transformers spacy tiktoken !python -m spacy download en_core_web_sm -q ``` ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} import pixeltable as pxt # Ensure a clean slate for the demo pxt.drop_dir('rag_ops_demo', force=True) # Create the Pixeltable workspace pxt.create_dir('rag_ops_demo') ``` ## Creating Tables and Views Now we’ll create the tables that represent our workflow, starting with a table to hold references to source documents. The table contains a single column `source_doc` whose elements have type `pxt.Document`, representing a general document instance. In this tutorial, we’ll be working with PDF documents, but Pixeltable supports a range of other document types, such as Markdown and HTML. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} docs = pxt.create_table('rag_ops_demo/docs', {'source_doc': pxt.Document}) ```
  Created table 'docs'.
If we take a peek at the `docs` table, we see its very simple structure. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} docs ```
Next we create a view to represent chunks of our PDF documents. A Pixeltable view is a virtual table, which is dynamically derived from a source table by applying a transformation and/or selecting a subset of data. In this case, our view represents a one-to-many transformation from source documents into individual sentences. This is achieved using Pixeltable’s built-in `document_splitter` class. Note that the `docs` table is currently empty, so creating this view doesn’t actually *do* anything yet: it simply defines an operation that we want Pixeltable to execute when it sees new data. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} from pixeltable.functions.document import document_splitter sentences = pxt.create_view( 'rag_ops_demo/sentences', # Name of the view docs, # Table from which the view is derived iterator=document_splitter( docs.source_doc, separators='sentence', # Chunk docs into sentences metadata='title,heading,sourceline', ), ) ``` Let’s take a peek at the new `sentences` view. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} sentences ```
We see that `sentences` inherits the `source_doc` column from `docs`, together with some new fields: * `pos`: The position in the source document where the sentence appears. * `text`: The text of the sentence. * `title`, `heading`, and `sourceline`: The metadata we requested when we set up the view. ## Data Ingestion Ok, now it’s time to insert some data into our workflow. A document in Pixeltable is just a URL; the following command inserts a single row into the `docs` table with the `source_doc` field set to the specified URL: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} docs.insert( [ { 'source_doc': 'https://raw.githubusercontent.com/pixeltable/pixeltable/main/docs/resources/rag-demo/Argus-Market-Digest-June-2024.pdf' } ] ) ```
  Inserting rows into \`docs\`: 1 rows \[00:00, 292.76 rows/s]
  Inserting rows into \`sentences\`: 217 rows \[00:00, 42910.00 rows/s]
  Inserted 218 rows with 0 errors.
  218 rows inserted, 2 values computed.
We can see that two things happened. First, a single row was inserted into `docs`, containing the URL representing our source PDF. Then, the view `sentences` was incrementally updated by applying the `document_splitter` according to the definition of the view. This illustrates an important principle in Pixeltable: by default, anytime Pixeltable sees new data, the update is incrementally propagated to any downstream views or computed columns. We can see the effect of the insertion with the `select` command. There’s a single row in `docs`: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} docs.select(docs.source_doc.fileurl).show() ```
And here are the first 20 rows in `sentences`. The content of the PDF is broken into individual sentences, as expected. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} sentences.select(sentences.text, sentences.heading).show(20) ```
## Experimenting with Chunking Of course, chunking into sentences isn’t the only way to split a document. Perhaps we want to experiment with different chunking methodologies, in order to see which one performs best in a particular application. Pixeltable makes it easy to do this, by creating several views of the same source table. Here are a few examples. Notice that as each new view is created, it is initially populated from the data already in `docs`. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} chunks = pxt.create_view( 'rag_ops_demo/chunks', docs, iterator=document_splitter( docs.source_doc, separators='sentence,token_limit', limit=2048, overlap=0, metadata='title,heading,sourceline', ), ) ```
  Inserting rows into \`chunks\`: 217 rows \[00:00, 47827.85 rows/s]
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} short_chunks = pxt.create_view( 'rag_ops_demo/short_chunks', docs, iterator=document_splitter( docs.source_doc, separators='sentence,token_limit', limit=72, overlap=0, metadata='title,heading,sourceline', ), ) ```
  Inserting rows into \`short\_chunks\`: 219 rows \[00:00, 49104.70 rows/s]
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} short_char_chunks = pxt.create_view( 'rag_ops_demo/short_char_chunks', docs, iterator=document_splitter( docs.source_doc, separators='sentence,char_limit', limit=72, overlap=0, metadata='title,heading,sourceline', ), ) ```
  Inserting rows into \`short\_char\_chunks\`: 459 rows \[00:00, 63241.10 rows/s]
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} chunks.select(chunks.text, chunks.heading).show(20) ```
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} short_chunks.select(short_chunks.text, short_chunks.heading).show(20) ```
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} short_char_chunks.select( short_char_chunks.text, short_char_chunks.heading ).show(20) ```
Now let’s add a few more documents to our workflow. Notice how all of the downstream views are updated incrementally, processing just the new documents as they are inserted. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} urls = [ 'https://raw.githubusercontent.com/pixeltable/pixeltable/main/docs/resources/rag-demo/Argus-Market-Watch-June-2024.pdf', 'https://raw.githubusercontent.com/pixeltable/pixeltable/main/docs/resources/rag-demo/Company-Research-Alphabet.pdf', 'https://raw.githubusercontent.com/pixeltable/pixeltable/main/docs/resources/rag-demo/Zacks-Nvidia-Report.pdf', ] docs.insert({'source_doc': url} for url in urls) ```
  Inserting rows into \`docs\`: 3 rows \[00:00, 1969.77 rows/s]
  Inserting rows into \`chunks\`: 742 rows \[00:00, 61926.41 rows/s]
  Inserting rows into \`short\_chunks\`: 747 rows \[00:00, 67743.68 rows/s]
  Inserting rows into \`sentences\`: 742 rows \[00:00, 67949.90 rows/s]
  Inserting rows into \`short\_char\_chunks\`: 1165 rows \[00:00, 3603.41 rows/s]
  Inserted 3399 rows with 0 errors.
  3399 rows inserted, 6 values computed.
## Further Experiments This is a good time to mention another important guiding principle of Pixeltable. The preceding examples all used the built-in `document_splitter` class with various configurations. That’s probably fine as a first cut or to prototype an application quickly, and it might be sufficient for some applications. But other applications might want to do more sophisticated kinds of chunking, implementing their own specialized logic or leveraging third-party tools. Pixeltable imposes no constraints on the AI or RAG operations a workflow uses: the iterator interface is highly general, and it’s easy to implement new operations or adapt existing code or third-party tools into the Pixeltable workflow. ## Computing Embeddings Next, let’s look at how embedding indices can be added seamlessly to existing Pixeltable workflows. To compute our embeddings, we’ll use the Huggingface `sentence_transformer` package, running it over the `chunks` view that broke our documents up into sentence-based chunks. Pixeltable has a built-in `sentence_transformer` adapter, and all we have to do is add a new column that leverages it. Pixeltable takes care of the rest, applying the new column to all existing data in the view. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} from pixeltable.functions.huggingface import sentence_transformer chunks.add_computed_column( minilm_embed=sentence_transformer( chunks.text, model_id='paraphrase-MiniLM-L6-v2' ) ) ```
  Added 959 column values with 0 errors.
  959 rows updated, 959 values computed.
The new column is a *computed column*: it is defined as a function on top of existing data and updated incrementally as new data are added to the workflow. Let’s have a look at how the new column affected the `chunks` view. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} chunks ```
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} chunks.select(chunks.text, chunks.heading, chunks.minilm_embed).head() ```
Similarly, we might want to add a CLIP embedding to our workflow; once again, it’s just another computed column: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} from pixeltable.functions.huggingface import clip chunks.add_computed_column( clip_embed=clip(chunks.text, model_id='openai/clip-vit-base-patch32') ) ```
  Added 959 column values with 0 errors.
  959 rows updated, 959 values computed.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} chunks ```
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} chunks.select(chunks.text, chunks.heading, chunks.clip_embed).head() ```
# Working with Voxel51 for Visualization in Pixeltable Source: https://docs.pixeltable.com/howto/working-with-fiftyone Open in Kaggle  Open in Colab  Download Notebook This documentation page is also available as an interactive notebook. You can launch the notebook in Kaggle or Colab, or download it for use with an IDE or local Jupyter installation, by clicking one of the above links. Pixeltable can export data directly from tables and views to the popular [Voxel51](https://voxel51.com/) frontend, providing a way to visualize and explore image and video datasets. In this tutorial, we’ll learn how to: * Export data from Pixeltable to Voxel51 * Apply labels from image classification and object detection models to exported data We begin by installing the necessary libraries for this tutorial. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} %pip install -qU pixeltable fiftyone torch transformers timm ``` ## Example 1: An Image Dataset ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} import fiftyone as fo import pixeltable as pxt # Create a Pixeltable directory for the demo. We first drop the directory if it # exists, in order to ensure a clean environment. pxt.drop_dir('fo_demo', force=True) pxt.create_dir('fo_demo') ```
  Connected to Pixeltable database at: postgresql+psycopg://postgres:@/pixeltable?host=/Users/pjlb/.pixeltable/pgdata
  Created directory 'fo\_demo'.
  \
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Create a Pixeltable table for our dataset and insert some sample images. url_prefix = 'https://raw.githubusercontent.com/pixeltable/pixeltable/main/docs/resources/images' urls = [ 'https://raw.githubusercontent.com/pixeltable/pixeltable/main/docs/resources/images/000000000019.jpg', 'https://raw.githubusercontent.com/pixeltable/pixeltable/main/docs/resources/images/000000000025.jpg', 'https://raw.githubusercontent.com/pixeltable/pixeltable/main/docs/resources/images/000000000030.jpg', 'https://raw.githubusercontent.com/pixeltable/pixeltable/main/docs/resources/images/000000000034.jpg', ] t = pxt.create_table('fo_demo/images', {'image': pxt.Image}) t.insert({'image': url} for url in urls) t.head() ```
  Created table 'images'.
  Inserted 4 rows with 0 errors in 0.71 s (5.60 rows/s)
Now we export our new table to a Voxel51 dataset and load it into a new Voxel51 session within our demo notebook. Once it’s been loaded, the images can be interactively navigated as with any other Voxel51 dataset. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} fo_dataset = pxt.io.export_images_as_fo_dataset(t, t.image) session = fo.launch_app(fo_dataset) ```
  You are running the oldest supported major version of MongoDB. Please refer to [https://deprecation.voxel51.com\ for\ deprecation\ notices.\ You\ can\ suppress\ this\ exception\ by\ setting\ your\ \\\`database\_validation\\\`\ config\ parameter\ to\ \\\`False\\\`.\ See\ https://docs.voxel51.com/user\_guide/config.html#configuring-a-mongodb-connection\ for\ more\ information](https://deprecation.voxel51.com\ for\ deprecation\ notices.\ You\ can\ suppress\ this\ exception\ by\ setting\ your\ \\`database_validation\\`\ config\ parameter\ to\ \\`False\\`.\ See\ https://docs.voxel51.com/user_guide/config.html#configuring-a-mongodb-connection\ for\ more\ information)
   28 \[31.4ms elapsed, ? remaining, 890.5 samples/s] 
## Adding Labels We’ll now show how Voxel51 labels can be attached to the exported dataset. Currently, Pixeltable supports only classification and detection labels; other Voxel51 label types may be added in the future. First, let’s generate some labels by applying two models from the Huggingface `transformers` library: A ViT model for image classification and a DETR model for object detection. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} from pixeltable.functions.huggingface import ( detr_for_object_detection, vit_for_image_classification, ) t.add_computed_column( classifications=vit_for_image_classification( t.image, model_id='google/vit-base-patch16-224' ) ) t.add_computed_column( detections=detr_for_object_detection( t.image, model_id='facebook/detr-resnet-50' ) ) ```
  Added 4 column values with 0 errors in 4.17 s (0.96 rows/s)
  Added 4 column values with 0 errors in 2.72 s (1.47 rows/s)
  4 rows updated.
Both models output JSON containing the model results. Let’s peek at the contents of our table now: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} t.head() ```
Now we need to transform our model data into the format the Voxel51 API expects (see the Pixeltable documentation for [pxt.io.export\_images\_as\_fo\_dataset](/sdk/latest/io#func-export_images_as_fo_dataset) for details). We’ll use Pixeltable UDFs to do the appropriate conversions. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf def vit_to_fo(vit_labels: list) -> list: return [ {'label': label, 'confidence': score} for label, score in zip( vit_labels['label_text'], vit_labels['scores'] ) ] @pxt.udf def detr_to_fo(img: pxt.Image, detr_labels: dict) -> list: result = [] for label, box, score in zip( detr_labels['label_text'], detr_labels['boxes'], detr_labels['scores'], ): # DETR gives us bounding boxes in (x1,y1,x2,y2) absolute (pixel) coordinates. # Voxel51 expects (x,y,w,h) relative (fractional) coordinates. # So we need to do a conversion. fo_box = [ box[0] / img.width, box[1] / img.height, (box[2] - box[0]) / img.width, (box[3] - box[1]) / img.height, ] result.append( {'label': label, 'bounding_box': fo_box, 'confidence': score} ) return result ``` We can test that our UDFs are working as expected with a `select()` statement. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} t.select( t.image, t.classifications, vit_to_fo(t.classifications), t.detections, detr_to_fo(t.image, t.detections), ).head() ```
Now we pass the modified structures to `export_images_as_fo_dataset`. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} fo_dataset = pxt.io.export_images_as_fo_dataset( t, t.image, classifications=vit_to_fo(t.classifications), detections=detr_to_fo(t.image, t.detections), ) session = fo.launch_app(fo_dataset) ```
   28 \[41.8ms elapsed, ? remaining, 669.2 samples/s] 
## Adding Multiple Label Sets You can include multiple label sets of the same type in the same dataset by passing a `list` or `dict` of expressions to the `classifications` and/or `detections` parameters. If a `list` is specified, default names will be assigned to the label sets; if a `dict` is specified, the label sets will be named according to its keys. As an example, let’s try recomputing our detections using the more powerful DETR model ResNet-101, and then load them into the same Voxel51 dataset as the earlier detections in order to compare them side-by-side. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} t.add_computed_column( detections_101=detr_for_object_detection( t.image, model_id='facebook/detr-resnet-101' ) ) ```
  Added 4 column values with 0 errors in 21.91 s (0.18 rows/s)
  4 rows updated.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} fo_dataset = pxt.io.export_images_as_fo_dataset( t, t.image, classifications=vit_to_fo(t.classifications), detections={ 'detections_50': detr_to_fo(t.image, t.detections), 'detections_101': detr_to_fo(t.image, t.detections_101), }, ) session = fo.launch_app(fo_dataset) ```
   28 \[44.2ms elapsed, ? remaining, 633.4 samples/s] 
Exploring the resulting images, we can see that the results are not much different between the two models, at least on our small sample dataset. # Cloud Storage Source: https://docs.pixeltable.com/integrations/cloud-storage Store and manage media files in cloud storage providers like S3, GCS, Azure, and more Hosted Cloud tables write media to their managed `pxtfs://org:db/home` store without configuration. For a local table that should write to S3, set `PIXELTABLE_OUTPUT_MEDIA_DEST=s3://my-bucket/output/` before creating the table. Computed media files then go to that bucket when rows are inserted. ## Supported providers Managed storage, no bucket setup required Native S3 storage with full feature support GCS buckets with gs\:// URI scheme Azure containers with wasb:// or abfs\:// schemes S3-compatible storage with zero egress fees Cost-effective S3-compatible storage Globally distributed S3-compatible storage ## How it works When you configure a storage destination, Pixeltable automatically: 1. **Uploads computed media**: AI-generated images, extracted video frames, and other computed media files are stored in your bucket 2. **Copies input media**: Optionally persists referenced media files for durability 3. **Manages file lifecycle**: Cleans up files when table data is deleted 4. **Handles caching**: Downloads files on-demand with intelligent local caching ## Which destination do you need? | Where the tables run | What to set | | - | - | | Hosted Cloud tables and services | Nothing. The database already writes to `pxtfs://org:db/home`. | | Local Pixeltable against that Cloud home bucket | `pxt login` or `PIXELTABLE_API_KEY` (API Keys, then export), plus `PIXELTABLE_INPUT_MEDIA_DEST` / `PIXELTABLE_OUTPUT_MEDIA_DEST` pointing at the `pxtfs://` URI. | | Your own S3 / GCS / Azure bucket | Provider keys in [Secrets](https://www.pixeltable.com/dashboard) or `pxt secret`, and those dest env vars pointing at `s3://`, `gs://`, or `wasbs://`. | Pixeltable reaches the home bucket with either credential: a `pxt login` session or an API key ([Signing in](/platform/cli#signing-in)). The key can also be `api_key` in the `[pixeltable]` section of the Pixeltable config file; `PIXELTABLE_API_KEY` wins when both are set, and either one outranks a `pxt login` session. Hosted pods get their own worker key from the platform; you do not set it. ## Configuration Cloud storage destinations are set as a default for the database, or per column. Local Pixeltable and bring-your-own buckets need this. A hosted Cloud database defaults to its home bucket and only needs it to use a bucket of its own. ### Default destinations You can set default destinations for media columns in three places, listed here from highest to lowest precedence (see [Configuration](/platform/configuration#per-database-settings) for details). The database entry in `pixeltable.toml`, for one database (see the [Cloud configuration reference](/platform/cli#cloud-configuration-reference) on the CLI page). Leave `name` off for the local database, or set `name = 'pxt://org:db'` for a hosted one: ```toml theme={"theme":{"light":"light-plus","dark":"dark-plus"}} [[pixeltable.database]] # For input media (inserted/referenced files) db_input_media_dest = "s3://my-bucket/input/" # For computed media (AI-generated outputs) db_output_media_dest = "s3://my-bucket/output/" ``` Environment variables, for the current process: ```bash theme={"theme":{"light":"light-plus","dark":"dark-plus"}} export PIXELTABLE_INPUT_MEDIA_DEST="s3://my-bucket/input/" export PIXELTABLE_OUTPUT_MEDIA_DEST="s3://my-bucket/output/" ``` The `[pixeltable]` section of the Pixeltable config file, for every database on the installation: ```toml theme={"theme":{"light":"light-plus","dark":"dark-plus"}} [pixeltable] input_media_dest = "s3://my-bucket/input/" output_media_dest = "s3://my-bucket/output/" ``` Configure these before creating tables. All media columns will automatically use the configured destinations. ### Per-column destination (computed columns only) For **computed columns**, you can override the default with a specific destination: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} import pixeltable as pxt TableModel = pxt.model_base() class Images(TableModel, name='images'): image: pxt.Image # uses input_media_dest if configured thumbnail = pxt.Column( value=image.resize((128, 128)), destination='s3://my-bucket/thumbnails/', ) ``` Then `pxt schema update app.py my_app`. In a notebook or a test, the same column is `t.add_computed_column(thumbnail=t.image.resize((128, 128)), destination=...)`. The `destination` parameter only applies to stored computed columns. For input columns, set a default input destination as shown above. ### Precedence rules Destinations are resolved in this order: 1. **Explicit column destination**: highest priority (computed columns only) 2. Database entry: `db_input_media_dest` / `db_output_media_dest` in `[[pixeltable.database]]` 3. Environment variable: `PIXELTABLE_INPUT_MEDIA_DEST` / `PIXELTABLE_OUTPUT_MEDIA_DEST` 4. Global config: `input_media_dest` / `output_media_dest` under `[pixeltable]` in `config.toml` 5. Fallback destination: `pxtfs://org:db/home` when the database is hosted in Cloud; local disk (usually `~/.pixeltable/media`) when it is running locally ## Provider configuration ### Pixeltable Cloud (home bucket) A Pixeltable Cloud database comes with a managed Media Store at `pxtfs://org:db/home`. Hosted tables and services write there automatically. No destination, no provider account, no credentials file. Cloud is in Limited Beta: email [contact@pixeltable.com](mailto:contact@pixeltable.com) to get an account. Use dest plus a credential only when a **local** Pixeltable process should write into that same bucket: ```bash theme={"theme":{"light":"light-plus","dark":"dark-plus"}} pxt login # or: export PIXELTABLE_API_KEY='your-key' export PIXELTABLE_INPUT_MEDIA_DEST='pxtfs://org:db/home' export PIXELTABLE_OUTPUT_MEDIA_DEST='pxtfs://org:db/home' ``` For a key instead of `pxt login`, create it under **API Keys** in the dashboard and export it, or set `[pixeltable].api_key` in `~/.pixeltable/config.toml`. Pixeltable does not load a `.env` file on its own. Provider keys (`AWS_ACCESS_KEY_ID`, `OPENAI_API_KEY`, ...) go under **Secrets** or `pxt secret set`. ``` pxtfs://org-slug:db-slug/home ``` Replace `org-slug` and `db-slug` with your Pixeltable Cloud organization and database names. ```bash theme={"theme":{"light":"light-plus","dark":"dark-plus"}} pxt login ``` Or `export PIXELTABLE_API_KEY='your-key'`. Pixeltable fetches temporary home-bucket credentials from the Cloud API with the key when one is set, and with the `pxt login` session otherwise. Hosted pods already have a worker key; do not copy that into your laptop `config.toml`. On Cloud, open **Storage** in the database sidebar and browse `home`. Nothing to configure for hosted tables. ``` https://www.pixeltable.com/dashboard/{org-slug}/{db-slug}/storage/home/browse ``` ### Amazon S3 ``` s3://bucket-name/optional/prefix/ ``` Uses standard AWS credential chain: * Environment variables (`AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY`) * On Pixeltable Cloud, store those names under **Secrets** (or `pxt secret`), not in `PIXELTABLE_API_KEY` * AWS credentials file (`~/.aws/credentials`) * IAM role (when running on AWS) Optionally specify a profile in `config.toml`: ```toml theme={"theme":{"light":"light-plus","dark":"dark-plus"}} [pixeltable] s3_profile = "my-aws-profile" ``` ### Google Cloud Storage ``` gs://bucket-name/optional/prefix/ ``` Uses Google Cloud Application Default Credentials: * Service account key file (`GOOGLE_APPLICATION_CREDENTIALS`) * gcloud CLI authentication * GCE metadata service (when running on GCP) ```bash theme={"theme":{"light":"light-plus","dark":"dark-plus"}} pip install google-cloud-storage ``` ### Azure Blob Storage Azure supports multiple URI schemes: ``` wasbs://container@account.blob.core.windows.net/prefix/ abfss://container@account.dfs.core.windows.net/prefix/ ``` Configure in `config.toml`: ```toml theme={"theme":{"light":"light-plus","dark":"dark-plus"}} [azure] storage_account_name = "myaccount" storage_account_key = "your-key-here" ``` Or via environment variables: ```bash theme={"theme":{"light":"light-plus","dark":"dark-plus"}} export AZURE_STORAGE_ACCOUNT_NAME="myaccount" export AZURE_STORAGE_ACCOUNT_KEY="your-key-here" ``` ```bash theme={"theme":{"light":"light-plus","dark":"dark-plus"}} pip install azure-storage-blob ``` ### Cloudflare R2 ``` https://account-id.r2.cloudflarestorage.com/bucket-name/prefix/ ``` Create an R2 API token and configure AWS-style credentials. In `~/.aws/credentials`: ```ini theme={"theme":{"light":"light-plus","dark":"dark-plus"}} [r2] aws_access_key_id = your-r2-access-key aws_secret_access_key = your-r2-secret-key ``` In `config.toml`: ```toml theme={"theme":{"light":"light-plus","dark":"dark-plus"}} [pixeltable] r2_profile = "r2" ``` ### Backblaze B2 ``` https://s3.region.backblazeb2.com/bucket-name/prefix/ ``` Create B2 application keys and configure AWS-style credentials. In `~/.aws/credentials`: ```ini theme={"theme":{"light":"light-plus","dark":"dark-plus"}} [b2] aws_access_key_id = your-b2-key-id aws_secret_access_key = your-b2-application-key ``` In `config.toml`: ```toml theme={"theme":{"light":"light-plus","dark":"dark-plus"}} [pixeltable] b2_profile = "b2" ``` ### Tigris ``` https://t3.storage.dev/bucket-name/prefix/ ``` Configure AWS-style credentials for Tigris. In `~/.aws/credentials`: ```ini theme={"theme":{"light":"light-plus","dark":"dark-plus"}} [tigris] aws_access_key_id = your-tigris-access-key aws_secret_access_key = your-tigris-secret-key ``` In `config.toml`: ```toml theme={"theme":{"light":"light-plus","dark":"dark-plus"}} [pixeltable] tigris_profile = "tigris" ``` ## Complete example Here's a full example using S3 for both input and computed media. First, configure the database's default destinations in `pixeltable.toml`: ```toml theme={"theme":{"light":"light-plus","dark":"dark-plus"}} [[pixeltable.database]] db_input_media_dest = "s3://my-app-bucket/uploads/" db_output_media_dest = "s3://my-app-bucket/generated/" ``` and, optionally, the AWS profile in `~/.pixeltable/config.toml` (default credentials are used if not set): ```toml theme={"theme":{"light":"light-plus","dark":"dark-plus"}} [pixeltable] s3_profile = "my-aws-profile" ``` Then declare the columns in your application file: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} import pixeltable as pxt from pixeltable.functions import openai TableModel = pxt.model_base() class Photos(TableModel, name='photos'): photo: pxt.Image # input media goes to input_media_dest thumbnail = pxt.Column( value=photo.resize((256, 256)), destination='s3://my-app-bucket/thumbnails/', # overrides output_media_dest ) description = openai.chat_completions( [ { 'role': 'user', 'content': [ {'type': 'text', 'text': 'Describe this image briefly.'}, {'type': 'image_url', 'image_url': photo}, ], } ], model='gpt-4o-mini', ) ``` `pxt schema update app.py production` creates the table. Insert and Pixeltable handles the uploads: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} import pixeltable as pxt t = pxt.get_table('production.photos') t.insert([ {'photo': 'https://example.com/image1.jpg'}, {'photo': '/local/path/to/image2.png'}, ]) # Query as usual: files are streamed/cached as needed t.select(t.photo, t.thumbnail, t.description).collect() ``` ## Best practices Structure your bucket with prefixes that reflect your application: ``` s3://my-bucket/ ├── production/ │ ├── uploads/ │ └── generated/ └── staging/ ├── uploads/ └── generated/ ``` Use different prefixes or buckets for input vs computed media: * Easier to set different retention policies * Clearer cost attribution * Simpler backup strategies Set up bucket lifecycle policies to automatically: * Transition old data to cheaper storage tiers * Delete temporary/staging data after a period * Enable versioning for critical data When running on cloud infrastructure, use IAM roles instead of access keys: * More secure (no key rotation needed) * Automatic credential refresh * Better audit trails ## Troubleshooting Verify your credentials have the necessary permissions: * `s3:GetObject`, `s3:PutObject`, `s3:DeleteObject` * `s3:ListBucket` for the bucket For GCS: `storage.objects.create`, `storage.objects.get`, `storage.objects.delete` * Ensure the bucket exists and the name is spelled correctly * Check the region matches your credential configuration * For S3-compatible providers, verify the endpoint URL is correct * Pixeltable uses connection pooling and parallel uploads automatically * Consider using a bucket in the same region as your compute * Check your network bandwidth and latency See the complete list of storage configuration options including profiles for S3, R2, B2, Tigris, and Azure. # Embedding Models Source: https://docs.pixeltable.com/integrations/embedding-model Wrap your own embedding model in a UDF and declare it as an index Pixeltable ships embedding functions for OpenAI, Hugging Face, Jina, Voyage, and more. When your model is not one of them, wrap it in a UDF and declare it in `__indexes__`. The index then loads with the existing rows and updates as new rows arrive. ## Quick start A UDF that returns a fixed-width array is usable as an embedding. Declare it on the model: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} import pixeltable as pxt import pixeltable.functions as pxtf TableModel = pxt.model_base() embed = pxtf.huggingface.sentence_transformer.using(model_id='intfloat/e5-large-v2') class Docs(TableModel, name='docs'): text: pxt.String __indexes__ = [pxt.EmbeddingIndex(text, string_embed=embed, name='docs_embed')] ``` `pxt schema update app.py my_app` creates the table and the index. Query it with `similarity()`: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} t = pxt.get_table('my_app.docs') sim = t.text.similarity(string='how do computed columns work') t.order_by(sim, asc=False).limit(10).select(t.text, sim).collect() ``` Application code declares indexes in `__indexes__`. In a notebook or a test, call `t.add_embedding_index('text', string_embed=embed)` instead. ## Your own model `.using()` covers a built-in function with a fixed parameter. For a model Pixeltable does not ship, write the UDF yourself. The return type fixes the dimension, and the index requires it: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf def my_embed(text: str) -> pxt.Array[(768,), pxt.Float]: return _model().encode(text) ``` Declaring it is the same: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} class Docs(TableModel, name='docs'): text: pxt.String __indexes__ = [pxt.EmbeddingIndex(text, string_embed=my_embed)] ``` ## Load the model once A UDF body runs per row. Loading weights inside it reloads them on every row. Cache the model at module scope so the cost is paid once per process: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} import functools @functools.cache def _model(): from sentence_transformers import SentenceTransformer return SentenceTransformer('intfloat/e5-large-v2') @pxt.udf def my_embed(text: str) -> pxt.Array[(768,), pxt.Float]: return _model().encode(text) ``` ## Batch the calls Embedding models are much faster on a batch than on single rows. A batched UDF takes and returns `Batch`, and Pixeltable groups rows for you up to `batch_size`: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} from pixeltable.func import Batch @pxt.udf(batch_size=32) def my_embed(texts: Batch[str]) -> Batch[pxt.Array[(768,), pxt.Float]]: return list(_model().encode(list(texts))) ``` The signature is the only change. The `__indexes__` declaration stays the same. This is how the built-in functions are written: `pxtf.huggingface.sentence_transformer` is a `@pxt.udf(batch_size=32)` over `Batch[str]`. ## Metric and precision `EmbeddingIndex` takes `metric` (`cosine`, `ip`, or `l2`; default `cosine`) and `precision` (`fp16` or `fp32`; default `fp16`). Match the metric to how your model was trained: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} __indexes__ = [ pxt.EmbeddingIndex(text, string_embed=my_embed, metric='ip', precision='fp32') ] ``` ## Additional resources Writing and batching UDFs Built-in embeddings and search Find embedding models # Ecosystem Source: https://docs.pixeltable.com/integrations/frameworks Built-in AI provider integrations as computed columns Each AI provider is a function you assign as a computed column on a `TableModel`. Create the table with `pxt schema update`. In notebooks you can still call `add_computed_column()`. Each provider page lists its API key and optional extras. If a provider is missing, wrap it in a [`@pxt.udf`](/platform/udfs-in-pixeltable) or ask on [GitHub Discussions](https://github.com/pixeltable/pixeltable/discussions). ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} import pixeltable as pxt import pixeltable.functions as pxtf TableModel = pxt.model_base() class Articles(TableModel, name='articles'): body: pxt.String response = pxtf.openai.chat_completions( messages=[{'role': 'user', 'content': body}], model='gpt-4o-mini', ) ``` ## Cloud LLM providers Claude models for language and multimodal generation Gemini via Google AI Studio or Vertex AI GPT models for text, embeddings, and image analysis OpenAI models via Azure Mistral language models DeepSeek language and code models Groq-hosted models ## Model hubs Platforms that host or route many models from a shared catalog. CLIP, DETR, sentence-transformers, ViT, Speech2Text, and more Run models on Replicate Open-source models via Together Fireworks inference One API across many LLM providers Models through Bedrock Nebius Token Factory via an OpenAI-compatible API ## Embeddings and reranking Embeddings and reranking for text, images, and video Embeddings and reranking for search Multimodal embeddings via the TwelveLabs Embed API ## Media generation FLUX image generation and editing from Black Forest Labs Flux, Stable Diffusion, and other image models Runway video generation ## Local LLM runtimes Local LLMs on CPU and GPU Local open-source models via Ollama High-throughput local inference ## Computer vision Object detection with YOLOX Image and video datasets with FiftyOne ## Audio Speech recognition with Whisper ## Enterprise Azure OpenAI through Microsoft Fabric ## DataFrames Import and export pandas DataFrames. Do not use pandas as the database that holds your app data. Provider notebooks: [docs/release/howto/providers](https://github.com/pixeltable/pixeltable/tree/main/docs/release/howto/providers). # AI coding agents Source: https://docs.pixeltable.com/overview/building-pixeltable-with-llms Install the Pixeltable skill, then use the same application file locally and on Pixeltable Cloud Hand the agent [get-started.md](https://www.pixeltable.com/get-started.md) to install the CLI, the [skill](https://github.com/pixeltable/pixeltable-skill), and MCP. After that, follow the [Quickstart](/overview/quick-start): generate an example app, create its tables, then start the endpoints. The skill tells the coding agent not to use LangChain, pandas as a database, per-row model loops, or a separate vector database. An application is a Python file that declares tables as `TableModel` classes. Notebooks can still use `pxt.create_table()`. ## Set Up Your AI Tool ```bash theme={"theme":{"light":"light-plus","dark":"dark-plus"}} npx skills add pixeltable/pixeltable-skill ``` Works with tools that support [Agent Skills](https://agentskills.io/specification): Cursor, Claude Code, Windsurf, Cline, OpenCode, Codex CLI, and others. To install the skill, run `npx skills add`. Do not copy this repository's `AGENTS.md` into an application; that file is for Pixeltable contributors. Same `npx skills add`, or the plugin: ```bash theme={"theme":{"light":"light-plus","dark":"dark-plus"}} /plugin marketplace add pixeltable/pixeltable-skill /plugin install pixeltable-skill@pixeltable-skill ``` If your tool has no skill support, append `.md` to any docs URL and paste that markdown into the chat. Site index: [llms.txt](https://docs.pixeltable.com/llms.txt) ([standard](https://llmstxt.org/)). The skill: [SKILL.md](https://raw.githubusercontent.com/pixeltable/pixeltable-skill/main/skills/pixeltable-skill/SKILL.md). | Resource | URL | | - | - | | This page as markdown | [building-pixeltable-with-llms.md](https://docs.pixeltable.com/overview/building-pixeltable-with-llms.md) | | Site index | [llms.txt](https://docs.pixeltable.com/llms.txt) | | Full map | [llms-full.txt](https://docs.pixeltable.com/llms-full.txt) | | The skill | [SKILL.md](https://raw.githubusercontent.com/pixeltable/pixeltable-skill/main/skills/pixeltable-skill/SKILL.md) | ## What the agent should write Write an application file, then create its tables with `pxt schema update app.py my_app`. When you insert a row, computed columns run automatically. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} import pixeltable as pxt import pixeltable.functions as pxtf TableModel = pxt.model_base() class Articles(TableModel, name='articles'): title: pxt.String body: pxt.String response = pxtf.openai.chat_completions( messages=[{'role': 'user', 'content': body}], model='gpt-4o-mini', ) summary = response.choices[0].message.content ``` ```bash theme={"theme":{"light":"light-plus","dark":"dark-plus"}} pxt schema update app.py my_app ``` After the schema exists, open the table with `t = pxt.get_table('my_app.articles')` and insert rows, or serve the same file over [HTTP](/howto/deployment/serving). To run that file on Pixeltable Cloud, sign in with `pxt login` and run `pxt db update`, then `pxt schema update`, then `pxt service update`, against `pxt://org:main`: [Pixeltable Cloud](/cloud). ## Choose a Pixeltable MCP server The [Pixeltable plugin](https://github.com/pixeltable/pixeltable-skill) packages the Skill for Claude, Cursor, and ChatGPT. Neither the plugin nor the Skill configures an MCP server; add the one the job needs. | Server | Connect to | Access | | - | - | - | | [Docs MCP](https://docs.pixeltable.com/mcp) | `https://docs.pixeltable.com/mcp` | Public documentation search. The page menu can install it in Cursor or VS Code. | | [Site MCP](https://www.pixeltable.com/mcp) | `https://www.pixeltable.com/mcp` (remote HTTP) | Public documentation search and integration listing. This URL accepts MCP JSON-RPC requests; the site's in-browser WebMCP tools are separate. | | [Cloud MCP](https://pixeltable.com/developers/mcp-cloud) | `https://www.pixeltable.com/mcp/cloud` | The Cloud organization selected during WorkOS AuthKit sign-in. Reads databases, services, catalog entries, schemas, up to 25 table rows per call, and logs. Starts a database at once. Stops a database, restarts a service, or sets a secret only when called again with `confirm: true` after returning a preview. Add `?read_only=true` for the read tools only, or `?db=` for one database. Connect it as a remote HTTP server in Claude Code, Cursor, or another MCP client. | | [Developer MCP](https://github.com/pixeltable/mcp-server-pixeltable-developer) | Local stdio command | Catalog inspection, queries, and REPL tools against a local or Cloud database. Install and run it on your machine; it has broader capabilities than the hosted Cloud MCP. | To deploy or delete, use the `pxt` CLI after `pxt login`: `pxt db update` and `pxt service update` ship code, and the hosted Cloud MCP does neither. Catalog inspection without importing Python: `pxt ls --json`, `pxt describe --json`, `pxt errors`. The `pxt` CLI (v0.7.4+) exposes `--json` on inspection and query commands. See the [CLI](/platform/cli). To call remote MCP tools from a table column, use `pxt.mcp_udfs(url)` (see [Agents as tables](/use-cases/agentic-workflows)). ## Next Write `app.py`, create the tables, then start the endpoints. Register tools and run `invoke_tools` as computed columns. Templates for serving, batch, and FastAPI + React. The same `app.py`. `pxt db update`, then `pxt schema update` and `pxt service update` against `pxt://org:main`. # How it works Source: https://docs.pixeltable.com/overview/how-it-works pxt schema update creates tables. pxt service update starts the endpoints. diff shows what would change. [Quickstart](/overview/quick-start) is the first run. This page explains what `pxt schema update` and `pxt service update` do, and what the last argument names. One Python file declares tables and, if you want endpoints, routes. `pxt schema update` creates the tables. `pxt service update` starts the endpoints on an assigned port. The last argument is a catalog such as `my_app`, or a `pxt://org:db` URI for Cloud. It does not create a folder beside `app.py`. A `TableModel` becomes a table. `pxt schema update` creates and migrates it. A `FastAPIRouter` in the same file. `pxt service update` binds it to the tables. A local catalog, or a `pxt://` URI for Cloud. Your repo holds `app.py` and usually `pixeltable.toml`. After `pxt schema update`, Python loads the table as `pxt.get_table('my_app.docs')`. Inspect with `pxt ls my_app`. Layout: [Infrastructure](/howto/deployment/infrastructure). `pxt schema` and `pxt service` both have these subcommands: | Command | What it does | | - | - | | `diff` | Prints pending changes; does not run `update` | | `update` | Creates or migrates tables or services so they match `app.py` | | `prune` | Deletes tables or services that are no longer in the file | | `example` | Write a working file to start from | Full flags: [CLI](/platform/cli). ## Schema An annotation (`title: pxt.String`) is a stored column: you insert that value. A bare type is required; use `pxt.String | None` when the value may be missing. Cast a JSON path such as `transcript.text` with `.astype(pxt.String | None)` when the value can be missing. `.astype(pxt.String)` aborts the insert if the value is None. An assignment (`title_upper = ...`) is a computed column. It runs on insert and on update. Use `pxt.Column(...)` when you need a primary key, `stored=False`, or a media option an annotation cannot express. Set `base=Docs` to make a view of `Docs`. The view can use columns from that base. Add an iterator when each input row should become many output rows, such as frames or chunks. A view (`base=`) cannot use `group_by`, aggregates, `order_by`, `limit`, or `join`. `.group_by()` on that view is allowed at query time ([Views](/platform/views)). Indexes belong on the model (`__indexes__` with `EmbeddingIndex` or `BtreeIndex`). B-tree indexes are not created automatically. Do not call `add_embedding_index()` after `pxt schema update`. `pxt schema example` writes a long sample. `--brief` writes only a small set of classes. Create tables with `pxt schema update app.py my_app`. Extra tables in that namespace stay until you run `pxt schema prune`, which deletes them. `pxt schema update` imports `app.py` as a module, so a `@pxt.udf` there is recorded by file path. A UDF defined in a script you run with `python app.py` is refused. Renaming `app.py` leaves columns that call it unable to compute. On Cloud, `pxt db update` puts the project in the image so workers can resolve that path ([Pixeltable Cloud](/cloud)). ## Services The same file declares a `FastAPIRouter`. `pxt service update` starts it against tables that already exist. Run `pxt schema update` first. `pxt service list` prints the URL; the port is assigned. Do not hard-code `:8000` for `pxt service update`. An insert route writes a row and returns the columns listed in `outputs`. A compute route runs those columns and writes no row. Update and delete match a row (primary key by default). File uploads, background jobs, query routes, and mounting on your own FastAPI app: [HTTP serving](/howto/deployment/serving). A schema change with a service already running needs `pxt service update` after `pxt schema update`. If you only change routes, run `pxt service update` again; it restarts the services whose routes changed. Inspect with `pxt dashboard`, `pxt ls`, and `pxt errors`. ## Database No Cloud account. Follow the [Quickstart](/overview/quick-start). For which process should listen, see [Self-hosting](/howto/deployment/overview). ```bash theme={"theme":{"light":"light-plus","dark":"dark-plus"}} pxt init pxt schema update app.py my_app pxt service update app.py my_app ``` Sign in with `pxt login`, or set `PIXELTABLE_API_KEY`. That variable, or `api_key` in the config file, takes precedence over a `pxt login` session. `pxt org create` provisions `main`. Add this hosted entry to `pixeltable.toml`, keeping the local entry that `pxt init` wrote: ```toml theme={"theme":{"light":"light-plus","dark":"dark-plus"}} [[pixeltable.database]] name = 'pxt://org:main' ``` If `pxt init` wrote to `pyproject.toml` instead, use `[[tool.pixeltable.database]]` for the hosted entry. Then run: ```bash theme={"theme":{"light":"light-plus","dark":"dark-plus"}} pxt db update pxt://org:main pxt schema update app.py pxt://org:main pxt service update app.py pxt://org:main ``` [Pixeltable Cloud](/cloud#first-deployment) shows the full setup. `pxt service run` only starts endpoints in your local terminal. ## Next Use the same `app.py` against `pxt://org:main`. Which process: nothing listening, your FastAPI, or `pxt service`. # Why Pixeltable? Source: https://docs.pixeltable.com/overview/pixeltable The unified multimodal backend agents build with. Database, orchestration, and serving in one Python file. Pixeltable Cloud is in Limited Beta. Email [contact@pixeltable.com](mailto:contact@pixeltable.com) if you are interested. ## Before Most stacks glue object storage, a warehouse, a vector database, an orchestrator, and custom endpoints. Each extra store or orchestrator is another system to operate. Pixeltable is the database, the orchestration, and the serving: tables, computed columns, indexes, and endpoints in **one Python file** (`app.py`). ## With Pixeltable Images, video, audio, documents, and ordinary columns. Incremental, not a pipeline you re-run. Embeddings stay current with the rows. Or skip `pxt service update` and insert from Python. If you process media and serve model pipelines, that file is the backend you keep. Insert a row. Transforms run. Indexes stay current. Locally, create those tables with `pxt schema update`. Start the endpoints with `pxt service update`. On [Pixeltable Cloud](/cloud), sign in with `pxt login`, or set `PIXELTABLE_API_KEY`. `pxt org create` provisions `main`. Then run `pxt db update`, `pxt schema update`, and `pxt service update` against `pxt://org:main`. `pxt db update` uploads the project onto that database. It does not insert rows and does not start app endpoints. Write `app.py`, run `pxt schema update` and `pxt service update`, then POST a row. ## What you declare A `TableModel` class becomes a table when you run `pxt schema update`. An annotation (`title: pxt.String`) is a stored column: the value you insert. A bare type is required; use `pxt.String | None` when the value may be missing. An assignment (`title_upper = ...`) is a computed column. It runs on insert and on update. An embedding index lives on the model, so new rows are searchable without a separate vector database. A `FastAPIRouter` in the same file defines endpoints, for example POST a row and get computed columns back. Endpoints are optional. Skip `pxt service update` if you insert from Python, then export. Views, UDFs, versioning, and import/export start in the [User Guide](/platform/type-system). Notebooks and tests can still call `pxt.create_table()`. An app declares tables in `app.py`. ## Built for RAG and live APIs. Tool calls as columns. Frames and transcripts as rows. Coming from Pinecone, LangChain, or hand-rolled FastAPI? See [Migrate](/howto/coming-from). The longer argument: [Making Multimodal More Lovable](https://www.pixeltable.com/blog/making-multimodal-more-lovable). # Quickstart Source: https://docs.pixeltable.com/overview/quick-start Install, write one application file, create tables, POST an insert Python 3.11+ on Linux, macOS, or Windows. Tables, computed columns, and endpoints live in one file. When you insert a row, computed columns run automatically. ## Install ```bash theme={"theme":{"light":"light-plus","dark":"dark-plus"}} python -m venv .venv source .venv/bin/activate # Windows: .venv\Scripts\activate pip install 'pixeltable[serve]' ``` ```bash theme={"theme":{"light":"light-plus","dark":"dark-plus"}} uv venv --python 3.12 source .venv/bin/activate # Windows: .venv\Scripts\activate uv pip install 'pixeltable[serve]' ``` `[serve]` pulls in FastAPI and uvicorn, which `pxt service` needs. Skip `[serve]` and `pxt service update` if you insert from Python; `pip install pixeltable` is enough. `pxt schema example --brief` writes models only. [Self-hosting](/howto/deployment/overview). ## Write the file, create tables, start the endpoints `pxt service example` writes a working application file: models and a `FastAPIRouter` in one place. ```bash theme={"theme":{"light":"light-plus","dark":"dark-plus"}} pxt service example --out app.py ``` `pxt service example` writes this application file. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} import pixeltable as pxt import pixeltable.functions as pxtf from pixeltable.serving import FastAPIRouter TableModel = pxt.model_base() @pxt.udf def excerpt(text: str, n: int = 12) -> str: return text if len(text) <= n else f'{text[:n]}...' class Docs(TableModel, name='docs'): id = pxt.Column(value=pxtf.uuid.uuid7(), primary_key=True) # a generated key: provided automatically on insert title: pxt.String body: pxt.String | None title_upper = pxtf.string.upper(title) # an assignment: computed on insert and on update summary = excerpt(title) ingest = FastAPIRouter(name='ingest') ingest.add_insert_route( Docs, path='/docs', inputs=[Docs.title, Docs.body], outputs=[Docs.id, Docs.title_upper, Docs.summary] ) ingest.add_update_route( # POST /docs/update takes id plus the new values Docs, path='/docs/update', inputs=[Docs.title], outputs=[Docs.id, Docs.title_upper] ) ingest.add_compute_route(Docs, path='/titles', inputs=[Docs.title], outputs=[Docs.title_upper]) ``` `title: pxt.String` is a value you insert. `title_upper = ...` is computed on insert and on update. `id` is a `uuid7()` primary key: filled on insert, required for `pxt get` and the errors view. The same file can hold `pxt.Image`, `pxt.Video`, `pxt.Audio`, or `pxt.Document`: [media pipelines](/use-cases/media-processing), [RAG](/use-cases/multimodal-backend). ```bash theme={"theme":{"light":"light-plus","dark":"dark-plus"}} pxt init pxt schema update app.py my_app pxt service update app.py my_app ``` `pxt init` marks this directory as a project root. `pxt schema update` errors if you have not run it in this directory or a parent. `my_app`, the last argument, is a catalog inside Pixeltable, not a folder beside `app.py`. `pxt schema update` creates that catalog and its tables. It does not start the endpoints. Run it before `pxt service update`, which starts the endpoints and does not create tables. Send a row with HTTP POST. Pixeltable computes `title_upper` during that insert. `pxt service list` prints the URL (the port is assigned): ```bash theme={"theme":{"light":"light-plus","dark":"dark-plus"}} pxt service list # my_app/ingest http://127.0.0.1: pid 12345 app.py curl -X POST http://127.0.0.1:/docs \ -H 'Content-Type: application/json' \ -d '{"title": "Hello", "body": "world"}' ``` ## The CLI `pxt` ships with the package. The first `pxt schema` or `pxt service` command starts a background process on `127.0.0.1:22089`, and that process keeps running after you close the terminal. `pxt health` confirms it. | Command | What it does | | - | - | | `pxt init` | Mark this directory as a project root | | `pxt service example --out app.py` | Write a working file: models plus routes | | `pxt schema example --out app.py` | Schema only. `--brief` writes a small set of classes; without it, every DSL construct | | `pxt schema update FILE my_app` | Create and migrate the tables `FILE` declares under last argument `my_app` | | `pxt service update FILE my_app` | Start the services `FILE` declares against those tables | | `pxt service list` | What is running, and the URL | | `pxt ls my_app` | Tables under the namespace | | `pxt rows my_app/docs` | Peek stored cells | | `pxt dashboard` | Local UI on the background process | `pxt schema` and `pxt service` both have `diff`, `update`, and `prune` subcommands. Full flags: [CLI](/platform/cli). Tables live under `~/.pixeltable` (`pgdata`, `media`, `file_cache`). That path is not the project directory. Project layout and `pixeltable.toml`: [Infrastructure](/howto/deployment/infrastructure). ## Same file, other first runs To run this same file on Pixeltable Cloud, sign in with `pxt login`, or set `PIXELTABLE_API_KEY`. The environment variable, or `api_key` in the config file, takes precedence over a `pxt login` session. `pxt org create` provisions `main`. Add this entry to the project file before `pxt db update`. Without it, the command errors. `pxt db update` uploads the project onto that database. `pxt schema update` creates tables. `pxt service update` starts hosted endpoints. Try changes by inserting from the [Cloud dashboard](https://www.pixeltable.com/dashboard) and running `pxt schema diff`. ```toml theme={"theme":{"light":"light-plus","dark":"dark-plus"}} [[pixeltable.database]] name = 'pxt://org:main' ``` ```bash theme={"theme":{"light":"light-plus","dark":"dark-plus"}} pxt db update pxt://org:main pxt schema update app.py pxt://org:main pxt service update app.py pxt://org:main ``` [Pixeltable Cloud](/cloud). [`uvx pixeltable-new`](https://github.com/pixeltable/pixeltable-new) copies a full app. The default copied app uses namespace `agent` as the last argument to `pxt schema update`. `--video` copies video search and uses `videointel`. ```bash theme={"theme":{"light":"light-plus","dark":"dark-plus"}} uvx pixeltable-new myapp cd myapp uv sync pxt schema update app.py agent pxt service update app.py agent ``` [Starter kit](/resources/starter-kit). This quickstart uses a `TableModel` in `app.py` and creates the tables with `pxt schema update`. Notebooks and tests can still call `pxt.create_table()`, `add_computed_column()`, and `add_embedding_index()`. The skill writes that `TableModel`. If the agent writes `create_table` in app code, names the file `schema.py`, or writes the removed command `pxt serve`, the installed skill is stale: this page wins. Reinstall: `npx skills add pixeltable/pixeltable-skill`. ## Next What `pxt schema` and `pxt service` do, including `diff`, `update`, and `prune`. The same `app.py`. `pxt db update`, then `pxt schema update` and `pxt service update` against `pxt://org:main`. # 10-Minute Tour Source: https://docs.pixeltable.com/overview/ten-minute-tour Open in Kaggle  Open in Colab  Download Notebook This documentation page is also available as an interactive notebook. You can launch the notebook in Kaggle or Colab, or download it for use with an IDE or local Jupyter installation, by clicking one of the above links. Welcome to Pixeltable! In this tutorial, we’ll survey how to create tables, populate them with data, and enhance them with built-in and user-defined transformations and AI operations. This notebook uses `pxt.create_table()` so you can run cells without a project. An app puts the same columns on a `TableModel` in `app.py` and creates the tables with `pxt schema update`. ## Install Python packages First run the following command to install Pixeltable and related libraries needed for this tutorial. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} %pip install -qU torch transformers timm openai pixeltable ``` ## Creating a table Let’s begin by creating a `demo` directory (if it doesn’t already exist) and a table that can hold image data, `demo/first`. The table will initially have just a single column to hold our input images, which we’ll call `input_image`. We also need to specify a type for the column: `pxt.Image`. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} import pixeltable as pxt # Create the directory `demo`, dropping it first (if it exists) # to ensure a clean environment. pxt.drop_dir('demo', force=True) pxt.create_dir('demo') # Create the table `demo/first` with a single column `input_image` t = pxt.create_table('demo/first', {'input_image': pxt.Image}) ```
  Connected to Pixeltable database at: postgresql+psycopg://postgres:@/pixeltable?host=/Users/asiegel/.pixeltable/pgdata
  Created directory 'demo'.
  Created table 'first'.
We can use `t.describe()` to examine the table schema. We see that it now contains a single column, as expected. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} t.describe() ```
The new table is initially empty, with no rows: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} t.count() ```
  0
Now let’s put an image into it! We can add images simply by giving Pixeltable their URLs. The example images in this demo come from the [COCO dataset](https://cocodataset.org/), and we’ll be referencing copies of them in the Pixeltable github repo. But in practice, the images can come from anywhere: an S3 bucket, say, or the local file system. When we add the image, we see that Pixeltable gives us some useful status updates indicating that the operation was successful. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} t.insert( [ { 'input_image': 'https://raw.githubusercontent.com/pixeltable/pixeltable/release/docs/resources/images/000000000025.jpg' } ] ) ```
  Inserted 1 row with 0 errors in 0.21 s (4.86 rows/s)
  1 row inserted.
We can use `t.head()` to examine the contents of the table. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} t.head() ```
## Adding computed columns Great! Now we have a table containing some data. Let’s add an object detection model to our workflow. Specifically, we’re going to use the ResNet-50 object detection model, which runs using the Huggingface DETR (“DEtection TRansformer”) model class. Pixeltable contains a built-in adapter for this model family, so all we have to do is call the `detr_for_object_detection` Pixeltable function. A nice thing about the Huggingface models is that they run locally, so you don’t need an account with a service provider in order to use them. This is our first example of a **computed column**, a key concept in Pixeltable. Recall that when we created the `input_image` column, we specified a type, `pxt.Image`, indicating our intent to populate it with data in the future. When we create a *computed* column, we instead specify a function that operates on other columns of the table. By default, when we add the new computed column, Pixeltable immediately evaluates it against all existing data in the table - in this case, by calling the `detr_for_object_detection` function on the image. Depending on your setup, it may take a minute for the function to execute. In the background, Pixeltable is downloading the model from Huggingface (if necessary), instantiating it, and caching it for later use. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} from pixeltable.functions import huggingface t.add_computed_column( detections=huggingface.detr_for_object_detection( t.input_image, model_id='facebook/detr-resnet-50' ) ) ```
  Added 1 column value with 0 errors in 3.26 s (0.31 rows/s)
  1 row updated.
Let’s examine the results. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} t.head() ```
We see that the model returned a JSON structure containing a lot of information. In particular, it has the following fields: * `label_text`: Descriptions of the objects detected * `boxes`: Bounding boxes for each detected object * `scores`: Confidence scores for each detection * `labels`: The DETR model’s internal IDs for the detected objects Perhaps this is more than we need, and all we really want are the text labels. We could add another computed column to extract `label_text` from the JSON struct: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} t.add_computed_column(detections_text=t.detections.label_text) t.head() ```
If we inspect the table schema now, we see how Pixeltable distinguishes between ordinary and computed columns. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} t.describe() ```
Now let’s add some more images to our table. This demonstrates another important feature of computed columns: by default, they update incrementally any time new data shows up on their inputs. In this case, Pixeltable will run the ResNet-50 model against each new image that is added, then extract the labels into the `detect_text` column. Pixeltable will orchestrate the execution of any sequence (or DAG) of computed columns. Note how we can pass multiple rows to `t.insert` with a single statement, which will insert them more efficiently. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} more_images = [ 'https://raw.githubusercontent.com/pixeltable/pixeltable/release/docs/resources/images/000000000030.jpg', 'https://raw.githubusercontent.com/pixeltable/pixeltable/release/docs/resources/images/000000000034.jpg', 'https://raw.githubusercontent.com/pixeltable/pixeltable/release/docs/resources/images/000000000042.jpg', 'https://raw.githubusercontent.com/pixeltable/pixeltable/release/docs/resources/images/000000000061.jpg', ] t.insert({'input_image': image} for image in more_images) ```
  Inserted 4 rows with 0 errors in 1.51 s (2.65 rows/s)
  4 rows inserted.
Let’s see what the model came up with. We’ll use `t.select` to suppress the display of the `detect` column, since right now we’re only interested in the text labels. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} t.select(t.input_image, t.detections_text).head() ```
## Pixeltable is persistent An important feature of Pixeltable is that *everything is persistent*. Unlike in-memory Python libraries such as Pandas, Pixeltable is a database: all your data, transformations, and computed columns are stored and preserved between sessions. To see this, let’s clear all the variables in our notebook and start fresh. You can optionally restart your notebook kernel at this point, to demonstrate how Pixeltable data persists across sessions. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Clear all variables in the notebook %reset -f # Instantiate a new client object import pixeltable as pxt t = pxt.get_table('demo/first') # Display just the first two rows, to avoid cluttering the tutorial t.select(t.input_image, t.detections_text).head(2) ```
## GPT-4o For comparison, let’s try running our examples through a generative model, Open AI’s `gpt-4o-mini`. For this section, you’ll need an OpenAI account with an API key. You can use the following command to add your API key to the environment (just enter your API key when prompted): ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} import getpass import os if 'OPENAI_API_KEY' not in os.environ: os.environ['OPENAI_API_KEY'] = getpass.getpass( 'Enter your OpenAI API key:' ) ``` Now we can connect to OpenAI through Pixeltable. This may take some time, depending on how long OpenAI takes to process the query. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} from pixeltable.functions import openai # Construct a message dict for OpenAI. It follows the same pattern # as the OpenAI SDK, except that in place of an image URL, we can # put a reference to our image column, and Pixeltable will do the # substitution once for each row of the table. messages = [ { 'role': 'user', 'content': [ {'type': 'text', 'text': "What's in this image?"}, {'type': 'image_url', 'image_url': t.input_image}, ], } ] t.add_computed_column( vision=openai.chat_completions(messages, model='gpt-4o-mini') ) ```
  Added 5 column values with 0 errors in 6.98 s (0.72 rows/s)
  5 rows updated.
Let’s see how GPT-4’s responses compare to the traditional discriminative (DETR) model. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} t.select(t.input_image, t.detections_text, t.vision).head() ```
It looks like OpenAI returned a whole range of context information along with the image descriptions. Let’s pluck out just the response content from inside those JSON structures, so that it’s easier to see in the table. Note that we can unpack JSON columns in Pixeltable the same way we would with ordinary Python dicts and lists. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} t.select( t.input_image, t.detections_text, t.vision['choices'][0]['message']['content'], ).head() ```
In addition to adapters for local models and inference APIs, Pixeltable can perform a range of more basic image operations. These image operations can be seamlessly chained with API calls, and Pixeltable will keep track of the sequence of operations, constructing new images and caching when necessary to keep things running smoothly. Just for fun (and to demonstrate the power of computed columns), let’s see what OpenAI thinks of our sample images when we rotate them by 180 degrees. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} t.add_computed_column(rot_image=t.input_image.rotate(180)) # This is identical to the preceding messages dict, but with # `t.rot_image` in place of `t.input_image`. messages = [ { 'role': 'user', 'content': [ {'type': 'text', 'text': "What's in this image?"}, {'type': 'image_url', 'image_url': t.rot_image}, ], } ] t.add_computed_column( rot_vision=openai.chat_completions(messages, model='gpt-4o-mini') ) ```
  Added 5 column values with 0 errors in 6.19 s (0.81 rows/s)
  5 rows updated.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} t.select( t.rot_image, t.rot_vision['choices'][0]['message']['content'] ).head() ```
## UDFs: Enhancing Pixeltable’s capabilities Another important principle of Pixeltable is that, although Pixeltable has a built-in library of useful operations and adapters, it will never prescribe a particular way of doing things. Pixeltable is built from the ground up to be extensible. Let’s take a specific example. Recall our use of the ResNet-50 detection model, in which the `detect` column contains a JSON blob with bounding boxes, scores, and labels. Suppose we want to create a column containing the single label with the highest confidence score. There’s no built-in Pixeltable function to do this, but it’s easy to write our own. In fact, all we have to do is write a Python function that does the thing we want, and mark it with the `@pxt.udf` decorator. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf def top_detection(detect: dict) -> str: scores = detect['scores'] label_text = detect['label_text'] # Get the index of the object with the highest confidence i = scores.index(max(scores)) # Return the corresponding label return label_text[i] ``` ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} t.add_computed_column(top=top_detection(t.detections)) ```
  Added 5 column values with 0 errors in 0.11 s (45.52 rows/s)
  5 rows updated.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} t.select(t.detections_text, t.top).show() ```
Congratulations! You’ve reached the end of the tutorial. Hopefully, this gives a good overview of the capabilities of Pixeltable, but there’s much more to explore. As a next step, you might check out one of the other tutorials, depending on your interests: * [Object Detection in Videos](/howto/use-cases/object-detection-in-videos) * [RAG Operations in Pixeltable](/howto/use-cases/rag-operations) * [Working with OpenAI in Pixeltable](/howto/providers/working-with-openai) ## Ship it as an app This notebook used `create_table` so you can run cells without a project. A `:` field is stored (you insert it). An `=` field is computed. After `pxt init`, create the tables with `pxt schema update`: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} import pixeltable as pxt import pixeltable.functions as pxtf TableModel = pxt.model_base() class First(TableModel, name='first'): input_image: pxt.Image detections = pxtf.huggingface.detr_for_object_detection(input_image) ``` ```bash theme={"theme":{"light":"light-plus","dark":"dark-plus"}} pxt init pxt schema update app.py demo ``` See the [Quickstart](/overview/quick-start). # CLI Reference Source: https://docs.pixeltable.com/platform/cli Inspect, query, and serve Pixeltable catalogs from the terminal with the pxt command. The `pxt` CLI ships with the `pixeltable` package. It covers two surfaces: * **Catalog operations** -- inspect, query, and manage tables, views, and directories. Backed by a long-lived local daemon so each command takes \~40 ms after the first invocation. * **Service deployment** -- run the services defined in an application file with `pxt service`. Requires the `serve` extra (which pulls in `fastapi[standard]` and `uvicorn`): ```bash theme={"theme":{"light":"light-plus","dark":"dark-plus"}} pip install 'pixeltable[serve]' ``` Verify the installation: ```bash theme={"theme":{"light":"light-plus","dark":"dark-plus"}} pxt --help pxt health ``` On the first catalog command, `pxt` auto-spawns a daemon bound to `127.0.0.1:22089`. The daemon survives across shells and stays warm for subsequent commands. Override the port with `PXT_PORT`. ## Command structure ```text theme={"theme":{"light":"light-plus","dark":"dark-plus"}} pxt [args...] ``` Use `pxt --help` for per-subcommand flags and examples. | Category | Commands | | - | - | | **Inspection** | `ls`, `describe`, `columns`, `computed`, `idxs`, `history`, `status`, `config` | | **Navigation** | `cd`, `pwd` | | **Query** | `rows`, `get`, `count`, `errors` | | **Mutation** | `drop`, `drop-dir`, `rename`, `mv`, `recompute`, `revert` | | **Schema** | `schema diff`, `schema update`, `schema prune` | | **Interactive** | `shell` | | **Serving** | `service` | | **Cloud** | `login`, `logout`, `whoami`, `db`, `secret`, `org`, `key` | | **Lifecycle** | `daemon`, `dashboard`, `health` | ### Universal flags These flags work the same way across the catalog commands that support them and are not repeated in the per-command tables below. | Flag | Available on | Description | | - | - | - | | `-h`, `--help` | Every command | Print the command's usage and examples | | `--json` | Inspection, query, and mutation commands (`ls`, `describe`, `columns`, `computed`, `idxs`, `history`, `status`, `config`, `rows`, `get`, `count`, `errors`, `drop`, `drop-dir`, `rename`, `mv`, `recompute`, `revert`), the `schema` and `service` verbs other than `example`, plus `init`, `login`, `whoami`, `db`, `secret`, `org`, `key`, and `daemon status` | Emit machine-readable JSON instead. Not accepted by `shell` (interactive REPL), `health` (always JSON), `dashboard` (URL launcher), the navigation commands (`cd`, `pwd`), `schema example` and `service example` (which print an example file), or the commands that print plain status lines (`logout`, `daemon start`/`stop`/`restart`). | | `--json-schema` | `schema diff`, `service diff`, `db diff`, `db status`, `service list` | Print the JSON Schema of that command's `--json` output, with descriptions of every field and enum value, and exit. | | `-n`, `--dry-run` | Every catalog mutation (`drop`, `drop-dir`, `rename`, `mv`, `recompute`, `revert`), plus `schema update`, `schema prune`, `service update` and `service prune` | Print the intended action; don't execute. | | `-f`, `--force` | Mutations that prompt for confirmation (`drop`, `drop-dir`, `recompute`, `revert`, `schema update`, `schema prune`, `service update`, `service prune`, `db update`, `db delete`) | Skip the `[y/N]` prompt. Required in non-interactive contexts. `rename` and `mv` don't prompt and don't accept `-f`. | ### Working directory `pxt cd` sets a working directory that is prepended to *relative* paths in later commands, and `pxt pwd` prints it -- the catalog analogue of a shell's `cd`/`pwd`. ```bash theme={"theme":{"light":"light-plus","dark":"dark-plus"}} pxt cd my_dir # relative paths now resolve under my_dir pxt pwd # print the current working directory pxt ls sub # lists my_dir/sub pxt cd .. # up one level pxt cd # clear it ``` Absolute paths ignore the working directory: a leading `/` (e.g. `pxt ls /other_dir`) resolves from the catalog root, and a `pxt://org:db/...` URI addresses a hosted catalog. `.` and `..` work in any path and resolve against the working directory; `..` at the catalog root keeps the root. The working directory is scoped to the **invoking terminal**, not the daemon globally -- it is keyed by the shell's session, sent with every command. So separate terminals have independent working directories, and it does **not** leak into subprocesses or agents you launch: a spawned process runs under its own session with no working directory, so its `pxt` commands resolve relative paths from the catalog root regardless of what you set interactively. Because of that isolation, scripts and agents should address the catalog with absolute paths (`/...` or `pxt://...`) and ignore the working directory; it is a convenience for interactive terminal use. ## Quick reference ```bash theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # inspect pxt ls -l # everything under root, with metadata pxt ls some_dir --counts # row counts (parallelized) pxt describe my_dir/my_table # schema pxt rows my_dir/my_table -n 5 # first 5 stored cells # query pxt get my_dir/my_table 42 # PK lookup pxt count my_dir/my_table # row count pxt errors my_dir/my_table # rows where a computed column failed # ops pxt drop my_dir/my_table -f pxt mv my_dir/my_table other_dir pxt recompute my_dir/my_table summary -f pxt revert my_dir/my_table --steps 3 -f # navigate pxt cd my_dir # set the working directory (this terminal) pxt pwd # print it # projects pxt init # mark the current directory as a project root # declarative schemas pxt schema check app.py # validate the file on its own, with no target pxt schema diff app.py my_app # what 'update' would change (exit 2 = drift) pxt schema diff --json-schema # JSON Schema for the --json output pxt schema update app.py my_app # create and migrate the declared tables pxt schema prune app.py my_app -f # drop the undeclared ones # interactive pxt shell # services declared in an application file pxt service check app.py # validate the file on its own, with no target pxt service diff app.py my_app # what 'update' would change (exit 2 = drift) pxt service diff --json-schema # JSON Schema for the --json output pxt service update app.py my_app # start them, restarting what changed pxt service list # what is running, and where pxt service logs my_app/ingest # what a service logged; pxt://myorg:mydb/my_app/ingest for a hosted one # cloud -- signing in pxt new # a free trial database, no account needed pxt login # approve a code in a browser; no API key needed pxt whoami # who this machine is signed in as, and which credential it sends pxt logout # forget this device's cached session, or its `pxt new` trial # cloud -- databases pxt db update pxt://myorg:mydb # create the database, or bring it up to what the project declares pxt db diff pxt://myorg:mydb # what 'update' would change pxt db diff --json-schema # JSON Schema for the --json output pxt db list # the databases in your org pxt db logs pxt://myorg:mydb # what the database's pod logged # cloud -- secrets pxt secret set pxt://myorg OPENAI_API_KEY=sk-... pxt secret list # every secret in your org and its databases # cloud -- keys pxt key create ci # a key that acts as you pxt key create app --grant access:pxt://myorg:mydb/services/ingest pxt key list # every key in the org, and who created it ``` ## Inspection commands ### `pxt ls` List entries under a directory. | Flag | Description | | - | - | | `-l`, `--long` | Include column count, last version, and flags | | `--counts` | Include row counts (runs `count()` per table, parallelized) | | `--tree` | Tree view | ```bash theme={"theme":{"light":"light-plus","dark":"dark-plus"}} pxt ls # root pxt ls some_dir # contents of some_dir pxt ls -l some_dir # with metadata pxt ls --counts # with row counts ``` Output of `pxt ls -l`: ```text theme={"theme":{"light":"light-plus","dark":"dark-plus"}} path kind cols version flags agent_demo dir - audio_chunks view 9 3 ci chess_vids table 3 3 ci chk table 2 1 i ``` Flag letters: `c` = has at least one computed column, `i` = has at least one index. ### `pxt describe` Show a table's schema and metadata. The plain form is human-readable; `--json` returns the full `get_metadata()` dict. ```bash theme={"theme":{"light":"light-plus","dark":"dark-plus"}} pxt describe my_dir/my_table pxt describe my_dir/my_table --json ``` ### `pxt columns` / `pxt computed` List columns for one or more tables. `pxt computed` is shorthand for `pxt columns --computed`. The path argument may be a single table or a directory; a directory path lists columns for every table beneath it, recursively. A directory path may be a local path or a hosted URI (`pxt://org:db/...`). With no path, every table in the in-process catalog is listed. | Flag | Description | | - | - | | `--computed` | Restrict to computed columns (no effect for `pxt computed`, which always sets it) | ```bash theme={"theme":{"light":"light-plus","dark":"dark-plus"}} pxt columns # every column in the catalog pxt columns my_dir/my_table # one table pxt columns my_dir # every table under a directory, recursively pxt columns pxt://org:db # every table in a hosted database pxt columns --computed # computed columns across every table pxt computed # same as above ``` ### `pxt idxs` List indexes. Shows both B-tree and embedding indexes by default; the `--embedding` flag restricts to embedding indexes. Like `pxt columns`, the path may be a single table or a directory (walked recursively), a hosted database root (`pxt://org:db`), or omitted for the whole in-process catalog. | Flag | Description | | - | - | | `--embedding` | Restrict to embedding indexes | ```bash theme={"theme":{"light":"light-plus","dark":"dark-plus"}} pxt idxs # every index pxt idxs my_dir/my_table # indexes on one table pxt idxs my_dir # every table under a directory, recursively pxt idxs pxt://org:db # every table in a hosted database pxt idxs --embedding # only embedding indexes ``` ### `pxt history` Show a table's version timeline. | Flag | Description | | - | - | | `-n N` | Show at most N most recent versions | ```bash theme={"theme":{"light":"light-plus","dark":"dark-plus"}} pxt history my_dir/my_table pxt history my_dir/my_table -n 5 # last 5 versions ``` ### `pxt status` Daemon and runtime state: pxt version, daemon PID, configured paths, total tables, total errors. | Flag | Description | | - | - | | `--sizes` | Also report media and file-cache disk usage (slower; scans the directories) | ```bash theme={"theme":{"light":"light-plus","dark":"dark-plus"}} pxt status pxt status --sizes --json ``` ### `pxt config` Every documented configuration setting with its current value and source (`env`, `file`, or `unset`). Credentials show `` when set; the `source` column reveals presence even when the value is masked. ```bash theme={"theme":{"light":"light-plus","dark":"dark-plus"}} pxt config pxt config --section openai pxt config --source env ``` ## Query commands ### `pxt rows` Show the first N rows of a table. Unstored computed columns are skipped by default (selecting one forces evaluation, which can invoke LLMs or expensive compute); pass them explicitly via `--cols` to include them. | Flag | Description | | - | - | | `-n N` | Number of rows (default 10) | | `--cols a,b,c` | Comma-separated column subset. Default: all stored columns | ```bash theme={"theme":{"light":"light-plus","dark":"dark-plus"}} pxt rows my_dir/my_table -n 3 pxt rows my_dir/my_table --cols id,text,score ``` ### `pxt get` Look up a single row by primary key. An `Int`, `Float`, or `UUID` PK column parses its value and rejects one that does not parse. A `String` PK column keeps the value as typed, so `007` stays `007`. Lookup by a PK of any other type, such as `Date` or `Timestamp`, is not supported yet and finds no row. The table must declare a primary key. Unstored computed columns are skipped unless requested explicitly via `--cols` (consistent with `rows`). | Flag | Description | | - | - | | `--cols a,b,c` | Comma-separated column subset. Default: all stored columns | ```bash theme={"theme":{"light":"light-plus","dark":"dark-plus"}} pxt get my_dir/my_table 42 # single-column PK, int pxt get my_dir/my_table some_string_id # single-column PK, string pxt get my_dir/my_table 42 abc # composite PK, in declared order pxt get my_dir/my_table 42 --cols id,text # restrict to listed columns ``` ### `pxt count` ```bash theme={"theme":{"light":"light-plus","dark":"dark-plus"}} pxt count my_dir/my_table # prints the integer pxt count my_dir/my_table --json ``` ### `pxt errors` List rows where a stored computed column failed. The table must have a primary key (so each failing row can be identified). | Flag | Description | | - | - | | `--col NAME` | Filter to a single computed column | ```bash theme={"theme":{"light":"light-plus","dark":"dark-plus"}} pxt errors my_dir/my_table pxt errors my_dir/my_table --col embedding ``` ## Mutation commands Every mutation accepts the universal `-n`/`--dry-run` and `--json` flags. The destructive ones (`drop`, `drop-dir`, `recompute`, `revert`) also prompt `[y/N]` with a TTY and accept `-f`/`--force` to skip the prompt; in non-interactive contexts they refuse to proceed without `-f`. `rename` and `mv` don't prompt: renaming or moving a catalog entry is reversible and doesn't lose data. ### `pxt drop` Drop a table or view. Use `pxt drop-dir` for directories. | Flag | Description | | - | - | | `--cascade` | Also drop dependent views | ```bash theme={"theme":{"light":"light-plus","dark":"dark-plus"}} pxt drop my_dir/my_table -f # drop a table pxt drop my_dir/my_table --cascade -f # also drop dependent views pxt drop my_dir/my_table -n # dry-run ``` ### `pxt drop-dir` Remove a directory. Use `pxt drop` for tables/views. | Flag | Description | | - | - | | `-r`, `--recursive` | Also remove contained tables/views/subdirectories | ```bash theme={"theme":{"light":"light-plus","dark":"dark-plus"}} pxt drop-dir my_dir -f # remove an empty directory pxt drop-dir my_dir -r -f # recursive: also remove contained tables/subdirs ``` ### `pxt rename` Rename in place; the parent directory is preserved. `` must be a single name component (no `/` or `.`). Takes only universal flags. ```bash theme={"theme":{"light":"light-plus","dark":"dark-plus"}} pxt rename my_dir/old_name new_name ``` ### `pxt mv` Move a table/view/dir under a different directory; the entry's own name is preserved. `` can be `''` or `/` for the root directory. Takes only universal flags. ```bash theme={"theme":{"light":"light-plus","dark":"dark-plus"}} pxt mv my_dir/my_table other_dir # -> other_dir/my_table pxt mv my_dir/my_table / # move to root ``` ### `pxt recompute` Recompute one or more computed columns of a table. Mirrors `Table.recompute_columns()`, without its `where` predicate. | Flag | Description | | - | - | | `--errors-only` | Only the rows whose value is an error. Takes a single column | | `--no-cascade` | Do not recompute the computed columns and views that transitively depend on this one | ```bash theme={"theme":{"light":"light-plus","dark":"dark-plus"}} pxt recompute my_dir/my_table summary -f # the column and its dependents pxt recompute my_dir/my_table summary embedding -f # several columns in one pass pxt recompute my_dir/my_table summary --errors-only -f # only the rows that failed pxt recompute my_dir/my_table summary -n # what it would recompute, and over how many rows ``` Recomputing evaluates the target columns over every row, which can require substantial computing resources. `--errors-only` limits that recomputation to only the rows that failed. `-n` reports the target table's own row count. ### `pxt revert` Undo recent ops on a table. Each revert undoes one op; `--steps` repeats. | Flag | Description | | - | - | | `--steps N` | Number of consecutive reverts (default 1) | ```bash theme={"theme":{"light":"light-plus","dark":"dark-plus"}} pxt revert my_dir/my_table -f # undo the last op pxt revert my_dir/my_table --steps 3 -f # roll back 3 versions ``` Revert is irreversible. Run `pxt history my_dir/my_table` first to see what would be undone. ## Project layout `pxt schema` and `pxt service` read a Python file, and the tables they create refer back to the UDFs that file calls. A reference is a module path, so the file has to belong to a project. The **project root** is the directory with the project configuration, and every local module path is relative to it. `pxt init` writes that configuration in the current directory: ```bash theme={"theme":{"light":"light-plus","dark":"dark-plus"}} cd ~/proj pxt init # writes pixeltable.toml, making ~/proj the project root ``` That file has one entry for each database in the project: ```toml theme={"theme":{"light":"light-plus","dark":"dark-plus"}} [[pixeltable.database]] # the local database vars.media_dest = 's3://bucket/prefix' # binds a ConfigVar declared by a schema [[pixeltable.database]] name = 'pxt://myorg:prod' # a hosted database system_dependencies = ['ffmpeg'] # what goes into its image ``` In a directory that already has a `pyproject.toml`, the same entry is appended there as `[[tool.pixeltable.database]]` instead, and that section marks the root. Every directory from the root down to a file becomes one component of that file's module path, so each of those directory names has to be a Python identifier: ``` ~/proj/pixeltable.toml # the project configuration; its directory is the root ~/proj/ad_gen/app.py # a UDF here is recorded as 'ad_gen.app.' ~/proj/ad_gen/functions.py # imported as 'from ad_gen.functions import ...' ``` Imports resolve from the root down. This is where a single-file recipe and a larger application diverge: a file directly under the root imports its neighbors by their bare names, so `recipe.py` and `functions.py` side by side use `from functions import ...`. Once an application moves into a subdirectory, that subdirectory joins the path: `from ad_gen.functions import ...`. A recorded path is how a later process -- the daemon, a serving worker, a hosted pod -- finds the UDF again, so a command given a file outside any project root is refused. `pxt schema check` and `pxt service check` validate a file on its own -- it imports without touching the catalog, it declares what the verb needs, and its columns refer to UDFs by paths another process can resolve: ```bash theme={"theme":{"light":"light-plus","dark":"dark-plus"}} pxt schema check app.py # exit 0 valid, 1 invalid pxt service check app.py --json ``` ## Schema management The commands above act on one object at a time. `pxt schema` works differently: you describe the tables you want in a Python file, and the CLI reconciles a catalog to that description. Provisioning an empty target and evolving an existing one are the same command, so there is no separate first-time step. | Command | Description | | - | - | | `pxt schema diff FILE my_app` | Show what `update` would change. Read-only | | `pxt schema update FILE my_app` | Create the tables the file declares under the last argument, and migrate existing ones | | `pxt schema prune FILE my_app` | Drop the tables under the last argument that the file does not declare | | `pxt schema check FILE` | Validate the file on its own. No catalog argument, and it reads no catalog | | `pxt schema example` | Write a working file to start from | `FILE` is a path to a Python file. The last argument is a catalog: a local directory such as `my_app`, or a `pxt://` URI. `update` creates it if it doesn't exist. ### The file The file defines one or more models on a `pxt.model_base()`. Each model becomes one table, named by `name=`. `pxt schema example` writes a file covering every construct the schema DSL supports, so you never have to start from a blank page and never have to look up a construct: ```bash theme={"theme":{"light":"light-plus","dark":"dark-plus"}} pxt schema example --out app.py ``` `pxt schema example --brief` writes the minimal version instead: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} import pixeltable as pxt import pixeltable.functions as pxtf TableModel = pxt.model_base() class Docs(TableModel, name='docs'): title: pxt.String body: pxt.String | None title_upper = pxtf.string.upper(title) class Titled(TableModel, name='titled', base=Docs.where(Docs.title != '')): headline = Docs.title_upper + '!' # a view of Docs, filtered by its base= query ``` An annotation (`name: type`) declares a stored column; an assignment (`name = expr`) declares a computed column. A model with `base=` becomes a view of that query's base model. The daemon imports the file, so it must be readable there. Its own directory is added to `sys.path`, so it can import modules sitting next to it. ### Reviewing and updating ```bash theme={"theme":{"light":"light-plus","dark":"dark-plus"}} pxt schema diff app.py my_app # review pxt schema update app.py my_app # create or migrate tables ``` `diff` prints one line per table, then one per operation: ```text theme={"theme":{"light":"light-plus","dark":"dark-plus"}} ~ my_app/docs update + column 'author' will be added safe - column 'body' will be dropped DESTRUCTIVE = my_app/titled_docs no change ! my_app/scratch extra (not in schema) Plan: 0 create, 1 update, 1 unchanged, 1 extra | 1 destructive ``` | Marker | Meaning | | - | - | | `+` | The table will be created, or the column/index will be added | | `~` | The table will be migrated | | `=` | The table already matches its model | | `-` | The column/index will be dropped | | `!` | The table cannot be migrated in place, or is not declared by the schema | ### `pxt schema update` `update` creates missing tables and migrates existing ones, adding and dropping columns and indexes. It takes the same flags as the other mutations, plus one of its own: | Flag | Description | | - | - | | `-n`, `--dry-run` | Print the plan; do not run `update`. Exit `2` if changes are pending | | `--allow-destructive` | Permit operations that drop a column or index | | `-f`, `--force` | Skip the `[y/N]` prompt shown before destructive operations | ```bash theme={"theme":{"light":"light-plus","dark":"dark-plus"}} pxt schema update app.py my_app # safe changes pxt schema update app.py my_app -n # the plan; does not run `update` pxt schema update app.py my_app --allow-destructive -f # including drops ``` If the plan contains a destructive operation and `--allow-destructive` is absent, `update` does not change the catalog and exits `3`. Dropping a column or an index destroys its data. Run `pxt schema diff` (or `update -n`) first: the plan marks every operation `safe`, `DESTRUCTIVE`, or `UNSUPPORTED`. Some differences cannot be migrated in place: a table declared where a view exists, a changed iterator, or a column whose type or properties changed. Those are reported as `UNSUPPORTED`, `update` changes nothing, and it exits `1`. Adjust the application file or the table by hand. ### Exit codes The schema commands report their outcome in the exit status, so a caller never has to parse the output: | Code | Meaning | | - | - | | `0` | The target agrees with the schema (including when there was nothing to do) | | `1` | Error: bad arguments, the file failed to import, or a table cannot be reconciled | | `2` | Changes are pending (`diff`, or `update -n` / `prune -n`) | | `3` | Refused: the plan is destructive and `--allow-destructive` was not given, or `-f` was needed to confirm without a terminal | A drift check in CI is therefore one command: ```bash theme={"theme":{"light":"light-plus","dark":"dark-plus"}} pxt schema diff app.py pxt://acme:main/prod # 0 = in sync, 2 = drift, 1 = error ``` ### Machine-readable plans `pxt schema diff --json` emits the whole plan as one object: `schema_file`, `catalog_dir`, `in_agreement`, `tables`, `extras`, and a `summary` with one count per resolution. Each entry in `tables` has a `path`, a `resolution`, a `destructive` flag, and its `ops`: ```json theme={"theme":{"light":"light-plus","dark":"dark-plus"}} { "path": "my_app/docs", "model_cls": "Docs", "kind": "table", "exists": true, "resolution": "update_destructive", "destructive": true, "ops": [ {"target": "column", "name": "author", "op": "add", "severity": "additive", "destructive": false, "description": "column 'author' will be added", "details": {"type": "String"}}, {"target": "column", "name": "body", "op": "drop", "severity": "destructive", "destructive": true, "description": "column 'body' will be dropped", "details": {}} ] } ``` An op's `target` is `column`, `index`, or `table`, and its `op` is `add`, `drop`, or `alter`. `name` is what it acts on -- a column, an index, the differing attribute when the target is a table, or the table path for a drop -- and `details` contains that operation's operands, such as the `type` of an added column. `severity` is `additive`, `destructive`, or `unsupported`; `destructive` is the boolean form of the middle case. A table's `resolution` is `up_to_date`, `create`, `update_additive`, `update_destructive`, or `unsupported`, and a `create` has no ops, because the create subsumes them. These field names and values are the catalog's own, as returned by `TableModel.get_model_diff()`, so a plan read from the CLI and a diff read from Python describe a change the same way. `pxt schema diff --json-schema` prints the JSON Schema of that output. `update` and `prune` return the same object with a `status` on every table and operation: | Status | Meaning | | - | - | | `applied` | Carried out | | `skipped` | Not carried out: a dry run, or nothing to do | | `refused` | Not carried out because consent was missing (`--allow-destructive`, or `-f` with no terminal) | Every path returns the plan, including the ones that refuse before reaching the daemon, so an `--allow-destructive` refusal is as machine-readable as a success: exit `3`, with the offending operations marked `refused` and the rest `skipped`. `prune` reports its drops in a top-level `ops` array, each with `target: "table"`, `op: "drop"`, and the dropped table's path in `name`. ### Pruning `update` only ever touches tables declared by the schema, so tables it doesn't know about accumulate. `diff` lists them as extras; `prune` drops them. A full reconcile is `update` followed by `prune`. | Flag | Description | | - | - | | `-n`, `--dry-run` | List what would be dropped; exit `2` if anything would be | | `-f`, `--force` | Skip the `[y/N]` prompt | ```bash theme={"theme":{"light":"light-plus","dark":"dark-plus"}} pxt schema prune app.py my_app -n # list what would be dropped pxt schema prune app.py my_app -f # drop it ``` Only tables under the last argument are considered, so nothing elsewhere in the catalog is affected, and declared tables are never dropped. A view is dropped before its base. Prune never force-drops: a table with a dependent outside the pruned set is left in place, and the drop fails with an error reporting that dependent. Pruning is irreversible. Run it with `-n` first. ## Interactive shell For agentic or scripted workloads that issue many commands in sequence, `pxt shell` amortizes Python startup over the session: ```text theme={"theme":{"light":"light-plus","dark":"dark-plus"}} $ pxt shell pxt> ls path kind agent_demo dir chess_vids table ... pxt> describe chess_vids ... pxt> exit ``` Inside the shell, every `pxt` command is available unmodified. Errors from one command don't kill the session. Use `help`, `exit`, `quit`, or Ctrl-D to leave. ## Output and scripting Most catalog commands accept `--json` for stable, machine-readable output; [Universal flags](#universal-flags) lists the commands that do not: ```bash theme={"theme":{"light":"light-plus","dark":"dark-plus"}} pxt ls --json | jq '.entries[] | select(.kind == "table")' pxt get my_dir/my_table 42 --json | jq '.row' pxt count my_dir/my_table --json | jq '.count' ``` Without `--json`, output is column-aligned text. The `schema` commands additionally report drift in their exit status (`0` in sync, `2` pending, `3` refused, `1` error), so a CI gate needs no output parsing. ## Serving `pxt service` runs the services in an application file. An application file is any Python source file containing table/view models and either `FastAPIRouter` instances (which serve routes over them) or a `fastapi.FastAPI` application of your own. It requires the `serve` extra (`pip install 'pixeltable[serve]'`), which pulls in `fastapi[standard]` and `uvicorn`. A service is a full FastAPI application with auto-generated [OpenAPI docs](https://fastapi.tiangolo.com/features/#automatic-docs) at `/docs`. For the API that defines the routes, see the [Python serving API](/howto/deployment/serving#mount-on-your-own-fastapi-app). One file contains both the schema and the services, so the same file drives both commands: ```bash theme={"theme":{"light":"light-plus","dark":"dark-plus"}} pxt service example --out app.py # a working file to start from pxt schema update app.py my_dir # create the tables declared by the models pxt service update app.py my_dir # serve this file's services against them ``` The last argument is the catalog the models are bound to, so the same file can run against a development directory and a production one. ### Serving your own application A file may supply its own `fastapi.FastAPI` object instead of leaving Pixeltable to build one. Then the file declares one service, named after its module, and `pxt service update` serves that application as it is: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # notes_app.py import fastapi import pixeltable as pxt TableModel = pxt.model_base() class Notes(TableModel, name='notes'): note_id = pxt.Column(type=pxt.Int, primary_key=True) text: pxt.String app = fastapi.FastAPI() @app.post('/notes') def add_note(note_id: int, text: str) -> dict[str, int]: return {'rows': Notes.insert([{'note_id': note_id, 'text': text}]).num_rows} ``` ```bash theme={"theme":{"light":"light-plus","dark":"dark-plus"}} pxt schema update notes_app.py my_dir # create the tables for the models in notes_app.py pxt service update notes_app.py my_dir # serve the application, as service 'notes_app' ``` The models in the file are bound to the last argument before the application serves, so a handler reaches them by name (`Notes.insert(...)`). All `FastAPIRouter` instances in the same source file are expected to be included in the `FastAPI` application (via `include_router()`), and the application file still produces a single service: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} app.include_router(ingest, prefix='/v1') # ingest's routes are served under /v1 ``` A router not included in `app` would never be served, and in that situation `pxt service` fails with an error. The routes of an included `FastAPIRouter` are diffed by their declarations (i.e., they take the data types of path parameters into account); the paths served by the application itself are diffed as path strings. ### Signing in Hosted commands need a credential. `pxt login`, `pxt whoami`, and `pxt key` require Pixeltable 0.7.10 or later. There are three: a `pxt login` session, a `pxt new` trial ([Trial databases](#trial-databases)), and an API key. You need one of them. ```bash theme={"theme":{"light":"light-plus","dark":"dark-plus"}} pxt login ``` `pxt login` prints a code and opens a browser. Approve it there, signing in or signing up, and the session lands on this machine. It uses the OAuth device grant, so nothing listens on a port and the browser need not be on this machine: this works over SSH. ```bash theme={"theme":{"light":"light-plus","dark":"dark-plus"}} pxt whoami you@example.com on https://internal-api.pixeltable.com Organization: org_01H8XYZ ``` A sign-in lasts as long as Pixeltable Cloud honors it: when its token expires, the next command that needs a token renews the session, and nothing on this machine expires it on a timer. `pxt whoami` reports whether Pixeltable Cloud recognizes the credential, and exits nonzero when it does not; it does not report whether the next command will succeed. A credential that is recognized but not permitted to list organizations, such as a key whose grants do not cover that, still counts as accepted: `pxt whoami` prints the refusal as a note and exits 0. `pxt whoami --offline` reports the cached session without asking. ```bash theme={"theme":{"light":"light-plus","dark":"dark-plus"}} pxt logout ``` `pxt logout` forgets this machine's cached session, or its `pxt new` trial ([Trial databases](#trial-databases)). When the session has the sign-in service's session id, `pxt logout` also opens that service's sign-out page for the session in your browser, and prints the link when no browser opens. It signs out of both because a browser that is still signed in confirms the next code without naming the account, so signing out of only one leaves you signed in as someone you did not choose. Forgetting the session needs no network. When `pxt logout` cannot work out the sign-out link, for example because the control plane cannot be reached, it still signs this machine out and warns that the browser was not signed out. Whether the sign-out page itself loads is something only the browser shows. An **API key** takes precedence over a sign-in whenever both are present: it is the explicit choice, and what CI runs on. Set one with `PIXELTABLE_API_KEY`, or with `api_key` in the `[pixeltable]` section of your Pixeltable config file; the environment variable wins when both are set. Unlike a sign-in, it does not expire. `pxt whoami` says which one your commands will actually send. Signing in proves who you are. If you belong to exactly one organization, `pxt login` also selects it. A brand-new account belongs to no organization, and every hosted command needs one, so `pxt login` and `pxt whoami` print "No organization yet" with the command that creates one, `pxt org create NAME`, rather than letting the next command fail as an authorization error. ### Trial databases ```bash theme={"theme":{"light":"light-plus","dark":"dark-plus"}} pxt new ``` `pxt new` needs no account. It creates a trial organization with one database, `main`, and caches the trial's API key on this machine, so later commands use it without `pxt login` or an exported key. It prints the database's URI, the `[[pixeltable.database]]` entry for `pixeltable.toml`, the next commands, and a claim link. It never prints the key, and neither does `pxt new --json`. Until it is claimed, the organization has 1 database and at most 2 services, each on 1 worker with up to 1 cpu, 2048 MB and 10 GB of disk, and 50 GB of media storage: past that, uploads stop. The server sets its expiry, normally 48 hours after creation. Its pods reach the internet on ports 80, 443 and 5432 only, and one network holds at most 10 unclaimed trials at once. Whoever opens the claim link signs in or signs up and becomes the organization's admin, so keep the link out of shared logs. Running `pxt new` again prints the same trial, without the claim link, and creates nothing. `pxt whoami` shows the trial and its expiry. `pxt logout` removes the trial's key from this machine and prints the claim link once more. It revokes nothing: the key works until the organization is claimed or expires. An API key or a `pxt login` session outranks a trial, so with either one configured, `pxt new` exits 1 and creates nothing. When the trial's key is rejected, the organization may have been claimed, and its person signs in with `pxt login`. `PIXELTABLE_SITE_URL` points `pxt new` at a site other than `https://www.pixeltable.com`. It must use https, except on localhost. ### Organizations ```bash theme={"theme":{"light":"light-plus","dark":"dark-plus"}} pxt org create acme --name "Acme Inc" ``` Creates the organization, its first database and its storage in one call. The name is the namespace in `pxt://acme:main`, and it must be unique across Pixeltable. It works with a `pxt login` session or with a personal API key, one created without grants; a key with grants belongs to its organization and cannot create another. Creating one also switches your `pxt login` session to it: Pixeltable Cloud takes the organization from your session, not from the URI. If the switch fails, the organization exists anyway, and `pxt org create` warns and says to run `pxt login`. An API key stays bound to its own organization and outranks a `pxt login` session, so to work in the new one, use a key created there, or remove the API key and then run `pxt login`. `pxt org list` shows the ones you can reach. ### Keys A key is the credential that does not expire, for the places a browser sign-in cannot reach: CI, a cron job, a deployed service. There is one command and two shapes, and the grants decide which. ```bash theme={"theme":{"light":"light-plus","dark":"dark-plus"}} pxt key create ci # acts as you pxt key create app --grant access:pxt://acme:main/services/ingest # acts as nobody ``` Without grants, the key acts as whoever created it and reaches whatever they reach: the API key you already know. With grants it belongs to the organization instead, and reaches only what it was granted, so a service you deploy has no more authority than its job needs. Keys with grants are a preview. Until they are enabled for your organization, Pixeltable Cloud refuses `pxt key create --grant` with "Keys with grants are not available yet". A grant is a verb on a resource, never a bare resource: a service on its own does not say whether the holder may call it or reconfigure it, and those are not the same permission. | Grant | What it allows | | - | - | | `access:pxt://org:db` | every service in the database, and its storage | | `access:pxt://org:db/services` | call any service in the database | | `access:pxt://org:db/services/name` | call that service, every route under it | | `manage:pxt://org:db/services` | create, list and manage any service in the database | | `manage:pxt://org:db/services/name` | start, stop, update, delete that service | A service is addressed by its base path and name, as in `access:pxt://acme:main/services/my_dir/ingest`. `manage` applies to services only, so `manage:pxt://org:db` is refused. `access` and `manage` are independent: a key that can call a service cannot reconfigure it, and one that can stop it cannot read what flows through it. The secret is shown once, when the key is created, and is never retrievable afterwards. Deleting a key revokes it. Every member of the organization sees every key, can change any runtime key's grants, and can delete any key, and `pxt key list` shows who created each one. ### Hosted targets The last argument may be a `pxt://org:db` URI, and the services then run in that database rather than on this machine. The database needs this project and its tables first, so the order is: ```bash theme={"theme":{"light":"light-plus","dark":"dark-plus"}} pxt db update pxt://acme:main # create the database and put this project's code in it pxt schema update app.py pxt://acme:main # create the tables declared by the models pxt service update app.py pxt://acme:main # start the services there ``` `diff`, `update`, `prune`, `stop`, `restart` and `list` all accept a hosted target. `run` does not: it serves from the calling process, so it is local by definition. Two verbs differ in meaning against a hosted database, where a stopped service keeps its registration: `stop` stops it and leaves it there to be started again, while `prune` forgets it. `pxt service stop` accepts a service address in several forms. A bare name matches the services of the current project, or every local service when the working directory is outside a project. `my_dir/NAME` stops the service under `my_dir`, including one in another project, and `/NAME` stops the service at the catalog root. A bare name that matches more than one service is refused, and the error lists the qualified path of each match. ```bash theme={"theme":{"light":"light-plus","dark":"dark-plus"}} pxt service stop ingest # the service named ingest in the current project pxt service stop my_dir/ingest # the service of that name under my_dir pxt service stop /ingest # the service of that name at the catalog root pxt service stop pxt://acme:main/ingest # the one in a hosted database ``` A name that matches no service, or matches one that is already stopped, is reported as `skipped` with the reason (`not found` or `already stopped`), so stopping a set of services can be repeated safely. `pxt service restart` cycles a service's pods onto the database's current image and project, keeping the service's registration and endpoint, and accepts the same service addresses as `stop`, except that a bare name matches every local service of that name, whatever its project. Use it after `pxt db update` to move a running service onto the new project, and to pick up the current values of its referenced secrets. Against a local target it stops the process and starts it again on the same port, so callers keep their address. `pxt service logs` reads a service's log and accepts the same service addresses as `restart`. `pxt db logs` reads the log of a hosted database's pod: ```bash theme={"theme":{"light":"light-plus","dark":"dark-plus"}} pxt service logs ingest # a bare name, when only one target has a service of that name pxt service logs my_dir/ingest # the service of that name under my_dir pxt service logs pxt://acme:main/my_dir/ingest # the one in a hosted database pxt service logs pxt://acme:main/ingest --since 10m --tail 50 pxt db logs pxt://acme:main ``` A hosted log merges the serving process's log records, requests included, with its console output, ordered by time. The console output contains the traceback of a service that failed to start. Health probes are left out unless `--include-health` is given. The log outlives the pod, so the log of a failed deploy can be read after its pod is gone. A line appears in the log a few seconds after it is written. A service running on this machine logs to a local file instead, and `pxt service logs` reports the path of that file. ### `pxt service` verbs | Command | Description | | - | - | | `pxt service diff APP my_app` | Report what `update` would change; exit `2` if anything is pending | | `pxt service update APP my_app` | Start the services declared by `APP`, and restart the ones whose declaration changed | | `pxt service run APP my_app [SERVICE]` | Serve one of them from this process instead, until interrupted | | `pxt service prune APP my_app` | Stop and forget the services under the last argument that `APP` does not declare | | `pxt service stop NAME...` | Stop the named services; a bare name matches within the current project | | `pxt service list [my_app]` | What is running at a local or hosted catalog, and where | | `pxt service logs NAME` | What the named service logged. Pass `my_app/NAME` or a `pxt://` path to disambiguate | | `pxt service check APP` | Validate the application file on its own. No catalog argument, and it reads no catalog | | `pxt service example` | Write a working application file to start from | Like the `schema` verbs, `diff` reports drift in its exit status: `0` in agreement, `2` changes pending, `3` refused, `1` error. So a CI gate needs no output parsing. `pxt service diff --json-schema` prints the JSON Schema of that output. ### Flags | Flag | Verbs | Description | | - | - | - | | `--json` | every verb except `example` | Emit machine-readable JSON | | `--since`, `--tail`, `--include-health` | `logs` | How far back, how many lines, whether to keep health probes | | `-f`, `--force` | `update`, `prune` | Skip the `[y/N]` prompt | | `-n`, `--dry-run` | `update`, `prune` | Print the intended change; do not run `update` or `prune` | | `--allow-destructive` | `update` | Permit changes that stop serving a route callers may be using | | `--host`, `--port` | `run` | Bind address and port (default `127.0.0.1:8000`) | | `--otel` | `run`, `update`, `diff` | Emit OpenTelemetry traces; see [Tracing](#tracing) | ### Tracing `--otel` emits OpenTelemetry traces from the served application: Pixeltable's own spans, nested under the request spans of the FastAPI app. It needs the instrumentation package (`pip install 'pixeltable[otel]'`), and the endpoint and service name come from the `OTEL_*` environment variables or the `[otel]` config section, as they do for any Pixeltable process. ```bash theme={"theme":{"light":"light-plus","dark":"dark-plus"}} pxt service run app.py my_dir --otel # traced, in this process pxt service update app.py my_dir --otel # traced, in the background process update starts ``` Tracing is configured once per process, so it is a property of the deployment rather than of the file: adding or dropping `--otel` restarts the service, and `pxt service diff app.py my_dir --otel` reports that restart as a pending change before `update` performs it. ### Background and foreground `update` starts **one background process per service**, each on its own port, and records it so `list` and `stop` can find it again. A service that crashes disappears from `list` with no cleanup step, because a record is only as live as the process it refers to. ```bash theme={"theme":{"light":"light-plus","dark":"dark-plus"}} pxt service update app.py my_dir pxt service list # my_dir/ingest http://127.0.0.1:41273 pid 51234 /home/me/app.py pxt service stop ingest ``` `run` stays in the foreground until you interrupt it and does not register the service for `list` or `stop`, so it is a separate command, not a flag on `update`. Use it as a container entrypoint or a development loop. It serves one service per process, the same as `update`; name a specific service as a third argument when the file declares more than one: ```bash theme={"theme":{"light":"light-plus","dark":"dark-plus"}} pxt service run app.py my_dir --port 9000 # Pixeltable is running on http://localhost:9000 # Routes: 4 # API docs at http://localhost:9000/docs ``` ### Restarts A service binds its models once, when its process starts, so a changed declaration takes effect when `pxt service update` replaces the process. `diff` reports which services that replacement would interrupt, and `update` reports them before it proceeds. Adding a route is additive; changing or removing one stops serving a contract that callers may be using, so it needs `--allow-destructive`. ## Cloud `pxt db` and `pxt org` manage cloud-hosted databases and organizations. Cloud commands need a credential: a `pxt login` session, a `pxt new` trial ([Trial databases](#trial-databases)), or an API key ([Signing in](#signing-in)). To use an API key, create it in the Cloud dashboard ([Get an API key](/cloud#get-an-api-key)) or with `pxt key create`, then export it as `PIXELTABLE_API_KEY`. The CLI reads the process environment and does not load `.env` on its own ([Configuration](/platform/configuration)). Every cloud command except `pxt logout` accepts `--json` for machine-readable output. ### Cloud configuration reference `pxt db diff` and `pxt db update` read the entry of the target database from the project configuration (`pixeltable.toml`, or `pyproject.toml` under `[tool.pixeltable]`), searched for upward from the working directory. #### `[[pixeltable.database]]` -- what a project declares about one database Defines the attributes of a specific Pixeltable database (which may be either the local database, or a cloud-hosted database), including: * Which project files to include * Which dependencies to include * Compute settings * Media destinations * Telemetry configuration ```toml theme={"theme":{"light":"light-plus","dark":"dark-plus"}} [[pixeltable.database]] name = 'pxt://myorg:prod' # matches the URI passed to pxt db update # where media without an explicit destination goes (default: the database's home bucket) db_input_media_dest = 's3://bucket/input/' db_output_media_dest = 's3://bucket/output/' # the OpenTelemetry exporter; set its auth headers with pxt secret set db_exporter_otlp_endpoint = 'https://otlp.example.com' db_exporter_otlp_protocol = 'http/protobuf' # or 'grpc' # which project files the database gets. Glob patterns relative to the project root; everything git # would not ignore is uploaded by default, and the lockfile always is. Patterns use git wildmatch, so # a bare `*.py` matches at every depth while a leading slash anchors it: `/*.py` is the root only. include = ["app/**", "models/**"] exclude = ["__pycache__", "*.pyc", ".git", ".env", "*.egg-info", ".venv"] # what goes into the image system_dependencies = ['ffmpeg'] # conda-forge packages python_version = '3.11' # what the database runs on cpu = 2.0 memory_mb = 4096 disk_gb = 50 workers = 2 # fixed when the database is created location = 'aws' region = 'us-east-1' ``` An entry with no `name` configures the local database. A hosted database is configured by the entry whose `name` is its URI. ### `pxt db` Manage cloud-hosted Pixeltable databases. A database is a hosted Pixeltable instance with its own compute, storage, and Python environment. Database URIs use the form `pxt://org:db`. Valid states: `PROVISIONING`, `STARTING`, `AVAILABLE`, `UPDATING`, `STOPPING`, `STOPPED`, `FAILED`. The URI argument is optional: when it is omitted, these commands use `db_uri` from the Pixeltable config file (see [Configuration](/platform/configuration)), so a project that sets it can run `pxt db status` and the other `db` commands with no argument. #### `pxt db update` ```bash theme={"theme":{"light":"light-plus","dark":"dark-plus"}} pxt db update pxt://myorg:mydb pxt db update pxt://myorg:mydb -n # print the plan and stop pxt db update pxt://myorg:mydb --allow-destructive ``` | Flag | Description | | - | - | | `-n`, `--dry-run` | Print the plan; do not run `update` | | `-f`, `--force` | Skip the confirmation | | `--allow-destructive` | Permit taking capacity away | `update` creates a hosted database and keeps it current afterwards. Put the database in a `[[pixeltable.database]]` entry, then run `update` against the URI in its `name`: ```toml theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # pixeltable.toml [[pixeltable.database]] name = 'pxt://myorg:mydb' ``` ```bash theme={"theme":{"light":"light-plus","dark":"dark-plus"}} pxt db update pxt://myorg:mydb ``` The URI on the command line selects the entry by its `name`: an entry for another database configures that one, and a target with no entry is an error. The first run creates the database, builds the image for its environment and uploads the project files that the pods run; every later run applies whatever has changed since. The [cloud configuration reference](#cloud-configuration-reference) lists everything an entry can declare. `update` applies a new image if dependencies changed, then uploaded project files if sources changed, then one resize for any changed CPU, memory, disk, or worker counts so the pods restart only once. Secrets are set with [`pxt secret`](#pxt-secret) rather than from the project. The media destinations and the OTLP endpoint and protocol travel with the project files, which always include the project configuration. #### `pxt db list` ```bash theme={"theme":{"light":"light-plus","dark":"dark-plus"}} pxt db list pxt db list --json ``` #### `pxt db status` ```bash theme={"theme":{"light":"light-plus","dark":"dark-plus"}} pxt db status pxt://myorg:mydb ``` Prints the state, the resources, the project, and the pods, and marks what a pending update would change. #### `pxt db start` ```bash theme={"theme":{"light":"light-plus","dark":"dark-plus"}} pxt db start pxt://myorg:mydb ``` Wake a stopped database. Polls until `AVAILABLE`. #### `pxt db stop` ```bash theme={"theme":{"light":"light-plus","dark":"dark-plus"}} pxt db stop pxt://myorg:mydb ``` Stop a running database (releases compute; storage is preserved). #### `pxt db restart` ```bash theme={"theme":{"light":"light-plus","dark":"dark-plus"}} pxt db restart pxt://myorg:mydb ``` Cycle the pods onto their current image and project, without rebuilding or uploading anything. Use it to pick up a secret set since they started. Polls until `AVAILABLE`; a stopped database is started instead. #### `pxt db diff` ```bash theme={"theme":{"light":"light-plus","dark":"dark-plus"}} pxt db diff pxt://myorg:mydb pxt db diff pxt://myorg:mydb --json ``` `pxt db diff` compares the hosted database to that config entry (see the [cloud configuration reference](#cloud-configuration-reference)): capacity, the Python image, and the project file archive -- | Artifact | Contents | Replaced when | | - | - | - | | the image | the Python environment, and no project code | the lockfile, `python_version`, `system_dependencies` or the Pixeltable version changes | | the project archive | every selected project file | any selected file changes | so an edit to a source file is uploaded in seconds, and only a dependency change costs an image build. Exit status is 0 in agreement and 2 with changes pending; nothing is built, resized or set. `pxt db diff --json-schema` prints the JSON Schema of that output. #### `pxt db build-image` ```bash theme={"theme":{"light":"light-plus","dark":"dark-plus"}} pxt db build-image pxt://myorg:mydb ``` Builds the image from the project environment without comparing anything first, which is how you force a rebuild after a failed one; `pxt db update` rebuilds only when that environment changed. The project files are uploaded with it, unless the database already has that exact archive. Polls until the build completes or fails; the pods restart on the new image and the new sources. When a command reports an unsupported proxy protocol version and this Pixeltable is newer than the hosted database, run this rebuild against the database URI from the failed command. What that image installs is described in [What to run after a change](/cloud#what-to-run-after-a-change). #### `pxt db delete` ```bash theme={"theme":{"light":"light-plus","dark":"dark-plus"}} pxt db delete pxt://myorg:mydb -f --json ``` Deletes the database, its storage, and all services. Irreversible. Prompts for confirmation with `[y/N]` on a TTY; otherwise exits with code 3. Pass `-f` or `--force` to skip confirmation. A database recreated under the same name is a different database: its workers are keyed by a new internal id, so credentials for the old database no longer work. Treat a delete followed by an `update` as a new deployment rather than a reset. ### `pxt org` #### `pxt org list` ```bash theme={"theme":{"light":"light-plus","dark":"dark-plus"}} pxt org list pxt org list --json ``` List all organizations accessible to the current credential. #### `pxt org status` ```bash theme={"theme":{"light":"light-plus","dark":"dark-plus"}} pxt org status # only when your credential reaches exactly one org pxt org status pxt://myorg # name it when your credential reaches several ``` Show the organization's name, ID, and default database. ### `pxt key` ```bash theme={"theme":{"light":"light-plus","dark":"dark-plus"}} pxt key create NAME # a key that acts as you pxt key create NAME --grant VERB:URI # a scoped key that belongs to the org (preview); repeatable pxt key list # every key in the org, and who created it pxt key update NAME --grant VERB:URI # add a grant pxt key update NAME --revoke VERB:URI # remove one pxt key delete NAME # revoke the secret ``` `create` prints the secret once. `list` never does. `update` is a delta, not a replacement, so widening a key does not depend on resending everything it already had. Revoking a grant the key never had is an error rather than a no-op, since a typo would otherwise read as a completed revocation. It applies only to a key that has grants: one that acts as you has nothing to change. Names are unique per organization across both shapes, so `pxt key delete NAME` never has to choose between two keys. Any member of the organization can delete any key. ### `pxt secret` ```bash theme={"theme":{"light":"light-plus","dark":"dark-plus"}} pxt secret list # every secret in your org and its databases pxt secret list pxt://myorg pxt secret list pxt://myorg:mydb pxt secret set pxt://myorg OPENAI_API_KEY=sk-... pxt secret delete pxt://myorg:mydb OLD_KEY ``` An org secret applies to every database in the org. A database secret applies to that database and wins on a key collision. `list` prints names, never values. `list` prints one table for the org and its databases. Without a URI it covers your credential's org; `pxt://myorg:mydb` narrows it to the org and that database. A database secret with the same name as an org secret is marked `overrides an organization secret`, and the org row stays in the table: ``` KEY SCOPE NOTE OPENAI_API_KEY pxt://myorg AWS_SECRET_ACCESS_KEY pxt://myorg:mydb OPENAI_API_KEY pxt://myorg:mydb overrides an organization secret ``` With `--json`, each row is an object with `key` and `scope`, plus `"overrides_org": true` on a database secret that overrides an org secret. With nothing set, `list` prints `No secrets for pxt://myorg.` (or the database's URI), and `list --json` prints `[]`. `set` and `delete` print the keys they changed as the same `KEY` and `SCOPE` rows, without `NOTE`; with `--json`, as `key` and `scope` objects. Earlier releases printed bare key names, and `--json` printed an array of strings. A running database or service uses the values from its startup. Run `pxt db restart` for the database's tables process and `pxt service restart` for each service. A database or service started afterwards reads the current value at startup. A `[[pixeltable.database]]` entry no longer accepts `secrets`. A project config that still has `secrets.openai_api_key = 'env:OPENAI_API_KEY'` fails to load: ``` RequestError: Invalid `DatabaseConfig` in config file: pixeltable.toml secrets Extra inputs are not permitted ``` Delete the `secrets.*` lines and set each value with `pxt secret set` instead. The values were never stored in the file, so nothing is lost. ## What's next * [Working with the Pixeltable CLI](/howto/cookbooks/core/working-with-cli): hands-on cookbook for inspect, query, debug, and serve workflows * [HTTP Serving Guide](/howto/deployment/serving): TOML config reference, Python `FastAPIRouter` API, decorator routes * [Configuration](/platform/configuration): API keys, storage paths, and environment settings # Configuration Source: https://docs.pixeltable.com/platform/configuration Complete guide to configuring Pixeltable ## Configuration options Pixeltable can be configured through: * Environment variables * System configuration file (`~/.pixeltable/config.toml` on Linux/macOS or `C:\Users\\.pixeltable\config.toml` on Windows) * `pxt.init()`, for the calling process only: `pxt.init({'openai.api_key': '...'})` The settings governing the file cache that the whole instance shares are read from the config file only: `file_cache_size_g` and `file_cache_lease_s`. Prefer environment variable names (`PIXELTABLE_API_KEY`, `PIXELTABLE_OUTPUT_MEDIA_DEST`). Pixeltable resolves environment variables from the process environment and never loads a `.env` file itself, so source one yourself first (`set -a; source .env; set +a`) or load it with a dotenv library. Provider keys go under **Secrets** or `pxt secret`. On a hosted Cloud database the home bucket is already the default media store; dest env vars are for local Pixeltable and bring-your-own buckets. See [Cloud storage](/integrations/cloud-storage). Example `config.toml`: ```toml theme={"theme":{"light":"light-plus","dark":"dark-plus"}} [pixeltable] file_cache_size_g = 250 time_zone = "America/Los_Angeles" hide_warnings = true verbosity = 2 [openai] api_key = 'my-openai-api-key' [openai.rate_limits] tts-1 = 500 # OpenAI uses a per-model rate limit configuration (see below for details) [mistral] api_key = 'my-mistral-api-key' rate_limit = 600 # Mistral uses a single rate limit for all models ``` ## System settings | Environment Variable | Config File | Meaning | | - | - | - | | PIXELTABLE\_HOME | | (string) Pixeltable user directory; default is \~/.pixeltable | | PIXELTABLE\_CONFIG | | (string) Pixeltable config file; default is \$PIXELTABLE\_HOME/config.toml | | PIXELTABLE\_PGDATA | | (string) Directory where Pixeltable DB is stored; default is \$PIXELTABLE\_HOME/pgdata | | PIXELTABLE\_DB | | (string) Pixeltable database name; default is pixeltable | | PIXELTABLE\_DB\_POOL\_SIZE | \[pixeltable]
db\_pool\_size | (int) Number of database connections this process keeps open; default is 5 | | PIXELTABLE\_DB\_POOL\_MAX\_OVERFLOW | \[pixeltable]
db\_pool\_max\_overflow | (int) Number of temporary database connections this process may open beyond `db_pool_size`; default is 10 | | | \[pixeltable]
file\_cache\_size\_g | (float) Maximum size of the Pixeltable file cache, in GiB; required | | PIXELTABLE\_TIME\_ZONE | \[pixeltable]
time\_zone | (string) Default time zone in [IANA format](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones); defaults to the system time zone | | PIXELTABLE\_HIDE\_WARNINGS | \[pixeltable]
hide\_warnings | (bool) Suppress warnings generated by various libraries used by Pixeltable; default is false | | PIXELTABLE\_VERBOSITY | \[pixeltable]
verbosity | (int) Verbosity for Pixeltable console logging (0: minimum, 1: normal, 2: maximum); default is 1 | | PXT\_PORT | | (int) Port the background process listens on; default is 22089. See [Dashboard](/platform/dashboard) | | PIXELTABLE\_API\_KEY | \[pixeltable]
api\_key | (string) API key for Pixeltable Cloud, created under API Keys. The environment variable wins over the config file, and either one outranks a `pxt login` session. To use Cloud without a key, run `pxt login` ([Signing in](/platform/cli#signing-in)). | | PIXELTABLE\_INPUT\_MEDIA\_DEST | \[pixeltable]
input\_media\_dest | (string) Default destination URI for media files that are inserted into tables | | PIXELTABLE\_OUTPUT\_MEDIA\_DEST | \[pixeltable]
output\_media\_dest | (string) Default destination URI for media files that are generated by Pixeltable operations | | OTEL\_EXPORTER\_OTLP\_ENDPOINT | \[otel]
exporter\_otlp\_endpoint | (string) OTLP endpoint for traces. Requires `pip install 'pixeltable[otel]'`. See [Observability](/platform/observability) | | OTEL\_EXPORTER\_OTLP\_PROTOCOL | \[otel]
exporter\_otlp\_protocol | (string) OTLP transport: `http/protobuf` (default) or `grpc` | | OTEL\_EXPORTER\_OTLP\_HEADERS | \[otel]
exporter\_otlp\_headers | (string) OTLP auth headers, e.g. `Authorization=Basic `. On a hosted database, set it with `pxt secret set` | | PIXELTABLE\_R2\_PROFILE | \[pixeltable]
r2\_profile | (string) Name of AWS config profile to use when accessing Cloudflare R2 resources. If not specified, default AWS credentials will be used. | | PIXELTABLE\_S3\_PROFILE | \[pixeltable]
s3\_profile | (string) Name of AWS config profile to use when accessing Amazon S3 resources. If not specified, default AWS credentials will be used. | | PIXELTABLE\_B2\_PROFILE | \[pixeltable]
b2\_profile | (string) Name of an S3-compatible profile for accessing Backblaze B2. Defaults to the standard AWS credential chain if not set. | | PIXELTABLE\_TIGRIS\_PROFILE | \[pixeltable]
tigris\_profile | (string) Name of an S3-compatible profile for accessing Tigris. Defaults to the standard AWS credential chain if not set. | ### Per-database settings Media destinations and the OTLP endpoint and protocol can also be set for a single database. Each database has a `[[pixeltable.database]]` entry in `pixeltable.toml`. Add a `db_` field to that entry to set `` for that database only. A `[[pixeltable.database]]` entry with no `name` corresponds to the local database. A `[[pixeltable.database]]` entry named `pxt://org:db` is a hosted database; its pods pick up the config when doing a `pxt db update`, and a running service moves onto it with `pxt service update`. ```toml theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # pixeltable.toml [[pixeltable.database]] db_input_media_dest = 's3://bucket/input/' db_output_media_dest = 's3://bucket/output/' db_exporter_otlp_endpoint = 'https://otlp.example.com' db_exporter_otlp_protocol = 'grpc' [[pixeltable.database]] name = 'pxt://myorg:mydb' db_input_media_dest = 's3://bucket/prod/input/' db_output_media_dest = 's3://bucket/prod/output/' db_exporter_otlp_endpoint = 'https://otlp.example.com' ``` Settings are resolved in this order: 1. **Database entry**: `db_` in `[[pixeltable.database]]` 2. **Environment variable**: the variable from the table above, or a `pxt.init()` override 3. **Global config**: `` under `[pixeltable]` / `[otel]` in `config.toml`, shared by every database on the installation 4. **Fallback**: a hosted database uses its home bucket for media The OTLP auth headers are a secret and do not go in the entry: set `OTEL_EXPORTER_OTLP_HEADERS` in the environment, and on a hosted database with `pxt secret set pxt://org:db OTEL_EXPORTER_OTLP_HEADERS=...` (see [`pxt secret`](/platform/cli#pxt-secret)). ## API configuration | Environment Variable | Config File | Meaning | | - | - | - | | ANTHROPIC\_API\_KEY | \[anthropic]
api\_key | (string) API key to use for Anthropic services | | AZURE\_STORAGE\_ACCOUNT\_NAME | \[azure]
storage\_account\_name | (string) Azure Storage account name for use with Azure Blob Storage | | AZURE\_STORAGE\_ACCOUNT\_KEY | \[azure]
storage\_account\_key | (string) Azure Storage account key for use with Azure Blob Storage | | BEDROCK\_API\_KEY | \[bedrock]
api\_key | (string) API key to use for AWS Bedrock services | | DEEPSEEK\_API\_KEY | \[deepseek]
api\_key | (string) API key to use for Deepseek services | | FAL\_API\_KEY | \[fal]
api\_key | (string) API key to use for fal.ai services | | FIREWORKS\_API\_KEY | \[fireworks]
api\_key | (string) API key to use for Fireworks AI services | | GEMINI\_API\_KEY | \[gemini]
api\_key | (string) API key for Google AI Studio (not used for Vertex AI) | | GOOGLE\_API\_KEY | | (string) Alternative API key for Google AI Studio (not used for Vertex AI) | | GOOGLE\_CLOUD\_LOCATION | | (string) Google Cloud region for Vertex AI | | GOOGLE\_CLOUD\_PROJECT | | (string) Google Cloud project ID for Vertex AI | | GOOGLE\_GENAI\_USE\_VERTEXAI | | (bool) Set to `true` to use Vertex AI instead of Google AI Studio | | GROQ\_API\_KEY | \[groq]
api\_key | (string) API key to use for Groq AI services | | HF\_TOKEN | \[hf]
token | (string) Hugging Face token for use with Hugging Face services | | MISTRAL\_API\_KEY | \[mistral]
api\_key | (string) API key to use for Mistral AI services | | OPENAI\_API\_KEY | \[openai]
api\_key | (string) API key to use for OpenAI services | | OPENAI\_BASE\_URL | \[openai]
base\_url | (string, optional) Base URL to use for OpenAI services | | OPENAI\_API\_VERSION | \[openai]
api\_version | (string) API version for use with Azure OpenAI; must be `'latest'` or `'preview'` | | OPENROUTER\_API\_KEY | \[openrouter]
api\_key | (string) API key to use for OpenRouter services | | OPENROUTER\_SITE\_URL | \[openrouter]
site\_url | (string) Application URL (optional, for OpenRouter analytics) | | OPENROUTER\_APP\_NAME | \[openrouter]
app\_name | (string) Application name (optional, for OpenRouter analytics) | | REPLICATE\_API\_TOKEN | \[replicate]
api\_token | (string) API token to use for Replicate services | | TOGETHER\_API\_KEY | \[together]
api\_key | (string) API key to use for Together AI services | | TWELVELABS\_API\_KEY | \[twelvelabs]
api\_key | (string) API key to use for TwelveLabs services | | VOYAGE\_API\_KEY | \[voyage]
api\_key | (string) API key to use for Voyage AI services | ## Rate limit configuration Pixeltable supports two patterns for configuring API rate limits in `config.toml`. Refer to the docstring of the relevant udf in the [SDK Reference](/sdk/latest) for details on the rate limiting pattern used by that udf. ### Single rate limit per provider For providers with a single rate limit across all models, add a `rate_limit` key to the provider's config section: ```toml theme={"theme":{"light":"light-plus","dark":"dark-plus"}} [mistral] api_key = 'my-mistral-api-key' rate_limit = 600 # requests per minute [fireworks] api_key = 'my-fireworks-api-key' rate_limit = 300 ``` ### Per-model rate limits For providers that support different rate limits for different models, add a `.rate_limits` section and list the rate limits for each model: ```toml theme={"theme":{"light":"light-plus","dark":"dark-plus"}} [openai] api_key = 'my-openai-api-key' [openai.rate_limits] gpt-4o = 500 gpt-4o-mini = 1000 tts-1 = 50 dall-e-3 = 10 [gemini.rate_limits] gemini-2.5-flash = 600 gemini-2.5-pro = 300 ``` If no rate limit is configured, Pixeltable uses a default of 600 requests per minute. ## Configuration best practices ### Security considerations When configuring API keys and sensitive information: * Avoid hardcoding API keys in your code * Use environment variables for temporary access * Use the config file for persistent configuration * Ensure your config.toml file has appropriate permissions (readable only by you) ### Performance tuning * Adjust `file_cache_size_g` based on your available system memory * For large datasets, increase the cache size to improve performance * Set appropriate verbosity level based on your debugging needs ## Applying configuration changes Configuration changes take effect when you restart your Python session. Return to the installation guide for setup instructions # Dashboard (local UI) Source: https://docs.pixeltable.com/platform/dashboard Browse tables, inspect pipelines, preview media, and debug computed columns from a local web UI served by the pxt daemon. The Pixeltable dashboard is a local web UI for exploring your data. It's served by the pxt daemon at [http://localhost:22089](http://localhost:22089). No extra install is needed. The dashboard ships inside the `pixeltable` package and uses the same daemon that backs the [`pxt` CLI](/platform/cli). ## Opening the dashboard Run `pxt dashboard` from a terminal: ```bash theme={"theme":{"light":"light-plus","dark":"dark-plus"}} pxt dashboard ``` This auto-spawns the daemon if it isn't already running, prints the URL, and opens it in your default browser. The daemon survives across shells and stays warm; subsequent invocations just hand off the URL. The dashboard binds to `127.0.0.1` only and is not accessible from other machines on the network. For remote access to tables and queries, see [HTTP Serving](/howto/deployment/serving). ## What you can do The dashboard is a **read-only inspector**. You browse and debug your data visually; all mutations (inserts, schema changes, etc.) happen through the Python SDK or [CLI](/platform/cli). ### Browse the catalog The left sidebar has a sticky **Local / Cloud** catalog switcher. You browse **one active catalog** at a time: the local catalog on this machine by default, or a hosted database you attach with a `pxt://org:db` URI. The tree under the switcher shows that catalog’s directories, tables, views, snapshots, and replicas. Each entry shows its kind (icon next to the name), version (`vN` when present), and error marks when relevant. Click a table to open it, or a directory for a summary and child list. Large catalogs get a **Filter…** field under the switcher to narrow the tree by name. That is separate from global search: **Cmd/Ctrl + K** (search icon in the sidebar header) finds directories, tables, and columns across attached catalogs. ### Inspect table data The main table view has three tabs: * **Data** - paginated rows with typed columns, sortable by indexed columns, with configurable page sizes. Cells render inline previews for images, video, audio, and documents. * **Lineage** - shows the table's position in the pipeline: its base table, derived views, and column-level dependency graphs. * **History** - version list with change types (inserts, updates, deletes) and timestamps. Above the data grid, a collapsible **COLUMNS** panel lists every column with its type, whether it is computed, stored vs. unstored, and any embedding indexes. Collapse it to a compact chip summary of the schema; expand for the full table. Click a computed expression to open a readable view (with a Raw toggle). The Copy button copies the original expression text, not the pretty-printed view. ### Filter and search rows A filter panel lets you narrow down the current page: * **Free-text search** across all columns (debounced) * **Faceted filters**: text contains, numeric ranges, datetime ranges, boolean/enum checklists * **Errors-only mode**: server-side filter that shows only rows with computation errors, useful for debugging failed computed columns ### Preview media Images and videos render inline in the data grid. Click any media cell to open a **lightbox** with keyboard navigation (arrow keys for prev/next). For image-heavy tables, a **gallery view** displays thumbnails in a grid with a detail overlay per row. ### Explore the pipeline The **Lineage** page (lineage icon in the sidebar header, next to Search) shows a full-instance pipeline graph built with React Flow. Every table and view appears as a node, with edges showing relationships (base table, iterator, query dependencies). Click any node to open a detail drawer with: * Row count and version info * Column breakdown (insertable, computed, stored) * Embedding index definitions * Computed column expressions with dependency graphs * Version history A **Find table** search on the canvas lets you jump to any node and fit it into view. ### Export data Download the current table as **CSV** (up to 100,000 rows) from the table view. ### Copy SDK snippets The table view includes a **Copy** button that generates the Python SDK code for opening the current table, making it easy to switch from visual inspection to scripted work. ## Configuration | Setting | Default | Environment variable | Description | | - | - | - | - | | Daemon port | `22089` | `PXT_PORT` | Port the daemon (and dashboard) listens on | Set the port before invoking any `pxt` command: ```bash theme={"theme":{"light":"light-plus","dark":"dark-plus"}} PXT_PORT=3000 pxt dashboard ``` ## Keyboard shortcuts | Shortcut | Action | | - | - | | **Cmd/Ctrl + K** | Open global search | | **Cmd/Ctrl + F** | Focus in-table row filter | | **Esc** | Close search, modals, or lightbox | | **Arrow keys** | Navigate media in lightbox | ## Troubleshooting **Port already in use**: If another process occupies port 22089, set a different port via `PXT_PORT=3000`. The daemon refuses to start on a bound port and the CLI surfaces the bind error. **Daemon not responding**: Check `pxt daemon status` for a quick health report. If the daemon is stuck, `pxt daemon stop` then `pxt daemon start` cycles it. **Static files missing**: The React app ships as pre-built static files inside the `pixeltable` package. If you installed from source, ensure the dashboard was built (`npm run build` in the `dashboard/` directory). # Embedding Indices Source: https://docs.pixeltable.com/platform/embedding-indexes Open in Kaggle  Open in Colab  Download Notebook This documentation page is also available as an interactive notebook. You can launch the notebook in Kaggle or Colab, or download it for use with an IDE or local Jupyter installation, by clicking one of the above links. This notebook is about vector (embedding) indexes. B-tree indexes are separate and opt-in: `pxt.BtreeIndex(col)` on a `TableModel`, or `t.add_btree_index('col')` in a notebook. This notebook uses `pxt.create_table()` and `add_embedding_index()` so you can run cells without a project. In an app, list embedding indexes on the model in `__indexes__` and create the tables with `pxt schema update`. * Indexing in Pixeltable is declarative * you create an index on a column and supply the embedding functions you want to use (for inserting data into the index as well as lookups) * Pixeltable maintains the index in response to any kind of update of the indexed table (i.e., `insert()`/`update()`/`delete()`) * Perform index lookups with the `similarity()` pseudo-function, in combination with the `order_by()` and `limit()` clauses To make this concrete, let’s create a table of images with the [`create_table()`](/sdk/latest/pixeltable#func-create_table) function. We’re also going to add some columns to demonstrate combining similarity search with other predicates. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} %pip install -qU pixeltable transformers sentence-transformers ```
  \[notice] A new release of pip is available: 25.3 -> 26.0.1
  \[notice] To update, run: pip install --upgrade pip
  Note: you may need to restart the kernel to use updated packages.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} import pixeltable as pxt # Delete the `indices_demo` directory and its contents, if it exists pxt.drop_dir('indices_demo', force=True) # Create the directory and table to use for the demo pxt.create_dir('indices_demo') schema = {'id': pxt.Int, 'img': pxt.Image} imgs = pxt.create_table('indices_demo/img_tbl', schema) ``` We start out by inserting 10 rows: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} img_urls = [ 'https://raw.githubusercontent.com/pixeltable/pixeltable/release/docs/resources/images/000000000030.jpg', 'https://raw.githubusercontent.com/pixeltable/pixeltable/release/docs/resources/images/000000000034.jpg', 'https://raw.githubusercontent.com/pixeltable/pixeltable/release/docs/resources/images/000000000042.jpg', 'https://raw.githubusercontent.com/pixeltable/pixeltable/release/docs/resources/images/000000000049.jpg', 'https://raw.githubusercontent.com/pixeltable/pixeltable/release/docs/resources/images/000000000057.jpg', 'https://raw.githubusercontent.com/pixeltable/pixeltable/release/docs/resources/images/000000000061.jpg', 'https://raw.githubusercontent.com/pixeltable/pixeltable/release/docs/resources/images/000000000063.jpg', 'https://raw.githubusercontent.com/pixeltable/pixeltable/release/docs/resources/images/000000000064.jpg', 'https://raw.githubusercontent.com/pixeltable/pixeltable/release/docs/resources/images/000000000069.jpg', 'https://raw.githubusercontent.com/pixeltable/pixeltable/release/docs/resources/images/000000000071.jpg', ] imgs.insert({'id': i, 'img': url} for i, url in enumerate(img_urls)) ```
  Inserted 10 rows with 0 errors in 2.53 s (3.96 rows/s)
  10 rows inserted.
For the sake of convenience, we’re storing the images as external URLs, which are cached transparently by Pixeltable. For details on working with external media files, see [Working with External Files](/platform/external-files). ## Creating an index To create and populate an index, we call [`Table.add_embedding_index()`](/sdk/latest/table#method-add_embedding_index) and tell it which UDF or UDFs to use to create embeddings. That definition is persisted as part of the table’s metadata, which allows Pixeltable to maintain the index in response to updates to the table. Any embedding UDF can be used for the index. For this example, we’re going to use a [CLIP](https://huggingface.co/docs/transformers/en/model_doc/clip) model, which has built-in support in Pixeltable under the [`pixeltable.functions.huggingface`](/sdk/latest/huggingface) package. As an alternative, you could use an online service such as OpenAI (see [`pixeltable.functions.openai`](/sdk/latest/openai)), or create your own embedding UDF with custom code (we’ll see how to do this below). Because we’re adding an index to an image column, the UDF we specify *must* be able to handle images. In fact, CLIP models are multimodal: they can handle both text and images, which is useful for doing lookups against the index. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} import PIL.Image from pixeltable.functions.huggingface import clip # create embedding index on the 'img' column imgs.add_embedding_index( 'img', embedding=clip.using(model_id='openai/clip-vit-base-patch32') ) ``` The first parameter of `add_embedding_index()` is the name of the column being indexed; the `embed` parameter specifies the relevant embedding. Notice the notation we used: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} clip.using(model_id='openai/clip-vit-base-patch32') ``` `clip` is a general-purpose UDF that can accept any CLIP model available in the Hugging Face model repository. To define an embedding, however, we need to provide a specific embedding function to `add_embedding_index()`: a function that is *not* parameterized on `model_id`. The `.using(model_id=...)` syntax tells Pixeltable to specialize the `clip` UDF by fixing the `model_id` parameter to the specific value `'openai/clip-vit-base-patch32'`. If you’re familiar with functional programming concepts, you might recognize .using() as a partial function operator. It’s a general operator that can be applied to any UDF (not just embedding functions), transforming a UDF with n parameters into one with k parameters by fixing the values of n-k of its arguments. Python has something similar in the functools package: the functools.partial() operator. `add_embedding_index()` provides a few other optional parameters: * `idx_name`: optional name for the index, which needs to be unique for the table; a default name is created if this isn’t provided explicitly * `metric`: the metric to use to compute the similarity of two embedding vectors; one of: * `'cosine'`: cosine distance (default) * `'ip'`: inner product * `'l2'`: L2 distance If desired, you can create multiple indexes on the same column, using different embedding functions. This can be useful to evaluate the effectiveness of different embedding functions side-by-side, or to use embedding functions tailored to specific use cases. In that case, you can provide explicit names for those indexes and then reference them during queries. We’ll illustrate that later with an example. ## Using the index in queries To take advantage of an embedding index when querying a table, we use the `similarity()` pseudo-function, which is invoked as a method on the indexed column, in combination with the [`order_by()`](/sdk/latest/query#method-order_by) and [`limit()`](/sdk/latest/query#method-limit) clauses. First, we’ll get a sample image from the table: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # retrieve the 'img' column of some row as a PIL.Image.Image sample_img = imgs.select(imgs.img).collect()[6]['img'] sample_img ``` We then call the `similarity()` pseudo-function as a method on the indexed column and apply `order_by()` and `limit()`. We used the default cosine distance when we created the index, so we’re going to order by descending similarity (`order_by(..., asc=False)`): ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} sim = imgs.img.similarity(image=sample_img) res = ( imgs.order_by(sim, asc=False) # Order by descending similarity .limit(2) # Limit number of results to 2 .select(imgs.id, imgs.img, sim) .collect() # Retrieve results now ) res ```
We can combine nearest-neighbor/similarity search with standard predicates. Here’s the same query, but filtering out the selected `sample_img` (which we already know has perfect similarity with itself): ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} res = ( imgs.order_by(sim, asc=False) .where(imgs.id != 6) # Additional clause .limit(2) .select(imgs.id, imgs.img, sim) .collect() ) res ```
## Index updates In Pixeltable, each index is kept up-to-date automatically in response to changes to the indexed table. To illustrate this, let’s insert a few more rows: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} more_img_urls = [ 'https://raw.githubusercontent.com/pixeltable/pixeltable/release/docs/resources/images/000000000080.jpg', 'https://raw.githubusercontent.com/pixeltable/pixeltable/release/docs/resources/images/000000000090.jpg', 'https://raw.githubusercontent.com/pixeltable/pixeltable/release/docs/resources/images/000000000106.jpg', 'https://raw.githubusercontent.com/pixeltable/pixeltable/release/docs/resources/images/000000000108.jpg', 'https://raw.githubusercontent.com/pixeltable/pixeltable/release/docs/resources/images/000000000139.jpg', 'https://raw.githubusercontent.com/pixeltable/pixeltable/release/docs/resources/images/000000000285.jpg', 'https://raw.githubusercontent.com/pixeltable/pixeltable/release/docs/resources/images/000000000632.jpg', 'https://raw.githubusercontent.com/pixeltable/pixeltable/release/docs/resources/images/000000000724.jpg', 'https://raw.githubusercontent.com/pixeltable/pixeltable/release/docs/resources/images/000000000776.jpg', 'https://raw.githubusercontent.com/pixeltable/pixeltable/release/docs/resources/images/000000000785.jpg', ] imgs.insert( {'id': 10 + i, 'img': url} for i, url in enumerate(more_img_urls) ) ```
  Inserted 10 rows with 0 errors in 1.29 s (7.75 rows/s)
  10 rows inserted.
When we now re-run the initial similarity query, we get a different result: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} sim = imgs.img.similarity(image=sample_img) res = ( imgs.order_by(sim, asc=False) .limit(2) .select(imgs.id, imgs.img, sim) .collect() ) res ```
## Similarity search on different types Because CLIP models are multimodal, we can also do lookups by text. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} sim = imgs.img.similarity(string='train') # String lookup res = ( imgs.order_by(sim, asc=False) .limit(2) .select(imgs.id, imgs.img, sim) .collect() ) res ```
## Creating multiple indexes on a single column We can create multiple embedding indexes on the same column, utilizing different embedding models. In order to use a specific index in a query, we need to assign it a name and then use that name in the query. To illustrate this, let’s create a table with text (taken from the Wikipedia article on [Pablo Picasso](https://en.wikipedia.org/wiki/Pablo_Picasso)): ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} txts = pxt.create_table('indices_demo/text_tbl', {'text': pxt.String}) sentences = [ 'Pablo Ruiz Picasso (25 October 1881 – 8 April 1973) was a Spanish painter, sculptor, printmaker, ceramicist, and theatre designer who spent most of his adult life in France.', 'One of the most influential artists of the 20th century, he is known for co-founding the Cubist movement, the invention of constructed sculpture,[8][9] the co-invention of collage, and for the wide variety of styles that he helped develop and explore.', "Among his most famous works are the proto-Cubist Les Demoiselles d'Avignon (1907) and the anti-war painting Guernica (1937), a dramatic portrayal of the bombing of Guernica by German and Italian air forces during the Spanish Civil War.", 'Picasso demonstrated extraordinary artistic talent in his early years, painting in a naturalistic manner through his childhood and adolescence.', 'During the first decade of the 20th century, his style changed as he experimented with different theories, techniques, and ideas.', 'After 1906, the Fauvist work of the older artist Henri Matisse motivated Picasso to explore more radical styles, beginning a fruitful rivalry between the two artists, who subsequently were often paired by critics as the leaders of modern art.', "Picasso's output, especially in his early career, is often periodized.", 'While the names of many of his later periods are debated, the most commonly accepted periods in his work are the Blue Period (1901–1904), the Rose Period (1904–1906), the African-influenced Period (1907–1909), Analytic Cubism (1909–1912), and Synthetic Cubism (1912–1919), also referred to as the Crystal period.', "Much of Picasso's work of the late 1910s and early 1920s is in a neoclassical style, and his work in the mid-1920s often has characteristics of Surrealism.", 'His later work often combines elements of his earlier styles.', ] txts.insert({'text': s} for s in sentences) ```
  Inserted 10 rows with 0 errors in 0.03 s (301.25 rows/s)
  10 rows inserted.
When calling [`add_embedding_index()`](/sdk/latest/table#method-add_embedding_index), we now specify the index name (`idx_name`) directly. If it is not specified, Pixeltable will assign a name (such as `idx0`). ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} from pixeltable.functions.huggingface import sentence_transformer txts.add_embedding_index( 'text', idx_name='minilm_idx', embedding=sentence_transformer.using( model_id='sentence-transformers/all-MiniLM-L12-v2' ), ) txts.add_embedding_index( 'text', idx_name='e5_idx', embedding=sentence_transformer.using(model_id='intfloat/e5-large-v2'), ) ``` To do a similarity query, we now call `similarity()` with the `idx` parameter: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} sim = txts.text.similarity('cubism', idx='minilm_idx') res = ( txts.order_by(sim, asc=False) .limit(2) .select(txts.text, sim) .collect() ) res ```
## Using a UDF for a custom embedding ## Indexing precomputed embeddings (Array columns) If your data already contains precomputed embedding vectors — for example, embeddings exported from another system or generated by a custom pipeline — you can index them directly without specifying an embedding function. `add_embedding_index` supports `Array` columns in addition to `String`, `Image`, `Video`, and `Audio` columns. When the indexed column is an `Array`, Pixeltable treats its values as precomputed embeddings and builds the vector index directly from them. An embedding function is **optional** for `Array` columns. If provided, it will be used only to convert query values into embeddings during similarity search. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} import numpy as np import pixeltable as pxt pxt.drop_table('indices_demo.precomputed', force=True) # Table with a precomputed embedding column precomputed = pxt.create_table( 'indices_demo.precomputed', {'text': pxt.String, 'embedding': pxt.Array[(384,), pxt.Float]}, ) # Insert rows with precomputed vectors rng = np.random.default_rng(42) precomputed.insert( [ { 'text': 'sample sentence', 'embedding': rng.random(384).tolist(), }, { 'text': 'another sentence', 'embedding': rng.random(384).tolist(), }, ] ) # Index the Array column directly — no embedding function needed precomputed.add_embedding_index('embedding', metric='cosine') ``` To run a similarity search, pass a raw vector to the `similarity` method: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} query_vector = rng.random(384) sim = precomputed.embedding.similarity(vector=query_vector) precomputed.order_by(sim, asc=False).limit(2).select( precomputed.text, sim ).collect() ```
  Error: similarity(vector=...): expected \`numpy.ndarray\`, or array \`Expr\`; got \`list\`
  \[0;31m---------------------------------------------------------------------------\[0m
  \[0;31mError\[0m                                     Traceback (most recent call last)
  Cell \[0;32mIn\[15], line 2\[0m
  \[1;32m      1\[0m query\_vector \[38;5;241m=\[39m rng\[38;5;241m.\[39mrandom(\[38;5;241m384\[39m)\[38;5;241m.\[39mtolist()
  \[0;32m----> 2\[0m sim \[38;5;241m=\[39m \[43mprecomputed\[49m\[38;5;241;43m.\[39;49m\[43membedding\[49m\[38;5;241;43m.\[39;49m\[43msimilarity\[49m\[43m(\[49m\[43mvector\[49m\[38;5;241;43m=\[39;49m\[43mquery\_vector\[49m\[43m)\[49m
  \[1;32m      3\[0m precomputed\[38;5;241m.\[39morder\_by(sim, asc\[38;5;241m=\[39m\[38;5;28;01mFalse\[39;00m)\[38;5;241m.\[39mlimit(\[38;5;241m2\[39m)\[38;5;241m.\[39mselect(precomputed\[38;5;241m.\[39mtext, sim)\[38;5;241m.\[39mcollect()

  File \[0;32m/Users/pierre/pixeltable/.venv/lib/python3.12/site-packages/pixeltable/exprs/column\_ref.py:313\[0m, in \[0;36mColumnRef.similarity\[0;34m(self, item, string, image, audio, video, document, vector, idx)\[0m
  \[1;32m    311\[0m \[38;5;28;01melse\[39;00m:
  \[1;32m    312\[0m     \[38;5;28;01mif\[39;00m \[38;5;129;01mnot\[39;00m \[38;5;28misinstance\[39m(vector, np\[38;5;241m.\[39mndarray):
  \[0;32m--> 313\[0m         \[38;5;28;01mraise\[39;00m excs\[38;5;241m.\[39mError(
  \[1;32m    314\[0m             \[38;5;124mf\[39m\[38;5;124m'\[39m\[38;5;124msimilarity(vector=...): expected \`numpy.ndarray\`, or array \`Expr\`; \[39m\[38;5;124m'\[39m
  \[1;32m    315\[0m             \[38;5;124mf\[39m\[38;5;124m'\[39m\[38;5;124mgot \`\[39m\[38;5;132;01m\{\[39;00m\[38;5;28mtype\[39m(vector)\[38;5;241m.\[39m\[38;5;18m\_\_name\_\_\[39m\[38;5;132;01m}\[39;00m\[38;5;124m\`\[39m\[38;5;124m'\[39m
  \[1;32m    316\[0m         )
  \[1;32m    317\[0m     \[38;5;28;01mif\[39;00m vector\[38;5;241m.\[39mndim \[38;5;241m!=\[39m \[38;5;241m1\[39m:
  \[1;32m    318\[0m         \[38;5;28;01mraise\[39;00m excs\[38;5;241m.\[39mError(\[38;5;124mf\[39m\[38;5;124m'\[39m\[38;5;124msimilarity(vector=...): expected 1-dimensional array; got shape \[39m\[38;5;132;01m\{\[39;00mvector\[38;5;241m.\[39mshape\[38;5;132;01m}\[39;00m\[38;5;124m'\[39m)

  \[0;31mError\[0m: similarity(vector=...): expected \`numpy.ndarray\`, or array \`Expr\`; got \`list\`
If you want to search by **text** (or another modality) rather than a raw vector, provide an embedding function that converts the query value into the same vector space. The function is only used at query time — it does not re-embed the stored vectors. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} precomputed.add_embedding_index( 'embedding', string_embed=my_text_embedding_fn, # converts query strings → vectors metric='cosine', ) sim = precomputed.embedding.similarity(string='search query') ``` The above examples show how to use any model in the Hugging Face `CLIP` or `sentence_transformer` model families, and essentially the same pattern can be used for any other embedding with built-in Pixeltable support, such as OpenAI embeddings. But what if you want to adapt a new model family that doesn’t have built-in support in Pixeltable? This can be done by writing a custom Pixeltable UDF. In the following example, we’ll write a simple UDF to use the [BERT](https://www.kaggle.com/models/tensorflow/bert/tensorFlow2/en-uncased-preprocess/3) model built on TensorFlow. First we install the necessary dependencies. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} %pip install -qU tensorflow tensorflow-hub tensorflow-text 'setuptools<82' ``` Text embedding UDFs must always take a string as input, and return a 1-dimensional numpy array of fixed dimension (512 in the case of `small_bert`, the variant we’ll be using). If we were writing an image embedding UDF, the `input` would have type `PIL.Image.Image` rather than `str`. The UDF is straightforward, loading the model and evaluating it against the input, with a minor data conversion on either side of the model invocation. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} import pixeltable as pxt import tensorflow as tf import tensorflow_hub as hub import tensorflow_text # Necessary to ensure BERT dependencies are loaded @pxt.udf def bert(input: str) -> pxt.Array[(512,), pxt.Float]: """Computes text embeddings using the small_bert model.""" preprocessor = hub.load( 'https://tfhub.dev/tensorflow/bert_en_uncased_preprocess/3' ) bert_model = hub.load( 'https://tfhub.dev/tensorflow/small_bert/bert_en_uncased_L-4_H-512_A-8/2' ) tensor = tf.constant([input]) # Convert the string to a tensor result = bert_model(preprocessor(tensor))['pooled_output'] return result.numpy()[0, :] ``` ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} txts.add_embedding_index('text', idx_name='bert_idx', embedding=bert) ``` Here’s the output of our sample query run against `bert_idx`. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} sim = txts.text.similarity('cubism', idx='bert_idx') res = ( txts.order_by(sim, asc=False) .limit(2) .select(txts.text, sim) .collect() ) res ``` Our example UDF is very simple, but it would perform poorly in a production setting. To make our UDF production-ready, we’d want to do two things: * Cache the model: the current version calls `hub.load()` on every UDF invocation. In a real application, we’d want to instantiate the model just once, then reuse it on subsequent UDF calls. * Batch our inputs: we’d use Pixeltable’s batching capability to ensure we’re making efficient use of the model. Batched UDFs are described in depth in the [User-Defined Functions](/platform/udfs-in-pixeltable) how-to guide. You might have noticed that the updates to `bert_idx` seem sluggish; that’s why! ## Deleting an index To delete an index, call [`Table.drop_embedding_index()`](/sdk/latest/table#method-drop_embedding_index): * specify the `idx_name` parameter if you have multiple indices * otherwise the `column_name` parameter is sufficient Given that we have several embedding indices, we’ll specify which index to drop: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} txts.drop_embedding_index(idx_name='e5_idx') ``` ## Ship it as an app This notebook used `add_embedding_index`. On a `TableModel`, indexes live in `__indexes__`: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} import pixeltable as pxt from pixeltable.functions.huggingface import clip TableModel = pxt.model_base() class ImgTbl(TableModel, name='img_tbl'): img: pxt.Image __indexes__ = [ pxt.EmbeddingIndex(img, embedding=clip.using(model_id='openai/clip-vit-base-patch32'), name='img_idx'), ] ``` ```bash theme={"theme":{"light":"light-plus","dark":"dark-plus"}} pxt schema update app.py indices_demo ``` # External Files Source: https://docs.pixeltable.com/platform/external-files Open in Kaggle  Open in Colab  Download Notebook This documentation page is also available as an interactive notebook. You can launch the notebook in Kaggle or Colab, or download it for use with an IDE or local Jupyter installation, by clicking one of the above links. In Pixeltable, all media data (videos, images, audio) resides in external files, and Pixeltable stores references to those. The files can be local or remote (e.g., in S3). For the latter, Pixeltable automatically caches the files locally on access. This notebook uses `pxt.create_table()` so you can run cells without a project. An app puts the same columns on a `TableModel` in `app.py` and creates the tables with `pxt schema update`. When interacting with media data via Pixeltable, either through queries or UDFs, the user sees the following Python types: * `pxt.Image` (Python value: `PIL.Image.Image`) * `pxt.Video` (Python value: a local path string) * `pxt.Audio` (Python value: a local path string) Let’s create a table and load some data to see what that looks like: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} %pip install -qU pixeltable ``` ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} import pixeltable as pxt import random import shutil import tempfile # First drop the `external_data` directory if it exists, to ensure # a clean environment for the demo pxt.drop_dir('external_data', force=True) pxt.create_dir('external_data') ```
  Connected to Pixeltable database at: postgresql+psycopg://postgres:@/pixeltable?host=/Users/asiegel/.pixeltable/pgdata
  Created directory \`external\_data\`.
  \
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} v = pxt.create_table('external_data/videos', {'video': pxt.Video}) prefix = 's3://multimedia-commons/' paths = [ 'data/videos/mp4/ffe/ffb/ffeffbef41bbc269810b2a1a888de.mp4', 'data/videos/mp4/ffe/feb/ffefebb41485539f964760e6115fbc44.mp4', 'data/videos/mp4/ffe/f73/ffef7384d698b5f70d411c696247169.mp4', ] v.insert({'video': prefix + p} for p in paths) ```
  Created table \`videos\`.
  Computing cells:   0%|                                                    | 0/6 \[00:00\

We just inserted 3 rows with video files residing in S3. When we now
query these, we are presented with their locally cached counterparts.

(Note: we don’t simply display the output of `collect()` here, because
that is formatted as an HTML table with a media player and so would
obscure the file path.)

```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}}
rows = list(v.select(v.video).collect())
rows[0]
```

  \{'video': '/Users/asiegel/.pixeltable/file\_cache/682f022a704d4459adb2f29f7fe9577c\_0\_1fcfcb221263cff76a2853250fbbb2e90375dd495454c0007bc6ff4430c9a4a7.mp4'}
Let’s make a local copy of the first file and insert that separately. First, the copy: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} local_path = tempfile.mktemp(suffix='.mp4') shutil.copyfile(rows[0]['video'], local_path) local_path ```
  '/var/folders/hb/qd0dztsj43j\_mdb6hbl1gzyc0000gn/T/tmp1jo4a7ca.mp4'
Now the insert: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} v.insert([{'video': local_path}]) ```
  Computing cells:   0%|                                                    | 0/2 \[00:00\

When we query this again, we see that local paths are preserved:

```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}}
rows = list(v.select(v.video).collect())
rows
```

  \[\{'video': '/Users/asiegel/.pixeltable/file\_cache/682f022a704d4459adb2f29f7fe9577c\_0\_1fcfcb221263cff76a2853250fbbb2e90375dd495454c0007bc6ff4430c9a4a7.mp4'},
   \{'video': '/Users/asiegel/.pixeltable/file\_cache/682f022a704d4459adb2f29f7fe9577c\_0\_fc11428b32768ae782193a57ebcbad706f45bbd9fa13354471e0bcd798fee3ea.mp4'},
   \{'video': '/Users/asiegel/.pixeltable/file\_cache/682f022a704d4459adb2f29f7fe9577c\_0\_b9fb0d9411bc9cd183a36866911baa7a8834f22f665bce47608566b38485c16a.mp4'},
   \{'video': '/var/folders/hb/qd0dztsj43j\_mdb6hbl1gzyc0000gn/T/tmp1jo4a7ca.mp4'}]
UDFs also see local paths: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf def f(v: pxt.Video) -> int: print(f'{type(v)}: {v}') return 1 ``` ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} v.select(f(v.video)).show() ```
  \: /Users/asiegel/.pixeltable/file\_cache/682f022a704d4459adb2f29f7fe9577c\_0\_1fcfcb221263cff76a2853250fbbb2e90375dd495454c0007bc6ff4430c9a4a7.mp4
  \: /Users/asiegel/.pixeltable/file\_cache/682f022a704d4459adb2f29f7fe9577c\_0\_fc11428b32768ae782193a57ebcbad706f45bbd9fa13354471e0bcd798fee3ea.mp4
  \: /Users/asiegel/.pixeltable/file\_cache/682f022a704d4459adb2f29f7fe9577c\_0\_b9fb0d9411bc9cd183a36866911baa7a8834f22f665bce47608566b38485c16a.mp4
  \: /var/folders/hb/qd0dztsj43j\_mdb6hbl1gzyc0000gn/T/tmp1jo4a7ca.mp4
## Dealing with errors When interacting with media data in Pixeltable, the user can assume that the underlying files exist, are local and are valid for their respective data type. In other words, the user doesn’t need to consider error conditions. To that end, Pixeltable validates media data on ingest. The default behavior is to reject invalid media files: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} v.insert([{'video': prefix + 'bad_path.mp4'}]) ```
  Computing cells:   0%|                                                    | 0/2 \[00:01\ 1\[0m \[43mv\[49m\[38;5;241;43m.\[39;49m\[43minsert\[49m\[43m(\[49m\[43mvideo\[49m\[38;5;241;43m=\[39;49m\[43mprefix\[49m\[43m \[49m\[38;5;241;43m+\[39;49m\[43m \[49m\[38;5;124;43m'\[39;49m\[38;5;124;43mbad\_path.mp4\[39;49m\[38;5;124;43m'\[39;49m\[43m)\[49m

  File \[0;32m\~/Dropbox/workspace/pixeltable/pixeltable/pixeltable/catalog/insertable\_table.py:125\[0m, in \[0;36mInsertableTable.insert\[0;34m(self, rows, print\_stats, on\_error, \*\*kwargs)\[0m
  \[1;32m    123\[0m         \[38;5;28;01mraise\[39;00m excs\[38;5;241m.\[39mError(\[38;5;124m'\[39m\[38;5;124mrows must be a list of dictionaries\[39m\[38;5;124m'\[39m)
  \[1;32m    124\[0m \[38;5;28mself\[39m\[38;5;241m.\[39m\_validate\_input\_rows(rows)
  \[0;32m--> 125\[0m status \[38;5;241m=\[39m \[38;5;28;43mself\[39;49m\[38;5;241;43m.\[39;49m\[43m\_tbl\_version\[49m\[38;5;241;43m.\[39;49m\[43minsert\[49m\[43m(\[49m\[43mrows\[49m\[43m,\[49m\[43m \[49m\[38;5;28;43;01mNone\[39;49;00m\[43m,\[49m\[43m \[49m\[43mprint\_stats\[49m\[38;5;241;43m=\[39;49m\[43mprint\_stats\[49m\[43m,\[49m\[43m \[49m\[43mfail\_on\_exception\[49m\[38;5;241;43m=\[39;49m\[43mfail\_on\_exception\[49m\[43m)\[49m
  \[1;32m    127\[0m \[38;5;28;01mif\[39;00m status\[38;5;241m.\[39mnum\_excs \[38;5;241m==\[39m \[38;5;241m0\[39m:
  \[1;32m    128\[0m     cols\_with\_excs\_str \[38;5;241m=\[39m \[38;5;124m'\[39m\[38;5;124m'\[39m

  File \[0;32m\~/Dropbox/workspace/pixeltable/pixeltable/pixeltable/catalog/table\_version.py:723\[0m, in \[0;36mTableVersion.insert\[0;34m(self, rows, df, conn, print\_stats, fail\_on\_exception)\[0m
  \[1;32m    721\[0m \[38;5;28;01mif\[39;00m conn \[38;5;129;01mis\[39;00m \[38;5;28;01mNone\[39;00m:
  \[1;32m    722\[0m     \[38;5;28;01mwith\[39;00m Env\[38;5;241m.\[39mget()\[38;5;241m.\[39mengine\[38;5;241m.\[39mbegin() \[38;5;28;01mas\[39;00m conn:
  \[0;32m--> 723\[0m         \[38;5;28;01mreturn\[39;00m \[38;5;28;43mself\[39;49m\[38;5;241;43m.\[39;49m\[43m\_insert\[49m\[43m(\[49m
  \[1;32m    724\[0m \[43m            \[49m\[43mplan\[49m\[43m,\[49m\[43m \[49m\[43mconn\[49m\[43m,\[49m\[43m \[49m\[43mtime\[49m\[38;5;241;43m.\[39;49m\[43mtime\[49m\[43m(\[49m\[43m)\[49m\[43m,\[49m\[43m \[49m\[43mprint\_stats\[49m\[38;5;241;43m=\[39;49m\[43mprint\_stats\[49m\[43m,\[49m\[43m \[49m\[43mrowids\[49m\[38;5;241;43m=\[39;49m\[43mrowids\[49m\[43m(\[49m\[43m)\[49m\[43m,\[49m\[43m \[49m\[43mabort\_on\_exc\[49m\[38;5;241;43m=\[39;49m\[43mfail\_on\_exception\[49m\[43m)\[49m
  \[1;32m    725\[0m \[38;5;28;01melse\[39;00m:
  \[1;32m    726\[0m     \[38;5;28;01mreturn\[39;00m \[38;5;28mself\[39m\[38;5;241m.\[39m\_insert(
  \[1;32m    727\[0m         plan, conn, time\[38;5;241m.\[39mtime(), print\_stats\[38;5;241m=\[39mprint\_stats, rowids\[38;5;241m=\[39mrowids(), abort\_on\_exc\[38;5;241m=\[39mfail\_on\_exception)

  File \[0;32m\~/Dropbox/workspace/pixeltable/pixeltable/pixeltable/catalog/table\_version.py:737\[0m, in \[0;36mTableVersion.\_insert\[0;34m(self, exec\_plan, conn, timestamp, rowids, print\_stats, abort\_on\_exc)\[0m
  \[1;32m    735\[0m \[38;5;28mself\[39m\[38;5;241m.\[39mversion \[38;5;241m+\[39m\[38;5;241m=\[39m \[38;5;241m1\[39m
  \[1;32m    736\[0m result \[38;5;241m=\[39m UpdateStatus()
  \[0;32m--> 737\[0m num\_rows, num\_excs, cols\_with\_excs \[38;5;241m=\[39m \[38;5;28;43mself\[39;49m\[38;5;241;43m.\[39;49m\[43mstore\_tbl\[49m\[38;5;241;43m.\[39;49m\[43minsert\_rows\[49m\[43m(\[49m
  \[1;32m    738\[0m \[43m    \[49m\[43mexec\_plan\[49m\[43m,\[49m\[43m \[49m\[43mconn\[49m\[43m,\[49m\[43m \[49m\[43mv\_min\[49m\[38;5;241;43m=\[39;49m\[38;5;28;43mself\[39;49m\[38;5;241;43m.\[39;49m\[43mversion\[49m\[43m,\[49m\[43m \[49m\[43mrowids\[49m\[38;5;241;43m=\[39;49m\[43mrowids\[49m\[43m,\[49m\[43m \[49m\[43mabort\_on\_exc\[49m\[38;5;241;43m=\[39;49m\[43mabort\_on\_exc\[49m\[43m)\[49m
  \[1;32m    739\[0m result\[38;5;241m.\[39mnum\_rows \[38;5;241m=\[39m num\_rows
  \[1;32m    740\[0m result\[38;5;241m.\[39mnum\_excs \[38;5;241m=\[39m num\_excs

  File \[0;32m\~/Dropbox/workspace/pixeltable/pixeltable/pixeltable/store.py:323\[0m, in \[0;36mStoreBase.insert\_rows\[0;34m(self, exec\_plan, conn, v\_min, show\_progress, rowids, abort\_on\_exc)\[0m
  \[1;32m    321\[0m \[38;5;28;01mtry\[39;00m:
  \[1;32m    322\[0m     exec\_plan\[38;5;241m.\[39mopen()
  \[0;32m--> 323\[0m     \[38;5;28;01mfor\[39;00m row\_batch \[38;5;129;01min\[39;00m exec\_plan:
  \[1;32m    324\[0m         num\_rows \[38;5;241m+\[39m\[38;5;241m=\[39m \[38;5;28mlen\[39m(row\_batch)
  \[1;32m    325\[0m         \[38;5;28;01mfor\[39;00m batch\_start\_idx \[38;5;129;01min\[39;00m \[38;5;28mrange\[39m(\[38;5;241m0\[39m, \[38;5;28mlen\[39m(row\_batch), \[38;5;28mself\[39m\[38;5;241m.\[39m\_\_INSERT\_BATCH\_SIZE):
  \[1;32m    326\[0m             \[38;5;66;03m# compute batch of rows and convert them into table rows\[39;00m

  File \[0;32m\~/Dropbox/workspace/pixeltable/pixeltable/pixeltable/exec/expr\_eval\_node.py:45\[0m, in \[0;36mExprEvalNode.\_\_next\_\_\[0;34m(self)\[0m
  \[1;32m     44\[0m \[38;5;28;01mdef\[39;00m \[38;5;21m\_\_next\_\_\[39m(\[38;5;28mself\[39m) \[38;5;241m-\[39m\[38;5;241m>\[39m DataRowBatch:
  \[0;32m---> 45\[0m     input\_batch \[38;5;241m=\[39m \[38;5;28;43mnext\[39;49m\[43m(\[49m\[38;5;28;43mself\[39;49m\[38;5;241;43m.\[39;49m\[43minput\[49m\[43m)\[49m
  \[1;32m     46\[0m     \[38;5;66;03m# compute target exprs\[39;00m
  \[1;32m     47\[0m     \[38;5;28;01mfor\[39;00m cohort \[38;5;129;01min\[39;00m \[38;5;28mself\[39m\[38;5;241m.\[39mcohorts:

  File \[0;32m\~/Dropbox/workspace/pixeltable/pixeltable/pixeltable/exec/cache\_prefetch\_node.py:71\[0m, in \[0;36mCachePrefetchNode.\_\_next\_\_\[0;34m(self)\[0m
  \[1;32m     68\[0m     futures\[executor\[38;5;241m.\[39msubmit(\[38;5;28mself\[39m\[38;5;241m.\[39m\_fetch\_url, row, info\[38;5;241m.\[39mslot\_idx)] \[38;5;241m=\[39m (row, info)
  \[1;32m     69\[0m \[38;5;28;01mfor\[39;00m future \[38;5;129;01min\[39;00m concurrent\[38;5;241m.\[39mfutures\[38;5;241m.\[39mas\_completed(futures):
  \[1;32m     70\[0m     \[38;5;66;03m# TODO:  does this need to deal with recoverable errors (such as retry after throttling)?\[39;00m
  \[0;32m---> 71\[0m     tmp\_path \[38;5;241m=\[39m \[43mfuture\[49m\[38;5;241;43m.\[39;49m\[43mresult\[49m\[43m(\[49m\[43m)\[49m
  \[1;32m     72\[0m     \[38;5;28;01mif\[39;00m tmp\_path \[38;5;129;01mis\[39;00m \[38;5;28;01mNone\[39;00m:
  \[1;32m     73\[0m         \[38;5;28;01mcontinue\[39;00m

  File \[0;32m/opt/miniconda3/envs/pxt/lib/python3.9/concurrent/futures/\_base.py:439\[0m, in \[0;36mFuture.result\[0;34m(self, timeout)\[0m
  \[1;32m    437\[0m     \[38;5;28;01mraise\[39;00m CancelledError()
  \[1;32m    438\[0m \[38;5;28;01melif\[39;00m \[38;5;28mself\[39m\[38;5;241m.\[39m\_state \[38;5;241m==\[39m FINISHED:
  \[0;32m--> 439\[0m     \[38;5;28;01mreturn\[39;00m \[38;5;28;43mself\[39;49m\[38;5;241;43m.\[39;49m\[43m\_\_get\_result\[49m\[43m(\[49m\[43m)\[49m
  \[1;32m    441\[0m \[38;5;28mself\[39m\[38;5;241m.\[39m\_condition\[38;5;241m.\[39mwait(timeout)
  \[1;32m    443\[0m \[38;5;28;01mif\[39;00m \[38;5;28mself\[39m\[38;5;241m.\[39m\_state \[38;5;129;01min\[39;00m \[CANCELLED, CANCELLED\_AND\_NOTIFIED]:

  File \[0;32m/opt/miniconda3/envs/pxt/lib/python3.9/concurrent/futures/\_base.py:391\[0m, in \[0;36mFuture.\_\_get\_result\[0;34m(self)\[0m
  \[1;32m    389\[0m \[38;5;28;01mif\[39;00m \[38;5;28mself\[39m\[38;5;241m.\[39m\_exception:
  \[1;32m    390\[0m     \[38;5;28;01mtry\[39;00m:
  \[0;32m--> 391\[0m         \[38;5;28;01mraise\[39;00m \[38;5;28mself\[39m\[38;5;241m.\[39m\_exception
  \[1;32m    392\[0m     \[38;5;28;01mfinally\[39;00m:
  \[1;32m    393\[0m         \[38;5;66;03m# Break a reference cycle with the exception in self.\_exception\[39;00m
  \[1;32m    394\[0m         \[38;5;28mself\[39m \[38;5;241m=\[39m \[38;5;28;01mNone\[39;00m

  File \[0;32m/opt/miniconda3/envs/pxt/lib/python3.9/concurrent/futures/thread.py:58\[0m, in \[0;36m\_WorkItem.run\[0;34m(self)\[0m
  \[1;32m     55\[0m     \[38;5;28;01mreturn\[39;00m
  \[1;32m     57\[0m \[38;5;28;01mtry\[39;00m:
  \[0;32m---> 58\[0m     result \[38;5;241m=\[39m \[38;5;28;43mself\[39;49m\[38;5;241;43m.\[39;49m\[43mfn\[49m\[43m(\[49m\[38;5;241;43m*\[39;49m\[38;5;28;43mself\[39;49m\[38;5;241;43m.\[39;49m\[43margs\[49m\[43m,\[49m\[43m \[49m\[38;5;241;43m*\[39;49m\[38;5;241;43m\*\[39;49m\[38;5;28;43mself\[39;49m\[38;5;241;43m.\[39;49m\[43mkwargs\[49m\[43m)\[49m
  \[1;32m     59\[0m \[38;5;28;01mexcept\[39;00m \[38;5;167;01mBaseException\[39;00m \[38;5;28;01mas\[39;00m exc:
  \[1;32m     60\[0m     \[38;5;28mself\[39m\[38;5;241m.\[39mfuture\[38;5;241m.\[39mset\_exception(exc)

  File \[0;32m\~/Dropbox/workspace/pixeltable/pixeltable/pixeltable/exec/cache\_prefetch\_node.py:115\[0m, in \[0;36mCachePrefetchNode.\_fetch\_url\[0;34m(self, row, slot\_idx)\[0m
  \[1;32m    113\[0m     \[38;5;28mself\[39m\[38;5;241m.\[39mrow\_builder\[38;5;241m.\[39mset\_exc(row, slot\_idx, exc)
  \[1;32m    114\[0m     \[38;5;28;01mif\[39;00m \[38;5;129;01mnot\[39;00m \[38;5;28mself\[39m\[38;5;241m.\[39mctx\[38;5;241m.\[39mignore\_errors:
  \[0;32m--> 115\[0m         \[38;5;28;01mraise\[39;00m exc \[38;5;28;01mfrom\[39;00m \[38;5;28;01mNone\[39;00m  \[38;5;66;03m# suppress original exception\[39;00m
  \[1;32m    116\[0m \[38;5;28;01mreturn\[39;00m \[38;5;28;01mNone\[39;00m

  \[0;31mError\[0m: Failed to download s3://multimedia-commons/bad\_path.mp4: An error occurred (404) when calling the HeadObject operation: Not Found
The same happens for corrupted files: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # create invalid .mp4 with tempfile.NamedTemporaryFile( mode='wb', suffix='.mp4', delete=False ) as temp_file: temp_file.write(random.randbytes(1024)) corrupted_path = temp_file.name v.insert([{'video': corrupted_path}]) ```
  Computing cells: 100%|██████████████████████████████████████████| 2/2 \[00:00\<00:00, 1084.64 cells/s]
  Error: Not a valid video: /var/folders/hb/qd0dztsj43j\_mdb6hbl1gzyc0000gn/T/tmp3djgfyjp.mp4
  \[0;31m---------------------------------------------------------------------------\[0m
  \[0;31mError\[0m                                     Traceback (most recent call last)
  Cell \[0;32mIn\[10], line 6\[0m
  \[1;32m      3\[0m     temp\_file\[38;5;241m.\[39mwrite(random\[38;5;241m.\[39mrandbytes(\[38;5;241m1024\[39m))
  \[1;32m      4\[0m     corrupted\_path \[38;5;241m=\[39m temp\_file\[38;5;241m.\[39mname
  \[0;32m----> 6\[0m \[43mv\[49m\[38;5;241;43m.\[39;49m\[43minsert\[49m\[43m(\[49m\[43mvideo\[49m\[38;5;241;43m=\[39;49m\[43mcorrupted\_path\[49m\[43m)\[49m

  File \[0;32m\~/Dropbox/workspace/pixeltable/pixeltable/pixeltable/catalog/insertable\_table.py:125\[0m, in \[0;36mInsertableTable.insert\[0;34m(self, rows, print\_stats, on\_error, \*\*kwargs)\[0m
  \[1;32m    123\[0m         \[38;5;28;01mraise\[39;00m excs\[38;5;241m.\[39mError(\[38;5;124m'\[39m\[38;5;124mrows must be a list of dictionaries\[39m\[38;5;124m'\[39m)
  \[1;32m    124\[0m \[38;5;28mself\[39m\[38;5;241m.\[39m\_validate\_input\_rows(rows)
  \[0;32m--> 125\[0m status \[38;5;241m=\[39m \[38;5;28;43mself\[39;49m\[38;5;241;43m.\[39;49m\[43m\_tbl\_version\[49m\[38;5;241;43m.\[39;49m\[43minsert\[49m\[43m(\[49m\[43mrows\[49m\[43m,\[49m\[43m \[49m\[38;5;28;43;01mNone\[39;49;00m\[43m,\[49m\[43m \[49m\[43mprint\_stats\[49m\[38;5;241;43m=\[39;49m\[43mprint\_stats\[49m\[43m,\[49m\[43m \[49m\[43mfail\_on\_exception\[49m\[38;5;241;43m=\[39;49m\[43mfail\_on\_exception\[49m\[43m)\[49m
  \[1;32m    127\[0m \[38;5;28;01mif\[39;00m status\[38;5;241m.\[39mnum\_excs \[38;5;241m==\[39m \[38;5;241m0\[39m:
  \[1;32m    128\[0m     cols\_with\_excs\_str \[38;5;241m=\[39m \[38;5;124m'\[39m\[38;5;124m'\[39m

  File \[0;32m\~/Dropbox/workspace/pixeltable/pixeltable/pixeltable/catalog/table\_version.py:723\[0m, in \[0;36mTableVersion.insert\[0;34m(self, rows, df, conn, print\_stats, fail\_on\_exception)\[0m
  \[1;32m    721\[0m \[38;5;28;01mif\[39;00m conn \[38;5;129;01mis\[39;00m \[38;5;28;01mNone\[39;00m:
  \[1;32m    722\[0m     \[38;5;28;01mwith\[39;00m Env\[38;5;241m.\[39mget()\[38;5;241m.\[39mengine\[38;5;241m.\[39mbegin() \[38;5;28;01mas\[39;00m conn:
  \[0;32m--> 723\[0m         \[38;5;28;01mreturn\[39;00m \[38;5;28;43mself\[39;49m\[38;5;241;43m.\[39;49m\[43m\_insert\[49m\[43m(\[49m
  \[1;32m    724\[0m \[43m            \[49m\[43mplan\[49m\[43m,\[49m\[43m \[49m\[43mconn\[49m\[43m,\[49m\[43m \[49m\[43mtime\[49m\[38;5;241;43m.\[39;49m\[43mtime\[49m\[43m(\[49m\[43m)\[49m\[43m,\[49m\[43m \[49m\[43mprint\_stats\[49m\[38;5;241;43m=\[39;49m\[43mprint\_stats\[49m\[43m,\[49m\[43m \[49m\[43mrowids\[49m\[38;5;241;43m=\[39;49m\[43mrowids\[49m\[43m(\[49m\[43m)\[49m\[43m,\[49m\[43m \[49m\[43mabort\_on\_exc\[49m\[38;5;241;43m=\[39;49m\[43mfail\_on\_exception\[49m\[43m)\[49m
  \[1;32m    725\[0m \[38;5;28;01melse\[39;00m:
  \[1;32m    726\[0m     \[38;5;28;01mreturn\[39;00m \[38;5;28mself\[39m\[38;5;241m.\[39m\_insert(
  \[1;32m    727\[0m         plan, conn, time\[38;5;241m.\[39mtime(), print\_stats\[38;5;241m=\[39mprint\_stats, rowids\[38;5;241m=\[39mrowids(), abort\_on\_exc\[38;5;241m=\[39mfail\_on\_exception)

  File \[0;32m\~/Dropbox/workspace/pixeltable/pixeltable/pixeltable/catalog/table\_version.py:737\[0m, in \[0;36mTableVersion.\_insert\[0;34m(self, exec\_plan, conn, timestamp, rowids, print\_stats, abort\_on\_exc)\[0m
  \[1;32m    735\[0m \[38;5;28mself\[39m\[38;5;241m.\[39mversion \[38;5;241m+\[39m\[38;5;241m=\[39m \[38;5;241m1\[39m
  \[1;32m    736\[0m result \[38;5;241m=\[39m UpdateStatus()
  \[0;32m--> 737\[0m num\_rows, num\_excs, cols\_with\_excs \[38;5;241m=\[39m \[38;5;28;43mself\[39;49m\[38;5;241;43m.\[39;49m\[43mstore\_tbl\[49m\[38;5;241;43m.\[39;49m\[43minsert\_rows\[49m\[43m(\[49m
  \[1;32m    738\[0m \[43m    \[49m\[43mexec\_plan\[49m\[43m,\[49m\[43m \[49m\[43mconn\[49m\[43m,\[49m\[43m \[49m\[43mv\_min\[49m\[38;5;241;43m=\[39;49m\[38;5;28;43mself\[39;49m\[38;5;241;43m.\[39;49m\[43mversion\[49m\[43m,\[49m\[43m \[49m\[43mrowids\[49m\[38;5;241;43m=\[39;49m\[43mrowids\[49m\[43m,\[49m\[43m \[49m\[43mabort\_on\_exc\[49m\[38;5;241;43m=\[39;49m\[43mabort\_on\_exc\[49m\[43m)\[49m
  \[1;32m    739\[0m result\[38;5;241m.\[39mnum\_rows \[38;5;241m=\[39m num\_rows
  \[1;32m    740\[0m result\[38;5;241m.\[39mnum\_excs \[38;5;241m=\[39m num\_excs

  File \[0;32m\~/Dropbox/workspace/pixeltable/pixeltable/pixeltable/store.py:334\[0m, in \[0;36mStoreBase.insert\_rows\[0;34m(self, exec\_plan, conn, v\_min, show\_progress, rowids, abort\_on\_exc)\[0m
  \[1;32m    332\[0m \[38;5;28;01mif\[39;00m abort\_on\_exc \[38;5;129;01mand\[39;00m row\[38;5;241m.\[39mhas\_exc():
  \[1;32m    333\[0m     exc \[38;5;241m=\[39m row\[38;5;241m.\[39mget\_first\_exc()
  \[0;32m--> 334\[0m     \[38;5;28;01mraise\[39;00m exc
  \[1;32m    336\[0m rowid \[38;5;241m=\[39m (\[38;5;28mnext\[39m(rowids),) \[38;5;28;01mif\[39;00m rowids \[38;5;129;01mis\[39;00m \[38;5;129;01mnot\[39;00m \[38;5;28;01mNone\[39;00m \[38;5;28;01melse\[39;00m row\[38;5;241m.\[39mpk\[:\[38;5;241m-\[39m\[38;5;241m1\[39m]
  \[1;32m    337\[0m pk \[38;5;241m=\[39m rowid \[38;5;241m+\[39m (v\_min,)

  File \[0;32m\~/Dropbox/workspace/pixeltable/pixeltable/pixeltable/exprs/column\_ref.py:159\[0m, in \[0;36mColumnRef.eval\[0;34m(self, data\_row, row\_builder)\[0m
  \[1;32m    156\[0m     \[38;5;28;01mreturn\[39;00m
  \[1;32m    158\[0m \[38;5;28;01mtry\[39;00m:
  \[0;32m--> 159\[0m     \[38;5;28;43mself\[39;49m\[38;5;241;43m.\[39;49m\[43mcol\[49m\[38;5;241;43m.\[39;49m\[43mcol\_type\[49m\[38;5;241;43m.\[39;49m\[43mvalidate\_media\[49m\[43m(\[49m\[43mdata\_row\[49m\[38;5;241;43m.\[39;49m\[43mfile\_paths\[49m\[43m\[\[49m\[43munvalidated\_slot\_idx\[49m\[43m]\[49m\[43m)\[49m
  \[1;32m    160\[0m     \[38;5;66;03m# access the value only after successful validation\[39;00m
  \[1;32m    161\[0m     val \[38;5;241m=\[39m data\_row\[unvalidated\_slot\_idx]

  File \[0;32m\~/Dropbox/workspace/pixeltable/pixeltable/pixeltable/type\_system.py:906\[0m, in \[0;36mVideoType.validate\_media\[0;34m(self, val)\[0m
  \[1;32m    904\[0m             \[38;5;28;01mraise\[39;00m excs\[38;5;241m.\[39mError(\[38;5;124mf\[39m\[38;5;124m'\[39m\[38;5;124mNot a valid video: \[39m\[38;5;132;01m\{\[39;00mval\[38;5;132;01m}\[39;00m\[38;5;124m'\[39m)
  \[1;32m    905\[0m \[38;5;28;01mexcept\[39;00m av\[38;5;241m.\[39mAVError:
  \[0;32m--> 906\[0m     \[38;5;28;01mraise\[39;00m excs\[38;5;241m.\[39mError(\[38;5;124mf\[39m\[38;5;124m'\[39m\[38;5;124mNot a valid video: \[39m\[38;5;132;01m\{\[39;00mval\[38;5;132;01m}\[39;00m\[38;5;124m'\[39m) \[38;5;28;01mfrom\[39;00m \[38;5;28;01mNone\[39;00m

  \[0;31mError\[0m: Not a valid video: /var/folders/hb/qd0dztsj43j\_mdb6hbl1gzyc0000gn/T/tmp3djgfyjp.mp4
Alternatively, Pixeltable can also be instructed to record error conditions and proceed with the ingest, via the `on_error` flag (default: `'abort'`): ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} v.insert( [{'video': prefix + 'bad_path.mp4'}, {'video': corrupted_path}], on_error='ignore', ) ```
  Computing cells: 100%|████████████████████████████████████████████| 4/4 \[00:00\<00:00, 20.98 cells/s]
  Inserting rows into \`videos\`: 2 rows \[00:00, 671.63 rows/s]
  Computing cells: 100%|████████████████████████████████████████████| 4/4 \[00:00\<00:00, 20.13 cells/s]
  Inserted 2 rows with 4 errors across 2 columns (videos.video, videos.None).
  UpdateStatus(num\_rows=2, num\_computed\_values=4, num\_excs=4, updated\_cols=\[], cols\_with\_excs=\['videos.video', 'videos.None'])
Every media column has properties `errortype` and `errormsg` (both containing `string` data) that indicate whether the column value is valid. Invalid values show up as `None` and have non-null `errortype`/`errormsg`: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} v.select(v.video == None, v.video.errortype, v.video.errormsg).collect() ```
Errors can now be inspected (and corrected) after the ingest: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} v.where(v.video.errortype != None).select(v.video.errormsg).collect() ```
## Accessing the original file paths In some cases, it will be necessary to access file paths (not, say, the `PIL.Image.Image`), and Pixeltable provides the column properties `fileurl` and `localpath` for that purpose: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} v.select(v.video.fileurl, v.video.localpath).collect() ```
Note that for local media files, the `fileurl` property still returns a parsable URL. ## Ship it as an app Media columns on a `TableModel` are the same types as in this notebook: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} import pixeltable as pxt TableModel = pxt.model_base() class Videos(TableModel, name='videos'): video: pxt.Video ``` ```bash theme={"theme":{"light":"light-plus","dark":"dark-plus"}} pxt schema update app.py external_data ``` # Iterators Source: https://docs.pixeltable.com/platform/iterators Learn about iterators for processing documents, videos, audio, and images ## What are iterators? Iterators in Pixeltable are specialized tools for processing and transforming media content. They efficiently break down large files into manageable chunks, enabling analysis at different granularities. Iterators work seamlessly with views to create virtual derived tables without duplicating storage. In Pixeltable, iterators: * Process media files incrementally to manage memory efficiently * Transform single records into multiple output records * Support various media types including documents, videos, images, and audio * Integrate with the view system for automated processing pipelines * Provide configurable parameters for fine-tuning output Iterators are particularly useful when: * Working with large media files that can't be processed at once * Building retrieval systems that require chunked content * Creating analysis pipelines for multimedia data * Implementing feature extraction workflows In an app file, set `iterator=` on the view's `TableModel`. In a notebook, pass `iterator=` to `pxt.create_view()`. The examples below use `sentence` and `token_limit`. Install `spacy` and `tiktoken`, then download spaCy's default English model: ```bash theme={"theme":{"light":"light-plus","dark":"dark-plus"}} pip install spacy tiktoken python -m spacy download en_core_web_sm ``` ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} import pixeltable as pxt import pixeltable.functions as pxtf TableModel = pxt.model_base() class Documents(TableModel, name='docs'): document: pxt.Document class Chunks( TableModel, name='chunks', base=Documents, iterator=pxtf.document.document_splitter( Documents.document, separators='sentence,token_limit', limit=300 ), ): pass ``` In a notebook: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} import pixeltable as pxt from pixeltable.functions.document import document_splitter chunks = pxt.create_view( 'docs/chunks', documents_table, iterator=document_splitter( document=documents_table.document, separators='sentence,token_limit', limit=300 ) ) ``` ## Core concepts Split documents into chunks by headings, sentences, or token limits Extract frames at specified intervals or counts Divide images into overlapping or non-overlapping tiles Split audio files into time-based chunks with configurable overlap Iterators are powerful tools for processing large media files. They work seamlessly with Pixeltable's computed columns and versioning system. ## Available iterators ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} from pixeltable.functions.document import document_splitter # Create view with document chunks chunks_view = pxt.create_view( 'docs/chunks', docs_table, iterator=document_splitter( document=docs_table.document, separators='sentence,token_limit', limit=500, metadata='title,heading' ) ) ``` ### Parameters * `separators`: Choose from 'heading', 'sentence', 'token\_limit', 'char\_limit', 'page' * `limit`: Maximum tokens/characters per chunk * `metadata`: Optional fields like 'title', 'heading', 'sourceline', 'page', 'bounding\_box' * `overlap`: Optional overlap between chunks ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} from pixeltable.functions.video import frame_iterator # Extract frames at 1 FPS frames_view = pxt.create_view( 'videos/frames', videos_table, iterator=frame_iterator( video=videos_table.video, fps=1.0 ) ) # Extract exact number of frames (evenly spaced) frames_view = pxt.create_view( 'videos/sampled', videos_table, iterator=frame_iterator( video=videos_table.video, num_frames=10 # Extract 10 evenly-spaced frames ) ) # Extract only keyframes (I-frames) for efficient processing keyframes_view = pxt.create_view( 'videos/keyframes', videos_table, iterator=frame_iterator( video=videos_table.video, keyframes_only=True ) ) ``` ### Parameters * `fps`: Frames per second to extract (can be fractional) * `num_frames`: Exact number of frames to extract * `keyframes_only`: Extract only keyframes (I-frames) - efficient for quick video scanning * Only one of `fps`, `num_frames`, or `keyframes_only` can be specified ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} from pixeltable.functions.video import video_splitter # Split video into 10-second segments segments_view = pxt.create_view( 'videos/segments', videos_table, iterator=video_splitter( video=videos_table.video, duration=10.0, min_segment_duration=1.0 ) ) ``` ### Parameters * `duration`: Duration of each segment in seconds * `overlap`: Overlap between segments in seconds * `min_segment_duration`: Drop last segment if shorter than this value ### Returns For each segment, yields: * `segment_start`: Start time of the segment in seconds * `segment_end`: End time of the segment in seconds * `video_segment`: The video segment file ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} from pixeltable.functions.string import string_splitter # Split text into sentences sentences_view = pxt.create_view( 'texts/sentences', texts_table, iterator=string_splitter( text=texts_table.content, separators='sentence' ) ) ``` ### Parameters * `separators`: Choose from 'sentence' (requires spacy) ### Returns For each chunk, yields: * `text`: The text chunk ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} from pixeltable.functions.image import tile_iterator # Create tiles with overlap tiles_view = pxt.create_view( 'images/tiles', images_table, iterator=tile_iterator( image=images_table.image, tile_size=(224, 224), # Width, Height overlap=(32, 32) # Horizontal, Vertical overlap ) ) ``` ### Parameters * `tile_size`: Tuple of (width, height) for each tile * `overlap`: Optional tuple for overlap between tiles ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} from pixeltable.functions.audio import audio_splitter # Split by duration (exactly one of duration or max_size is required) segments = pxt.create_view( 'audio/segments', audio_table, iterator=audio_splitter( audio=audio_table.audio, duration=30.0, # ~30-second segments overlap=2.0, # 2-second overlap between segments min_segment_duration=5.0, # Drop last segment if < 5 seconds min_silence_len=0.3, # Snap cuts to quiet stretches silence_thresh=-40.0, trim_leading_silence=True, ), ) # Or split by byte budget (e.g. API upload limits) size_limited = pxt.create_view( 'audio/size_limited', audio_table, iterator=audio_splitter( audio=audio_table.audio, max_size=24 * 1024 * 1024, # at most 24 MB per segment ), ) ``` ### Parameters * `duration` (float | None): Segment length in seconds. Mutually exclusive with `max_size`. * `max_size` (int | None): Maximum segment size in bytes. Mutually exclusive with `duration`. * `overlap` (float, default: 0.0): Overlap between consecutive segments in seconds * `min_segment_duration` (float, default: 0.0): Drop the last segment if shorter than this * `min_silence_len` (float | None): If set, snap boundaries to quiet stretches of at least this length (seconds) * `silence_thresh` (float, default: -40.0): dBFS level at or below which audio counts as silence * `trim_leading_silence` (bool, default: False): Drop leading silence so each segment starts at audible content ### Returns For each segment, yields: * `segment_start`: Start time of the segment in seconds * `segment_end`: End time of the segment in seconds * `audio_segment`: The audio segment as `pxt.Audio` ### Notes * Exactly one of `duration` or `max_size` must be specified * With `max_size`, every emitted segment is guaranteed not to exceed the byte budget * With `min_silence_len`, cuts land at the latest silence at or before the duration/`max_size` budget * If the input contains no audio, no segments are yielded * Supports various audio formats including MP3, AAC, Vorbis, Opus, FLAC ## Common use cases Split documents for: * RAG systems * Text analysis * Content extraction Extract frames for: * Object detection * Scene classification * Activity recognition Create tiles for: * High-resolution analysis * Object detection * Segmentation tasks Split audio for: * Speech recognition * Sound classification * Audio feature extraction ## Example workflows ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Create document chunks chunks = pxt.create_view( 'rag/chunks', docs_table, iterator=document_splitter( document=docs_table.document, separators='sentence,token_limit', limit=500 ) ) # Add embeddings chunks.add_embedding_index( 'text', string_embed=sentence_transformer.using( model_id='all-mpnet-base-v2' ) ) ``` ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Extract frames at 1 FPS frames = pxt.create_view( 'detection/frames', videos_table, iterator=frame_iterator( video=videos_table.video, fps=1.0 ) ) # Add object detection frames.add_computed_column(detections=detect_objects(frames.frame)) ``` ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Split long audio files (silence-aware cuts avoid mid-word boundaries) segments = pxt.create_view( 'audio/segments', audio_table, iterator=audio_splitter( audio=audio_table.audio, duration=30.0, min_silence_len=0.3, ), ) # Add transcription segments.add_computed_column(text=whisper_transcribe(segments.audio_segment)) ``` ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} from pixeltable.functions.video import make_video # Extract frames at 1 FPS frames = pxt.create_view( 'video/frames', videos_table, iterator=frame_iterator( video=videos_table.video, fps=1.0 ) ) # Process frames (e.g., spread pixels to soften the image) frames.add_computed_column(processed=frames.frame.effect_spread(2)) # Create new videos from processed frames processed_videos = frames.select( frames.video_id, make_video(frames.pos, frames.processed) # Default fps is 25 ).group_by(frames.video_id).collect() ``` ## Best practices * Use appropriate chunk sizes * Consider overlap requirements * Monitor memory usage with large files * Balance chunk size vs. processing time * Use batch processing when possible * Cache intermediate results ## Tips & tricks When using `token_limit` with `document_splitter`, ensure the limit accounts for any model context windows in your pipeline. ## Custom iterators with `@pxt.iterator` You can create your own iterators using the `@pxt.iterator` decorator on a Python generator function. This is the simplest way to define a custom iterator that splits one row into many. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} from typing import Iterator, TypedDict import pixeltable as pxt class WordRow(TypedDict): word: str position: int @pxt.iterator def word_iterator(text: str) -> Iterator[WordRow]: for i, word in enumerate(text.split()): yield WordRow(word=word, position=i) # Use as a view iterator words_view = pxt.create_view( 'text/words', text_table, iterator=word_iterator(text_table.content) ) ``` Use `unstored_cols` to mark columns that should not be persisted. An unstored column is recomputed when a row is read, so the iterator must be able to jump straight to that row: `unstored_cols` requires a `seek()` method, and only a class can supply one. A function-style `@pxt.iterator` cannot use `unstored_cols`. This skeleton shows the required methods, not a runnable frame extractor. Implement `__next__()` to return a `FrameRow`, advance the position, and raise `StopIteration` at the end. After `seek(pos)`, the next call to `__next__()` must return the row at `pos`. The cookbook below has a runnable example. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} from typing import TypedDict import pixeltable as pxt class FrameRow(TypedDict): frame: pxt.Image timestamp: float @pxt.iterator(unstored_cols=['frame']) class my_frame_extractor(pxt.PxtIterator[FrameRow]): def __init__(self, video: pxt.Video, *, fps: float = 1.0): self.video = video self.fps = fps self.pos = 0 def __next__(self) -> FrameRow: # Custom frame extraction logic; raise StopIteration when done ... # seek() receives the position of the row being read, plus the stored # output columns of that row as keyword arguments. def seek(self, pos: int, **kwargs) -> None: self.pos = pos ``` Step-by-step guide to building custom iterators ## Additional resources All built-in iterators Chunk documents for RAG Extract video frames # Observability Source: https://docs.pixeltable.com/platform/observability Open in Kaggle  Open in Colab  Download Notebook This documentation page is also available as an interactive notebook. You can launch the notebook in Kaggle or Colab, or download it for use with an IDE or local Jupyter installation, by clicking one of the above links. Pixeltable can export its internal telemetry as OpenTelemetry (OTel) spans, so you can watch operations in any OTel backend. This notebook wires the OTel bridge to **Grafana Cloud** and traces a single `insert()` that exercises the full span set: source preparation, lock acquisition, planning, external media downloads, UDF evaluation, media persistence, the store write path, and view propagation. The insert emits one nested span tree:
  pixeltable.insert                    (operation span, root)
  ├─ pixeltable.data\_source.prepare    (source resolution + schema validation)
  ├─ pixeltable.catalog.begin\_xact     (connection + lock acquisition, one per attempt)
  ├─ pixeltable.plan.create            (insert plan construction, DEBUG level)
  ├─ pixeltable.media.fetch            (one per external file downloaded, DEBUG level)
  ├─ pixeltable.row                    (one per inserted row, DEBUG level)
  │  └─ pixeltable.udf.\          (each UDF call, nested under its row)
  ├─ pixeltable.media.save             (one per generated media file persisted, DEBUG level)
  ├─ pixeltable.store.build\_rows       (row -> store-row conversion, DEBUG level)
  ├─ pixeltable.sa.insert\_rows         (the SQL INSERT)
  └─ pixeltable.view\_load              (one per mutable view, containing its own row/store spans)
Traces land in Grafana Cloud Traces (Tempo). ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} %pip install -qU 'pixeltable[otel]' ``` ## Turn on the OpenTelemetry bridge Telemetry is opt-in: call `pxt_otel.init()` once per process, before the first table operation. It configures itself from the database’s `[[pixeltable.database]]` entry in `pixeltable.toml`, from environment variables, or from the `[otel]` section of `~/.pixeltable/config.toml`, in that order of precedence. **Option 1: the database entry in `pixeltable.toml`.** Put the endpoint on the `[[pixeltable.database]]` entry of the database (the entry with no `name` is the local database; see [Configuration](../platform/configuration#per-database-settings)). The headers are a secret and stay out of the file: keep `OTEL_EXPORTER_OTLP_HEADERS` in the environment, and on a hosted database set it with `pxt secret set pxt://org:db OTEL_EXPORTER_OTLP_HEADERS=...`, then `pxt db restart` so the running database sees it. ```toml theme={"theme":{"light":"light-plus","dark":"dark-plus"}} [[pixeltable.database]] db_exporter_otlp_endpoint = 'https://otlp-gateway-.grafana.net/otlp' ``` **Option 2: environment variables.** Set these **before starting the Jupyter kernel** (the kernel inherits them):
For Grafana Cloud, the OpenTelemetry connection page (Connections -> Add new connection -> OpenTelemetry) shows your ``, ``, and lets you create a ``. Then run these in the terminal, substituting your values, and start Jupyter from that same terminal: ```bash theme={"theme":{"light":"light-plus","dark":"dark-plus"}} export OTEL_EXPORTER_OTLP_ENDPOINT='https://otlp-gateway-.grafana.net/otlp' export OTEL_EXPORTER_OTLP_HEADERS="Authorization=Basic $(echo -n ':' | base64 | tr -d '\n')" ``` **Option 3: `~/.pixeltable/config.toml`**, shared by every database on the installation: ```toml theme={"theme":{"light":"light-plus","dark":"dark-plus"}} [otel] exporter_otlp_endpoint = 'https://otlp-gateway-.grafana.net/otlp' ``` `init()` runs **once per process**. If it ran without an endpoint earlier in this kernel it can’t be reconfigured, so fix the configuration, restart the kernel, and run this cell first. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} import opentelemetry.instrumentation.pixeltable as pxt_otel from opentelemetry import trace from opentelemetry.sdk.trace import TracerProvider pxt_otel.init( span_level='debug' ) # 'debug' emits per-row + per-UDF spans (default 'info' = operation span only) # fail loud instead of silently dropping every span: a no-op ProxyTracerProvider means the SDK never came up assert isinstance(trace.get_tracer_provider(), TracerProvider), ( 'OpenTelemetry is INERT: no OTLP endpoint was configured. ' 'Set the env vars (or config.toml) above, restart the kernel, and run this cell first.' ) print('OTel bridge active') ```
  OTel bridge active
## A pipeline that exercises every span type One table with an image column and two computed columns, plus a view: * inserting external image URLs triggers `pixeltable.media.fetch` (one download span per file, on worker threads) * the `add_one` UDF produces a `pixeltable.udf.add_one` span under each `pixeltable.row` * the stored `thumb` column generates thumbnails whose persistence produces `pixeltable.media.save` spans * the view is recomputed as part of the same insert, producing the nested `pixeltable.view_load` subtree `if_exists='replace_force'` also drops the dependent view on re-runs. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} import pixeltable as pxt t = pxt.create_table( 'otel_demo1', {'c': pxt.Int, 'img': pxt.Image}, if_exists='replace_force', ) # Simple expression (span not reported since no UDF is invoked) t.add_computed_column(inc=t.c + 1) # Complex expression (span reported since UDF is invoked) t.add_computed_column(thumb=t.img.resize((64, 64))) v = pxt.create_view( 'otel_demo_view', t, additional_columns={'inc2': t.inc + 2}, if_exists='replace', ) ``` ## Insert This is the traced operation: 4 rows referencing external images (well under the 100-span-per-operation cap, so every row gets a span). The whole pipeline, downloads, UDFs, thumbnails, store writes, and the view reload, lands under one `pixeltable.insert` trace. Note: `pixeltable.media.fetch` spans only appear when the files are not yet in the local file cache; on a re-run of this cell the downloads are skipped. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} base = 'https://raw.githubusercontent.com/pixeltable/pixeltable/main/docs/resources/images' images = [ '000000000001.jpg', '000000000016.jpg', '000000000019.jpg', '000000000025.jpg', ] status = t.insert( [{'c': i, 'img': f'{base}/{name}'} for i, name in enumerate(images)] ) status ```
  Inserted 8 rows with 0 errors in 0.04 s (224.62 rows/s)
  8 rows inserted.
## Wait a few seconds for export `init()` sets up a `BatchSpanProcessor`, which ships queued spans automatically about every 5 seconds while the kernel stays alive, so no manual flush is needed. Give it a few seconds after the insert, then check Grafana. (In a short-lived script that exits immediately you would call `trace.get_tracer_provider().force_flush()` before exit, but in a live notebook kernel the timer handles it.) ## View it in Grafana * **Traces**: Explore -> your Traces (Tempo) data source -> search Service Name `pixeltable`, or span name `pixeltable.insert`. Expand the trace to see the full hierarchy: `pixeltable.media.fetch` downloads running in parallel, `pixeltable.row` -> `pixeltable.udf.add_one`/`pixeltable.udf.resize` nesting, `pixeltable.media.save` persistence, the `pixeltable.store.build_rows`/`pixeltable.sa.insert_rows` write path, and the `pixeltable.view_load` subtree with its own rows and store writes. For more detail use `span_level='trace'`; to reduce volume on a large insert, use the default `'info'` (operation, `data_source.prepare`, `begin_xact`, `sa.insert_rows`, and `view_load` spans only). # Multimodal Type System Source: https://docs.pixeltable.com/platform/type-system Understanding Pixeltable types for structured data, media, and ML workflows Pixeltable provides a rich type system designed for multimodal AI applications. Every column and expression has an associated type that determines what data it can hold and what operations are available. This page leads with a `TableModel` in `app.py`. Create those tables with `pxt schema update`. Notebooks and tests can still call `pxt.create_table()`, `add_computed_column()`, and `add_embedding_index()`. ## Type overview | Pixeltable Type | Python Type | Description | | - | - | - | | `pxt.String` | `str` | Text data | | `pxt.Int` | `int` | Integer numbers | | `pxt.Float` | `float` | Decimal numbers | | `pxt.Bool` | `bool` | Boolean values | | `pxt.Timestamp` | `datetime.datetime` | Timestamp values | | `pxt.Date` | `datetime.date` | Date values | | `pxt.UUID` | `uuid.UUID` | Unique identifiers | | `pxt.Array` | `np.ndarray` | Numerical arrays (embeddings, tensors) | | `pxt.Json` | `dict`, `list`, `str`, `int`, `float`, `bool` | Flexible JSON data | | `pxt.Image` | `PIL.Image.Image` | Image data | | `pxt.Video` | `str` (file path) | Video files | | `pxt.Audio` | `str` (file path) | Audio files | | `pxt.Document` | `str` (file path) | Documents (PDFs, markdown, html, etc.) | A bare type is required. `title: pxt.String` rejects a missing value. Use `pxt.String | None` when the column may be empty. A column added to a table that already has rows must be nullable, so existing rows can stay empty. Table, column, and database names match without regard to case. `pxt.Audio`, `pxt.Video`, and `pxt.Document` return file paths when queried. Pixeltable automatically downloads and caches remote media locally. Use `.fileurl` to get the original URL. ## Basic types ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} import pixeltable as pxt TableModel = pxt.model_base() class Example(TableModel, name='basic_types'): text: pxt.String count: pxt.Int score: pxt.Float active: pxt.Bool created: pxt.Timestamp ``` In a notebook: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} table = pxt.create_table('example/basic_types', { 'text': pxt.String, 'count': pxt.Int, 'score': pxt.Float, 'active': pxt.Bool, 'created': pxt.Timestamp }) ``` ### Auto-generated UUIDs Use `uuid7()` to create columns that auto-generate unique identifiers: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} from pixeltable.functions.uuid import uuid7 # UUID as primary key - auto-generated for each row products = pxt.create_table('example/products', { 'id': uuid7(), # Auto-generates UUID 'name': pxt.String, 'price': pxt.Float }, primary_key=['id']) # Insert without providing 'id' - it's generated automatically products.insert([{'name': 'Laptop', 'price': 999.99}]) ``` You can also add UUIDs to existing tables: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Add UUID column to existing table orders.add_computed_column(order_id=uuid7()) ``` By default, `stored=True` for all computed columns—values compute once and persist. For UUIDs, this ensures stable identifiers. Setting `stored=False` would regenerate UUIDs on every query (almost never what you want). See the [UUID cookbook](/howto/cookbooks/core/workflow-uuid-identity) for more examples of working with unique identifiers. ## Media types Pixeltable natively supports images, video, audio, and documents as first-class column types. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} import pixeltable as pxt TableModel = pxt.model_base() class Media(TableModel, name='media'): image: pxt.Image video: pxt.Video audio: pxt.Audio document: pxt.Document ``` In a notebook: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} media = pxt.create_table('example/media', { 'image': pxt.Image, 'video': pxt.Video, 'audio': pxt.Audio, 'document': pxt.Document }) ``` ### Image specialization Images can be constrained by resolution and/or color mode: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Constrain by resolution thumbnails = pxt.create_table('example/thumbnails', { 'thumb': pxt.Image[(224, 224)] # Width 224, height 224 }) # Constrain by color mode grayscale = pxt.create_table('example/grayscale', { 'img': pxt.Image['L'] # Grayscale (1-channel) }) # Constrain both rgb_fixed = pxt.create_table('example/rgb_fixed', { 'img': pxt.Image[(300, 200), 'RGB'] # 300x200 RGB images }) ``` See the [PIL Documentation](https://pillow.readthedocs.io/en/stable/handbook/concepts.html) for the full list of image modes (`'RGB'`, `'RGBA'`, `'L'`, etc.). ## Array types (embeddings & tensors) Arrays are used for embeddings, feature vectors, and tensor data. They must always specify a shape and dtype. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} ml_data = pxt.create_table('example/ml_features', { # Fixed-size embedding (e.g., from CLIP or OpenAI) 'embedding': pxt.Array[(768,), pxt.Float], # Variable first dimension (batch of 512-dim vectors) 'features': pxt.Array[(None, 512), pxt.Float], # 3D tensor with flexible dimensions 'tensor': pxt.Array[(None, None, 3), pxt.Float] }) ``` Array shapes follow NumPy conventions. Use `None` for unconstrained dimensions: * `(512,)` — fixed 512-element vector * `(None, 768)` — variable-length sequence of 768-dim vectors * `(64, 64, 3)` — fixed 64×64×3 tensor ### Working with arrays ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Arrays can be sliced like NumPy arrays t.select( t.embedding[0], # First element t.embedding[5:10], # Slice t.embedding[-3:] # Last 3 elements ).collect() ``` ## JSON type The `Json` type stores flexible structured data—dictionaries, lists, or primitives. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} logs = pxt.create_table('example/logs', { 'event': pxt.Json }) logs.insert([ {'event': {'type': 'click', 'x': 100, 'y': 200}}, {'event': {'type': 'scroll', 'delta': 50}}, {'event': ['tag1', 'tag2', 'tag3']} ]) ``` ### JSON path access Access nested data using dictionary or attribute syntax: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Dictionary syntax t.select(t.event['type']).collect() # Attribute syntax (JSONPath) t.select(t.event.type).collect() # List indexing t.select(t.event.tags[0]).collect() # Slicing t.select(t.event.tags[:2]).collect() ``` Pixeltable handles missing keys gracefully—you'll get `None` instead of an exception. ### JSON schema validation Validate JSON columns against a schema to ensure data integrity: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Define a JSON schema movie_schema = { 'type': 'object', 'properties': { 'title': {'type': 'string'}, 'year': {'type': 'integer'}, 'rating': {'type': 'number'} }, 'required': ['title', 'year'] } # Create table with validated JSON column movies = pxt.create_table('example/validated_movies', { 'data': pxt.Json[movie_schema] }) # Valid insert movies.insert(data={'title': 'Inception', 'year': 2010, 'rating': 8.8}) # Invalid insert raises error (missing required 'year') # movies.insert(data={'title': 'Movie'}) # Error! ``` ### Using Pydantic models ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} from pydantic import BaseModel class Movie(BaseModel): title: str year: int rating: float | None = None # Use the model's JSON schema for validation movies = pxt.create_table('example/pydantic_movies', { 'data': pxt.Json[Movie.model_json_schema()] }) ``` ## Type conversion Use `astype()` to convert string file paths or URLs to media types: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # String file paths → Media types media = pxt.create_table('media_table', {'path': pxt.String}) media.insert([{'path': '/path/to/image.jpg'}]) # Convert string path to Image media.select(img=media.path.astype(pxt.Image)).collect() ``` **Primary use case:** Converting string columns containing file paths or URLs to media types (`Image`, `Video`, `Audio`, `Document`). For other type conversions, use built-in functions from the [`string`](/sdk/latest/string), [`json`](/sdk/latest/json), or [`math`](/sdk/latest/math) modules. For example, use `string.len()` to get string length as an integer, or access JSON fields directly. ## Column properties ### Media column properties Media columns (`Image`, `Video`, `Audio`, `Document`) have special properties: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Local file path (Pixeltable ensures this is on local filesystem) t.select(t.image.localpath).collect() # Original URL where the media resides t.select(t.image.fileurl).collect() ``` ### Error properties Computed columns have `errortype` and `errormsg` properties for debugging: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Create a computed column that might fail t.add_computed_column( result=some_function(t.input), on_error='ignore' # Continue on errors ) # Query error information for failed rows t.where(t.result == None).select( t.input, t.result.errortype, # Exception class name t.result.errormsg # Error message ).collect() ``` ## Best practices Prefer `pxt.Image[(224,224), 'RGB']` over `pxt.Image` when you know the constraints. This enables optimizations and catches errors early. Use JSON schema validation or Pydantic models for structured data to ensure consistency across your pipeline. Always specify array shapes and dtypes. Use `None` for variable dimensions: `pxt.Array[(None, 768), pxt.Float]`. Use `on_error='ignore'` in production pipelines, then query `.errortype` and `.errormsg` to debug failures. ## See also Creating and managing tables Transform data with computed columns Complete type reference # UDFs in Pixeltable Source: https://docs.pixeltable.com/platform/udfs-in-pixeltable Open in Kaggle  Open in Colab  Download Notebook This documentation page is also available as an interactive notebook. You can launch the notebook in Kaggle or Colab, or download it for use with an IDE or local Jupyter installation, by clicking one of the above links. Pixeltable comes with a library of built-in functions and integrations, but sooner or later, you’ll want to introduce some customized logic into your workflow. This is where Pixeltable’s rich UDF (User-Defined Function) capability comes in. Pixeltable UDFs let you write code in Python, then directly insert your custom logic into Pixeltable expressions and computed columns. In this how-to guide, we’ll show how to define UDFs, extend their capabilities, and use them in computed columns. This notebook uses `pxt.create_table()` so you can run cells without a project. An app puts the same columns on a `TableModel` in `app.py` and creates the tables with `pxt schema update`. To start, we’ll install the necessary dependencies, create a Pixeltable directory and table to experiment with, and add some sample data. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} %pip install -qU pixeltable ``` ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} import pixeltable as pxt # Create the directory and table pxt.drop_dir('udf_demo', force=True) # Ensure a clean slate for the demo pxt.create_dir('udf_demo') t = pxt.create_table('udf_demo/strings', {'input': pxt.String}) # Add some sample data t.insert( [ {'input': 'Hello, world!'}, {'input': 'You can do a lot with Pixeltable UDFs.'}, ] ) t.show() ```
  Connected to Pixeltable database at: postgresql+psycopg://postgres:@/pixeltable?host=/Users/asiegel/.pixeltable/pgdata
  Created directory \`udf\_demo\`.
  Created table \`strings\`.
  Inserting rows into \`strings\`: 2 rows \[00:00, 763.99 rows/s]
  Inserted 2 rows with 0 errors.
## What is a UDF? A Pixeltable UDF is just a Python function that is marked with the `@pxt.udf` decorator. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf def add_one(n: int) -> int: return n + 1 ``` It’s as simple as that! Without the decorator, `add_one` would be an ordinary Python function that operates on integers. Adding `@pxt.udf` converts it into a Pixeltable function that operates on *columns* of integers. The decorated function can then be used directly to define computed columns; Pixeltable will orchestrate its execution across all the input data. For our first working example, let’s do something slightly more interesting: write a function to extract the longest word from a sentence. (If there are ties for the longest word, we choose the first word among those ties.) In Python, that might look something like this: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} import numpy as np def longest_word(sentence: str, strip_punctuation: bool = False) -> str: words = sentence.split() if ( strip_punctuation ): # Remove non-alphanumeric characters from each word words = [''.join(filter(str.isalnum, word)) for word in words] i = np.argmax([len(word) for word in words]) return words[i] ``` ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} longest_word("Let's check that it works.", strip_punctuation=True) ```
  'check'
The `longest_word` Python function isn’t a Pixeltable UDF (yet); it operates on individual strings, not columns of strings. Adding the decorator turns it into a UDF: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf def longest_word(sentence: str, strip_punctuation: bool = False) -> str: words = sentence.split() if ( strip_punctuation ): # Remove non-alphanumeric characters from each word words = [''.join(filter(str.isalnum, word)) for word in words] i = np.argmax([len(word) for word in words]) return words[i] ``` Now we can use it to create a computed column. Pixeltable orchestrates the computation like it does with any other function, applying the UDF in turn to each existing row of the table, then updating incrementally each time a new row is added. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} t.add_computed_column(longest_word=longest_word(t.input)) t.show() ```
  Computing cells: 100%|███████████████████████████████████████████| 2/2 \[00:00\<00:00, 138.28 cells/s]
  Added 2 column values with 0 errors.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} t.insert([{'input': 'Pixeltable updates tables incrementally.'}]) t.show() ```
  Computing cells:   0%|                                                    | 0/3 \[00:00\

Oops, those trailing punctuation marks are kind of annoying. Let’s add another column, this time using the handy `strip_punctuation` parameter from our UDF. (We could alternatively drop the first column before adding the new one, but for purposes of this tutorial it’s convenient to see how Pixeltable executes both variants side-by-side.) Note how *columns* such as `t.input` and *constants* such as `True` can be freely intermixed as arguments to the UDF. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} t.add_computed_column( longest_word_2=longest_word(t.input, strip_punctuation=True) ) t.show() ```
  Computing cells: 100%|███████████████████████████████████████████| 3/3 \[00:00\<00:00, 252.91 cells/s]
  Added 3 column values with 0 errors.
## Types in UDFs You might have noticed that the `longest_word` UDF has *type hints* in its signature. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} def longest_word(sentence: str, strip_punctuation: bool = False) -> str: ... ``` The `sentence` parameter, `strip_punctuation` parameter, and return value all have explicit types (`str`, `bool`, and `str` respectively). In general Python code, type hints are usually optional. But Pixeltable is a database system: *everything* in Pixeltable must have a type. And since Pixeltable is also an orchestrator - meaning it sets up workflows and computed columns *before* executing them - these types need to be known in advance. That’s the reasoning behind a fundamental principle of Pixeltable UDFs: * Type hints are *required*. You can turn almost any Python function into a Pixeltable UDF, provided that it has type hints, and provided that Pixeltable supports the types that it uses. The most familiar types that you’ll use in UDFs are: * `int` * `float` * `str` * `list` (can optionally be parameterized, e.g., `list[str]`) * `dict` (can optionally be parameterized, e.g., `dict[str, int]`) * `PIL.Image.Image` In addition to these standard Python types, Pixeltable also recognizes various kinds of arrays, audio and video media, and documents. ## Local and module UDFs The `longest_word` UDF that we defined above is a *local* UDF: it was defined directly in our notebook, rather than in a module that we imported. Many other UDFs, including all of Pixeltable’s built-in functions, are defined in modules. We encountered a few of these in the 10-Minute Tour tutorial: the `huggingface.detr_for_object_detection` and `openai.chat_completions` functions. (Although these are built-in functions, they behave the same way as UDFs, and in fact they’re defined the same way under the covers.) There is an important difference between the two. When you add a module UDF such as `openai.chat_completions` to a table, Pixeltable stores a *reference* to the corresponding Python function in the module. If you later restart your Python runtime and reload Pixeltable, then Pixeltable will re-import the module UDF when it loads the computed column. This means that any code changes made to the UDF will be picked up at that time, and the new version of the UDF will be used in any future execution. Conversely, when you add a local UDF to a table, the *entire code* for the UDF is serialized and stored in the table. This ensures that if you restart your notebook kernel (say), or even delete the notebook entirely, the UDF will continue to function. However, it also means that if you modify the UDF code, the updated logic will not be reflected in any existing Pixeltable columns. To see how this works in practice, let’s modify our `longest_word` UDF so that if `strip_punctuation` is `True`, then we remove only a single punctuation mark from the *end* of each word. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf def longest_word(sentence: str, strip_punctuation: bool = False) -> str: words = sentence.split() if strip_punctuation: words = [ word if word[-1].isalnum() else word[:-1] for word in words ] i = np.argmax([len(word) for word in words]) return words[i] ``` Now we see that Pixeltable continues to use the *old* definition, even as new rows are added to the table. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} t.insert([{'input': "Let's check that it still works."}]) t.show() ```
  Computing cells:   0%|                                                    | 0/5 \[00:00\

But if we add a new *column* that references the `longest_word` UDF, Pixeltable will use the updated version. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} t.add_computed_column( longest_word_3=longest_word(t.input, strip_punctuation=True) ) t.show() ```
  Computing cells: 100%|███████████████████████████████████████████| 4/4 \[00:00\<00:00, 348.89 cells/s]
  Added 4 column values with 0 errors.
The general rule is: changes to module UDFs will affect any future execution; changes to local UDFs will only affect *new columns* that are defined using the new version of the UDF. ## Batching Pixeltable provides several ways to optimize UDFs for better performance. One of the most common is *batching*, which is particularly important for UDFs that involve GPU operations. Ordinary UDFs process one row at a time, meaning the UDF will be invoked exactly once per row processed. Conversely, a batched UDF processes several rows at a time; the specific number is user-configurable. As an example, let’s modify our `longest_word` UDF to take a batched parameter. Here’s what it looks like: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} from pixeltable.func import Batch @pxt.udf(batch_size=16) def longest_word( sentences: Batch[str], strip_punctuation: bool = False ) -> Batch[str]: results = [] for sentence in sentences: words = sentence.split() if strip_punctuation: words = [ word if word[-1].isalnum() else word[:-1] for word in words ] i = np.argmax([len(word) for word in words]) results.append(words[i]) return results ``` There are several changes: * The parameter `batch_size=16` has been added to the `@pxt.udf` decorator, specifying the batch size; * The `sentences` parameter has changed from `str` to `Batch[str]`; * The return type has also changed from `str` to `Batch[str]`; and * Instead of processing a single sentence, the UDF is processing a `Batch` of sentences and returning the result `Batch`. What exactly is a `Batch[str]`? Functionally, it’s simply a `list[str]`, and you can use it exactly like a `list[str]` in any Python code. The only difference is in the type hint; a type hint of `Batch[str]` tells Pixeltable, “My data consists of individual strings that I want you to process in batches”. Conversely, a type hint of `list[str]` would mean, “My data consists of *lists* of strings that I want you to process one at a time”. Notice that the `strip_punctuation` parameter is *not* wrapped in a `Batch` type. This because `strip_punctuation` controls the behavior of the UDF, rather than being part of the input data. When we use the batched `longest_word` UDF, the `strip_punctuation` parameter will always be a constant, not a column. Let’s put the new, batched UDF to work. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} t.add_computed_column( longest_word_3_batched=longest_word(t.input, strip_punctuation=True) ) t.show() ```
  Computing cells: 100%|███████████████████████████████████████████| 4/4 \[00:00\<00:00, 353.90 cells/s]
  Added 4 column values with 0 errors.
As expected, the output of the `longest_word_3_batched` column is identical to the `longest_word_3` column. Under the covers, though, Pixeltable is orchestrating execution in batches of 16. That probably won’t have much performance impact on our toy example, but for GPU-bound computations such as text or image embeddings, it can make a substantial difference. ## UDAs (aggregate UDFs) Ordinary UDFs are always one-to-one on rows: each row of input generates one UDF output value. Functions that aggregate data, conversely, are many-to-one, and in Pixeltable they are represented by a related abstraction, the UDA (User-Defined Aggregate). Pixeltable has a number of built-in UDAs; if you’ve worked through the Fundamentals tutorial, you’ll have already encountered a few of them, such as `sum` and `count`. In this section, we’ll show how to define your own custom UDAs. For demonstration purposes, let’s start by creating a table containing all the integers from 0 to 49. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} import pixeltable as pxt t = pxt.create_table('udf_demo/values', {'val': pxt.Int}) t.insert({'val': n} for n in range(50)) ```
  Created table \`values\`.
  Inserting rows into \`values\`: 50 rows \[00:00, 9267.95 rows/s]
  Inserted 50 rows with 0 errors.
  UpdateStatus(num\_rows=50, num\_computed\_values=0, num\_excs=0, updated\_cols=\[], cols\_with\_excs=\[])
If we wanted to compute their sum using the built-in `sum` aggregate, we’d do it like this: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} import pixeltable.functions as pxtf t.select(pxtf.sum(t.val)).collect() ```
Or perhaps we want to group them by `n // 10` (corresponding to the tens digit of each integer) and sum each group: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} t.group_by(t.val // 10).order_by(t.val // 10).select( t.val // 10, pxtf.sum(t.val) ).collect() ```
Now let’s define a new aggregate to compute the sum of squares of a set of numbers. To define an aggregate, we implement a subclass of the `pxt.Aggregator` Python class and decorate it with the `@pxt.uda` decorator, similar to what we did for UDFs. The subclass must implement three methods: * `__init__()` - initializes the aggregator; can be used to parameterize aggregator behavior * `update()` - updates the internal state of the aggregator with a new value * `value()` - retrieves the current value held by the aggregator In our example, the class will have a single member `cur_sum`, which holds a running total of the squares of all the values we’ve seen. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.uda class sum_of_squares(pxt.Aggregator): def __init__(self): # No data yet; initialize `cur_sum` to 0 self.cur_sum = 0 def update(self, val: int) -> None: # Update the value of `cur_sum` with the new datapoint self.cur_sum += val * val def value(self) -> int: # Retrieve the current value of `cur_sum` return self.cur_sum ``` ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} t.select(sum_of_squares(t.val)).collect() ```
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} t.group_by(t.val // 10).order_by(t.val // 10).select( t.val // 10, sum_of_squares(t.val) ).collect() ```
## Ship it as an app A UDF used as a column must be defined in this file (or another module under the project root): ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} import pixeltable as pxt TableModel = pxt.model_base() @pxt.udf def reverse(s: str) -> str: return s[::-1] class Strings(TableModel, name='strings'): input: pxt.String reversed = reverse(input) ``` ```bash theme={"theme":{"light":"light-plus","dark":"dark-plus"}} pxt schema update app.py udf_demo ``` # Version Control and Lineage Source: https://docs.pixeltable.com/platform/version-control Automatic versioning, time travel queries, and full data lineage tracking Pixeltable automatically tracks every change to your tables—data insertions, updates, deletions, and schema modifications. Query any point in history, undo mistakes, and maintain full reproducibility without manual version management. Put the table on a `TableModel` in `app.py` for an application. Inserts, updates, `history()`, and time travel below are runtime APIs and are the same in an app or a notebook. ## How it works Every operation that modifies a table creates a new version: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} import pixeltable as pxt TableModel = pxt.model_base() class Products(TableModel, name='products'): name: pxt.String price: pxt.Float ``` ```bash theme={"theme":{"light":"light-plus","dark":"dark-plus"}} pxt schema update app.py demo ``` ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} products = pxt.get_table('demo.products') # Version 1: Data inserted products.insert([ {'name': 'Widget', 'price': 9.99}, {'name': 'Gadget', 'price': 24.99} ]) # Version 2: Schema changed (notebook API; in app.py this is another computed column on the model) products.add_computed_column(price_with_tax=products.price * 1.08) # Version 3: Data updated products.update({'price': 19.99}, where=products.name == 'Widget') ``` No configuration required—versioning is always on. ## Viewing history ### Human-readable history ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} products.history() ``` Returns a DataFrame showing all versions with timestamps, change types, and row counts: | version | created\_at | change\_type | inserts | updates | deletes | schema\_change | | - | - | - | - | - | - | - | | 3 | 2025-01-15 10:30:00 | data | 0 | 1 | 0 | None | | 2 | 2025-01-15 10:29:00 | schema | 0 | 2 | 0 | Added: price\_with\_tax | | 1 | 2025-01-15 10:28:00 | data | 2 | 0 | 0 | None | | 0 | 2025-01-15 10:27:00 | schema | 0 | 0 | 0 | Initial Version | ### Programmatic access ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} versions = products.get_versions() # List of dictionaries latest = versions[0] print(f"Version {latest['version']}: {latest['inserts']} inserts") ``` ## Time travel queries Query any historical version using the `table_name:version` syntax: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Get the table at version 1 (before computed column) products_v1 = pxt.get_table('demo/products:1') products_v1.collect() # Returns data as it was at version 1 # Compare with current state products.collect() # Returns current data ``` Version handles are **read-only**—you cannot modify historical data. ### Use cases * **Debugging**: Compare data before and after a problematic update * **Auditing**: Track who changed what and when * **Recovery**: Find and extract accidentally deleted or modified data * **Reproducibility**: Query exact data used for a specific model training run ## Reverting changes Undo the most recent change with `revert()`: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Oops, wrong update products.update({'price': 0.00}, where=products.name == 'Widget') # Undo it products.revert() # Removes version N, table is now at version N-1 ``` `revert()` permanently removes the latest version. This cannot be undone. You can call `revert()` multiple times to go back further, but cannot revert past version 0 or past a version referenced by a snapshot. ## Snapshots Create named, persistent point-in-time copies for long-term preservation: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Freeze current state before a major data update baseline = pxt.create_snapshot('demo/products_baseline', products) # Later: source table changes, but snapshot remains unchanged products.insert([{'name': 'NewItem', 'price': 99.99}]) products.count() # 3 rows (updated) baseline.count() # 2 rows (frozen) ``` **Snapshots vs Time Travel:** * Time travel (`pxt.get_table('table:N')`) queries historical versions in place * Snapshots create a named, independent copy that persists even if the source table is modified or deleted ## Data lineage Pixeltable tracks the complete lineage of your data: ### Schema lineage Every computed column records its dependencies: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} products.add_computed_column( discounted=products.price * 0.9 ) products.add_computed_column( discounted_with_tax=products.discounted * 1.08 ) # Pixeltable knows: discounted_with_tax → discounted → price ``` ### View lineage Views automatically track their source tables: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} expensive = pxt.create_view( 'demo/expensive_products', products.where(products.price > 20) ) # View lineage: expensive_products → products ``` ### What's tracked | Change Type | Tracked Information | | - | - | | `insert()` | Row count, timestamp, computed values generated | | `update()` | Rows affected, old vs new values (via version comparison) | | `delete()` | Row count removed | | `add_column()` | Column name, type, dependencies | | `add_computed_column()` | Column name, expression, dependencies | | `drop_column()` | Column removed | | `rename_column()` | Old name → new name | ## Best practices Create snapshots before major data loads, model training runs, or production deployments. Log table version numbers alongside model artifacts: `products.get_versions()[0]['version']` Use `revert()` immediately after mistakes. For older issues, use time travel to identify the problem. Use directories like `dev/products`, `staging/products` to isolate versioning across environments. ## Comparison with other systems | Feature | Pixeltable | Git | Delta Lake | | - | - | - | - | | Automatic versioning | ✅ Every operation | Manual commits | ✅ Every operation | | Time travel queries | ✅ `table:N` syntax | Checkout commit | ✅ `VERSION AS OF` | | Schema versioning | ✅ Tracked | File-based | ✅ Schema evolution | | Computed column lineage | ✅ Automatic | N/A | N/A | | Revert | ✅ `revert()` | `git revert` | `RESTORE` | | Named snapshots | ✅ `create_snapshot()` | Tags/branches | N/A | ## Next steps Step-by-step cookbook with runnable examples # Views Source: https://docs.pixeltable.com/platform/views Learn how to create and use virtual derived tables in Pixeltable through views # When to Use Views Views in Pixeltable are best used when you need to: 1. **Transform Data**: When you need to process or reshape data from a base table (e.g., splitting documents into chunks, extracting features from images) 2. **Filter Data**: When you frequently need to work with a specific subset of your data 3. **Create Virtual Tables**: When you want to avoid storing redundant data and automatically keep derived data in sync 4. **Build Data Workflows**: When you need to chain multiple data transformations together 5. **Save Storage**: When you want to compute data on demand rather than storing it permanently Choose views over tables when your data is derived from other base tables and needs to stay synchronized with its source. Use regular tables when you need to store original data or when the computation cost of deriving data on demand is too high. Production apps declare views as `TableModel` classes in a Python file and create them with `pxt schema update`. Notebooks and tests can still call `pxt.create_table()` / `pxt.create_view()`. The `token_limit` separator below needs `tiktoken`, which the base install does not include: `pip install tiktoken`. ## Phase 1: Define your base table and view structure ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} import pixeltable as pxt import pixeltable.functions as pxtf TableModel = pxt.model_base() class Documents(TableModel, name='collection'): document: pxt.Document class Chunks( TableModel, name='chunks', base=Documents, iterator=pxtf.document.document_splitter(Documents.document, separators='token_limit', limit=300), ): pass ``` ```bash theme={"theme":{"light":"light-plus","dark":"dark-plus"}} pxt schema update app.py documents ``` In a notebook: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} import pixeltable as pxt from pixeltable.functions.document import document_splitter pxt.drop_dir('documents', force=True) pxt.create_dir('documents') documents = pxt.create_table( "documents/collection", {"document": pxt.Document} ) chunks = pxt.create_view( 'documents/chunks', documents, iterator=document_splitter( document=documents.document, separators='token_limit', limit=300 ) ) ``` ## Phase 2: Use your application ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} import pixeltable as pxt # Connect to your base table and view documents = pxt.get_table("documents/collection") chunks = pxt.get_table("documents/chunks") # Insert data into base table - view updates automatically documents.insert([{ "document": "path/to/document.pdf" }]) # Query the view print(chunks.collect()) ``` ## View types Views created using iterators to transform data: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Document splitting view chunks = pxt.create_view( 'docs/chunks', documents, iterator=document_splitter( document=documents.document ) ) ``` Views created from query operations: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Filtered view of high-budget movies blockbusters = pxt.create_view( 'movies/blockbusters', movies.where(movies.budget >= 100.0) ) ``` ## View operations Query views like regular tables: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Basic filtering on view chunks.where(chunks.text.contains('specific topic')).collect() # Select specific columns chunks.select(chunks.text, chunks.pos).collect() # Order results chunks.order_by(chunks.pos).limit(5).collect() ``` Add computed columns to views: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Add embeddings to chunks chunks.add_computed_column( embedding=sentence_transformer.using( model_id='intfloat/e5-large-v2' )(chunks.text) ) ``` Create views based on other views: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Create a view of embedded chunks embedded_chunks = pxt.create_view( 'docs/embedded_chunks', chunks.where(chunks.text.len() > 100) ) ``` ## Key features Views automatically update when base tables change Views compute data on demand, saving storage Views can be part of larger data workflows # AI Skills Source: https://docs.pixeltable.com/resources/ai-skills Install the Pixeltable skill. The coding agent writes TableModel classes in app.py. One skill file teaches the coding agent to declare tables as `TableModel` classes in `app.py` and create them with `pxt schema update`. The skill's workflow is: create tables from the file, start the endpoints with `pxt service update`, then try the app by inserting rows or opening the dashboard. Install details: [AI coding agents](/overview/building-pixeltable-with-llms). The skill is [pixeltable/pixeltable-skill](https://github.com/pixeltable/pixeltable-skill), and [docs.pixeltable.com/skill.md](https://docs.pixeltable.com/skill.md) points there. Install it: ```bash theme={"theme":{"light":"light-plus","dark":"dark-plus"}} npx skills add pixeltable/pixeltable-skill ``` If the agent writes `create_table` in app code, names the file `schema.py`, or writes the removed command `pxt serve`, the installed skill is stale: this page wins. Reinstall: `npx skills add pixeltable/pixeltable-skill`. ## What it teaches | Do | Do not | | - | - | | `class Docs(TableModel, name='docs')` in `app.py` | LangChain / LlamaIndex for chunking and retrieval | | Assignments as computed columns | pandas as the database that holds your app data | | `__indexes__` on the model | A separate vector database | | `pxt init`, then `pxt schema update`, then `pxt service update` | Per-row `for` loops that call a model | | Use the same `app.py` with a `pxt://` Cloud URI | Call `add_embedding_index()` in application code | | `from pixeltable.serving import FastAPIRouter` | Declare HTTP routes in TOML | Scaffold: `uvx pixeltable-new myapp`, then `pxt schema update app.py agent`. [Starter kit](/resources/starter-kit). ## How to invoke it Compatible agents pick the skill up when the task is a Pixeltable app. To call it by name: | Tool | Invoke | | - | - | | Cursor | `/pixeltable` | | Claude Code | `/pixeltable` | | GitHub Copilot | `/pixeltable` | | Codex | `$pixeltable` | Write `app.py`, then `pxt schema update` and `pxt service update`. Install details and which file the agent should write. # Starter kit Source: https://docs.pixeltable.com/resources/starter-kit uvx pixeltable-new copies a starter app. pxt schema update creates its tables. pxt service update starts the endpoints. The [Pixeltable Starter Kit](https://github.com/pixeltable/pixeltable-starter-kit) is two apps. `uvx pixeltable-new` copies one into a new directory. First create the tables (`pxt schema update`), then start the endpoints (`pxt service update`), then try the app by inserting rows, calling `/ask`, or opening the dashboard. A first run: [Quickstart](/overview/quick-start). ```bash theme={"theme":{"light":"light-plus","dark":"dark-plus"}} uvx pixeltable-new myapp cd myapp uv sync pxt schema update app.py agent pxt service update app.py agent ``` `agent` is the catalog Pixeltable creates, not a folder on disk. For the video app, run `uvx pixeltable-new myapp --video` and use `videointel` as that catalog. The scaffold includes `pixeltable.toml`. If you copied files by hand, run `pxt init` first. Routes live on a `FastAPIRouter` in the same file as the `TableModel` classes. If you already have a FastAPI app, mount the kit's router with `app.include_router(...)`. [HTTP serving](/howto/deployment/serving). To skip endpoints, see [Self-hosting](/howto/deployment/overview) for the insert-and-export path. ## Apps The default app is the chat agent. You can insert documents without an API key; `/ask` needs `ANTHROPIC_API_KEY`. ```bash theme={"theme":{"light":"light-plus","dark":"dark-plus"}} uvx pixeltable-new myapp pxt schema update app.py agent ``` Extracts video frames at 1 FPS, indexes them with CLIP, and accepts image uploads. ```bash theme={"theme":{"light":"light-plus","dark":"dark-plus"}} uvx pixeltable-new myapp --video pxt schema update app.py videointel ``` To run the same starter app on Pixeltable Cloud, sign in with `pxt login`. `pxt org create` provisions `main`. Add this entry to the project file before `pxt db update`. Without it, the command errors. `pxt db update` uploads the project onto that database: ```toml theme={"theme":{"light":"light-plus","dark":"dark-plus"}} [[pixeltable.database]] name = 'pxt://org:main' ``` ```bash theme={"theme":{"light":"light-plus","dark":"dark-plus"}} pxt db update pxt://org:main pxt schema update app.py pxt://org:main pxt service update app.py pxt://org:main ``` [Pixeltable Cloud](/cloud). `pxt service run` starts a service in your terminal and cannot target Cloud; use `pxt service update` for hosted endpoints. Teach an agent to write `TableModel` in `app.py`. `chat-agent/` and `video-search/` in one repo. # Support Source: https://docs.pixeltable.com/resources/support Get help from the Pixeltable community Join the Pixeltable community for questions, feedback, and support. Chat with the community and Pixeltable team Ask questions and share ideas Report bugs and request features Schedule time with the team # anthropic Source: https://docs.pixeltable.com/sdk/latest/anthropic View Source on GitHub # module  pixeltable.functions.anthropic Pixeltable UDFs that wrap various endpoints from the Anthropic API. In order to use them, you must first `pip install anthropic` and configure your Anthropic credentials, as described in the [Working with Anthropic](https://docs.pixeltable.com/howto/providers/working-with-anthropic) tutorial. ## func  invoke\_tools() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} invoke_tools( tools: pixeltable.func.tools.Tools, response: pixeltable.exprs.expr.Expr ) -> pixeltable.exprs.inline_expr.InlineDict ``` Converts an Anthropic response dict to Pixeltable tool invocation format and calls `tools._invoke()`. ## udf  messages() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf messages( messages: pxt.Json[(Json, ...)], *, model: pxt.String, max_tokens: pxt.Int, model_kwargs: pxt.Json | None = None, tools: pxt.Json[(Json, ...)] | None = None, tool_choice: pxt.Json | None = None ) -> pxt.Json ``` Create a Message. Equivalent to the Anthropic `messages` API endpoint. For additional details, see: [https://docs.anthropic.com/en/api/messages](https://docs.anthropic.com/en/api/messages) Request throttling: Uses the rate limit-related headers returned by the API to throttle requests adaptively, based on available request and token capacity. No configuration is necessary. **Requirements:** * `pip install anthropic` **Parameters:** * **`messages`** (`pxt.Json[(Json`): Input messages. * **`model`** (`Any`): The model that will complete your prompt. * **`model_kwargs`** (`Any`): Additional keyword args for the Anthropic `messages` API. For details on the available parameters, see: [https://docs.anthropic.com/en/api/messages](https://docs.anthropic.com/en/api/messages) * **`tools`** (`Any`): An optional list of Pixeltable tools to use for the request. * **`tool_choice`** (`Any`): An optional tool choice configuration. **Returns:** * `pxt.Json`: A dictionary containing the response and other metadata. **Examples:** Add a computed column that applies the model `claude-3-5-sonnet-20241022` to an existing Pixeltable column `tbl.prompt` of the table `tbl`: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} msgs = [{'role': 'user', 'content': tbl.prompt}] tbl.add_computed_column( response=messages(msgs, model='claude-3-5-sonnet-20241022') ) ``` # array Source: https://docs.pixeltable.com/sdk/latest/array View Source on GitHub # module  pixeltable.functions.array Pixeltable UDFs for `ArrayType`. Example: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} import pixeltable as pxt t = pxt.get_table(...) t.select(t.array_col.to_list()).collect() ``` ## udf  to\_list() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf to_list(self: pxt.Array) -> pxt.Json ``` Convert an array to a nested Python list. Equivalent to numpy's [`ndarray.tolist()`](https://numpy.org/doc/stable/reference/generated/numpy.ndarray.tolist.html). Useful for exporting to systems that lack a fixed-shape tensor type (e.g. Iceberg). **Examples:** ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} t.select(t.array_col.to_list()).collect() ``` # audio Source: https://docs.pixeltable.com/sdk/latest/audio View Source on GitHub # module  pixeltable.functions.audio Pixeltable UDFs for `AudioType`. ## iterator  audio\_splitter() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.iterator audio_splitter( audio: pxt.Audio, *, duration: pxt.Float | None = None, max_size: pxt.Int | None = None, overlap: pxt.Float = 0.0, min_segment_duration: pxt.Float = 0.0, min_silence_len: pxt.Float | None = None, silence_thresh: pxt.Float = -40.0, trim_leading_silence: pxt.Bool = False ) ``` Iterator over segments of an audio file. The audio file is split into smaller segments, sized either by `duration` (seconds) or by `max_size` (bytes). Exactly one of the two must be specified. * With `max_size`, every emitted segment is guaranteed not to exceed the given number of bytes. * With `duration`, each segment is approximately that long (a segment may run over by up to one packet). **Outputs**: One row per audio segment, with the following columns: * `segment_start` (`pxt.Float`): Start time of the audio segment in seconds * `segment_end` (`pxt.Float`): End time of the audio segment in seconds * `audio_segment` (`pxt.Audio | None`): The audio content of the segment When `min_silence_len` is set, segment boundaries are snapped to silences so that segments do not end in the middle of speech: each segment ends at the latest silence at or before its `duration` or `max_size` budget. `trim_leading_silence` additionally drops silence from the start of every segment. **Parameters:** * **`duration`** (`pxt.Float | None`): Audio segment duration in seconds. Mutually exclusive with `max_size`. * **`max_size`** (`pxt.Int | None`): Maximum audio segment size in bytes. Mutually exclusive with `duration`. * **`overlap`** (`pxt.Float`): Overlap between consecutive segments in seconds * **`min_segment_duration`** (`pxt.Float`): Drop the last segment if it is smaller than `min_segment_duration` (in seconds). * **`min_silence_len`** (`pxt.Float | None`): If set, enables silence-aware boundaries; the minimum length in seconds of a quiet stretch that counts as a usable cut point. * **`silence_thresh`** (`pxt.Float`): Level in dBFS at or below which audio is considered silent. Used when `min_silence_len` is set or `trim_leading_silence` is true. * **`trim_leading_silence`** (`pxt.Bool`): If true, drop leading silence from each segment so it starts at audible content. **Examples:** This example assumes an existing table `tbl` with a column `audio` of type `pxt.Audio`. Create a view that splits all audio files into segments of 30 seconds with 5 seconds overlap: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} pxt.create_view( 'audio_segments', tbl, iterator=audio_splitter(tbl.audio, duration=30.0, overlap=5.0), ) ``` Create a view that splits all audio files into segments of at most 24 MB, for transcription: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} pxt.create_view( 'audio_segments', tbl, iterator=audio_splitter(tbl.audio, max_size=24 * 1024 * 1024), ) ``` ## udf  encode\_audio() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf encode_audio( audio_data: pxt.Array[float32], *, input_sample_rate: pxt.Int, format: pxt.String, output_sample_rate: pxt.Int | None = None ) -> pxt.Audio ``` Encodes an audio clip represented as an array into a specified audio format. **Parameters:** * **`audio_data`** (`pxt.Array[float32]`): An array of sampled amplitudes. The accepted array shapes are `(N,)` or `(1, N)` for mono audio or `(2, N)` for stereo. * **`input_sample_rate`** (`pxt.Int`): The sample rate of the input audio data. * **`format`** (`pxt.String`): The desired output audio format. The supported formats are 'wav', 'mp3', 'flac', and 'mp4'. * **`output_sample_rate`** (`pxt.Int | None`): The desired sample rate for the output audio. Defaults to the input sample rate if unspecified. **Examples:** Add a computed column with encoded FLAC audio files to a table with audio data (as arrays of floats) and sample rates: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} t.add_computed_column( audio_file=encode_audio( t.audio_data, input_sample_rate=t.sample_rate, format='flac' ) ) ``` ## udf  get\_metadata() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf get_metadata(audio: pxt.Audio) -> ContainerMetadata ``` Gets various metadata associated with an audio file and returns it as a [`ContainerMetadata`](./containermetadata) dictionary. **Parameters:** * **`audio`** (`pxt.Audio`): The audio to get metadata for. **Returns:** * `ContainerMetadata`: A [`ContainerMetadata`](./containermetadata) with typical structure: ```json theme={"theme":{"light":"light-plus","dark":"dark-plus"}} { 'size': 2568827, 'streams': [ { 'type': 'audio', 'frames': 0, 'duration': 2646000, 'metadata': {}, 'time_base': 2.2675736961451248e-05, 'codec_context': { 'name': 'flac', 'profile': None, 'channels': 1, 'codec_tag': '\x00\x00\x00\x00', }, 'duration_seconds': 60.0, } ], 'bit_rate': 342510, 'metadata': {'encoder': 'Lavf61.1.100'}, 'bit_exact': False, } ``` **Examples:** Extract metadata for files in the `audio_col` column of the table `tbl`: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} tbl.select(tbl.audio_col.get_metadata()).collect() ``` # bedrock Source: https://docs.pixeltable.com/sdk/latest/bedrock View Source on GitHub # module  pixeltable.functions.bedrock Pixeltable UDFs for AWS Bedrock AI models. In order to use them, you must first configure your AWS credentials, as described in the [Working with Bedrock](https://docs.pixeltable.com/howto/providers/working-with-bedrock) tutorial. ## func  invoke\_tools() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} invoke_tools( tools: pixeltable.func.tools.Tools, response: pixeltable.exprs.expr.Expr ) -> pixeltable.exprs.inline_expr.InlineDict ``` Converts a Bedrock response dict to Pixeltable tool invocation format and calls `tools._invoke()`. ## udf  converse() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf converse( messages: pxt.Json[(Json, ...)], *, model_id: pxt.String, system: pxt.Json[(Json, ...)] | None = None, inference_config: pxt.Json | None = None, additional_model_request_fields: pxt.Json | None = None, tool_config: pxt.Json[(Json, ...)] | None = None ) -> pxt.Json ``` Generate a conversation response. Equivalent to the AWS Bedrock `converse` API endpoint. For additional details, see: [https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/bedrock-runtime/client/converse.html](https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/bedrock-runtime/client/converse.html) PIL images and media file paths in `messages[*].content[*].(image|video|audio).source.bytes` are converted to raw bytes automatically. **Parameters:** * **`messages`** (`pxt.Json[(Json`): Input messages. * **`model_id`** (`Any`): The model that will complete your prompt. * **`system`** (`Any`): An optional system prompt. * **`inference_config`** (`Any`): Base inference parameters to use. * **`additional_model_request_fields`** (`Any`): Additional inference parameters to use. * **`tool_config`** (`Any`): An optional list of Pixeltable tools to use. **Returns:** * `pxt.Json`: A dictionary containing the response and other metadata. **Examples:** Add a computed column that applies the model `anthropic.claude-3-haiku-20240307-v1:0` to an existing Pixeltable column `tbl.prompt` of the table `tbl`: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} msgs = [{'role': 'user', 'content': [{'text': tbl.prompt}]}] tbl.add_computed_column( response=converse( msgs, model_id='anthropic.claude-3-haiku-20240307-v1:0' ) ) ``` Pass an image via the Converse API: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} msgs = [ { 'role': 'user', 'content': [ {'image': {'format': 'jpeg', 'source': {'bytes': tbl.image}}}, {'text': "What's in this image?"}, ], } ] tbl.add_computed_column( response=converse(msgs, model_id='amazon.nova-lite-v1:0') ) ``` ## udf  embed() ```python Signatures theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Signature 1: @pxt.udf embed( text: pxt.String, *, model_id: pxt.String, dimensions: pxt.Int | None ) -> pxt.Array[(None,), float32] # Signature 2: @pxt.udf embed( image: pxt.Image, *, model_id: pxt.String, dimensions: pxt.Int | None ) -> pxt.Array[(None,), float32] ``` Generate text or image embeddings using Amazon Titan, Amazon Nova, or Cohere embedding models. Calls the AWS Bedrock `invoke_model` API for embedding models. For additional details, see: [https://docs.aws.amazon.com/bedrock/latest/userguide/titan-embedding-models.html](https://docs.aws.amazon.com/bedrock/latest/userguide/titan-embedding-models.html) [https://docs.aws.amazon.com/nova/latest/userguide/modality-embedding.html](https://docs.aws.amazon.com/nova/latest/userguide/modality-embedding.html) [https://docs.aws.amazon.com/bedrock/latest/userguide/model-parameters-embed.html](https://docs.aws.amazon.com/bedrock/latest/userguide/model-parameters-embed.html) **Parameters:** * **`text`** (`String`): Input text to embed. * **`model_id`** (`String`): The embedding model identifier. Supported models: * `amazon.titan-embed-text-v1` * `amazon.titan-embed-text-v2:0` (supports `dimensions`: 256, 512, 1024) * `amazon.nova-2-multimodal-embeddings-v1:0` (supports `dimensions`: 256, 512, 1024, 3072) * `cohere.embed-english-v3` * `cohere.embed-multilingual-v3` * `cohere.embed-v4:0` (supports `dimensions`: 256, 512, 1024, 1536) * **`dimensions`** (`Int | None`, default: `Literal(None)`): Output embedding dimensions (model-dependent, optional). **Returns:** * `pxt.Array[(None,), float32]`: Embedding vector. **Examples:** Create an embedding index on a column `description` with Nova embeddings and custom dimensions: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} tbl.add_embedding_index( tbl.description, string_embed=embed.using( model_id='amazon.nova-2-multimodal-embeddings-v1:0', dimensions=1024, ), ) ``` ## udf  invoke\_model() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf invoke_model(body: pxt.Json, *, model_id: pxt.String) -> pxt.Json ``` Invoke a Bedrock model. Equivalent to the AWS Bedrock `invoke_model` API endpoint, with automatic routing to `StartAsyncInvoke` for models that require it (e.g. video generation, audio/video embeddings). For additional details, see: [https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/bedrock-runtime/client/invoke\_model.html](https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/bedrock-runtime/client/invoke_model.html) PIL images and media file paths anywhere in the request body are converted automatically to the encoding expected by the target model. For image-generation models, base64-encoded images in the response are automatically decoded into `PIL.Image` objects. For models that require async invocation, `bedrock.temp_location` must be configured (set environment variable `BEDROCK_TEMP_LOCATION` or add `temp_location` to the `[bedrock]` section of your Pixeltable configuration file). **Parameters:** * **`body`** (`pxt.Json`): The prompt and inference parameters as a dictionary. * **`model_id`** (`pxt.String`): The model identifier to invoke. **Returns:** * `pxt.Json`: A dictionary containing the model response. For image-generation models, image fields are decoded to `PIL.Image` objects. For video-generation models, returns a `pxt.Video` path. **Examples:** Invoke Amazon Titan text embeddings: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} body = {'inputText': tbl.text, 'dimensions': 512, 'normalize': True} tbl.add_computed_column( response=invoke_model(body, model_id='amazon.titan-embed-text-v2:0') ) ``` Invoke TwelveLabs Marengo with an image column (note that the image can be included directly in the invoke body, and will be automatically base64-encoded by Pixeltable): ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} body = { 'inputType': 'image', 'image': {'mediaSource': {'base64String': tbl.image}}, } tbl.add_computed_column( response=invoke_model( body, model_id='twelvelabs.marengo-embed-3-0-v1:0' ) ) ``` Invoke TwelveLabs Marengo with audio (auto-routes to async via StartAsyncInvoke; requires `bedrock.temp_location` to be configured): ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} body = { 'inputType': 'audio', 'audio': {'mediaSource': {'base64String': tbl.audio}}, } tbl.add_computed_column( response=invoke_model( body, model_id='twelvelabs.marengo-embed-3-0-v1:0' ) ) ``` Invoke Anthropic Claude with an image: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} body = { 'anthropic_version': 'bedrock-2023-05-31', 'max_tokens': 1024, 'messages': [ { 'role': 'user', 'content': [ { 'type': 'image', 'source': { 'type': 'base64', 'media_type': 'image/jpeg', 'data': tbl.image, }, }, {'type': 'text', 'text': "What's in this image?"}, ], } ], } tbl.add_computed_column( response=invoke_model( body, model_id='anthropic.claude-3-haiku-20240307-v1:0' ) ) ``` Invoke Amazon Nova Lite with a video column: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} body = { 'messages': [ { 'role': 'user', 'content': [ { 'video': { 'format': 'mp4', 'source': {'bytes': tbl.video}, } }, {'text': 'What happens in this video?'}, ], } ] } tbl.add_computed_column( response=invoke_model(body, model_id='amazon.nova-lite-v1:0') ) ``` Invoke Stability AI for image generation: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} body = { 'prompt': tbl.prompt, 'mode': 'text-to-image', 'aspect_ratio': '1:1', 'output_format': 'jpeg', } tbl.add_computed_column( response=invoke_model(body, model_id='stability.sd3-5-large-v1:0') ) tbl.add_computed_column(image=tbl.response['images'][0]) ``` Invoke Amazon Nova Reel for video generation (auto-routes to async; requires `bedrock.temp_location`): ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} body = { 'taskType': 'TEXT_VIDEO', 'textToVideoParams': {'text': tbl.prompt}, 'videoGenerationConfig': {'durationSeconds': 6, 'fps': 24}, } tbl.add_computed_column( video=invoke_model(body, model_id='amazon.nova-reel-v1:1') ) ``` # bfl Source: https://docs.pixeltable.com/sdk/latest/bfl View Source on GitHub # module  pixeltable.functions.bfl Pixeltable [UDFs](https://docs.pixeltable.com/platform/udfs-in-pixeltable) that wrap [Black Forest Labs (BFL)](https://docs.bfl.ai/) FLUX image generation API. In order to use them, the API key must be specified either with `BFL_API_KEY` environment variable, or as `api_key` in the `bfl` section of the Pixeltable config file. For more information on FLUX models, see the [BFL documentation](https://docs.bfl.ai/). ## udf  edit() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf edit( prompt: pxt.String, image: pxt.Image, *, model: pxt.String, reference_images: pxt.Json[(Image, ...)] | None = None, width: pxt.Int | None = None, height: pxt.Int | None = None, seed: pxt.Int | None = None, safety_tolerance: pxt.Int | None = None, output_format: pxt.String | None = None, steps: pxt.Int | None = None, guidance: pxt.Float | None = None ) -> pxt.Image ``` Edit an image using FLUX models with text prompts and optional reference images. This UDF wraps the BFL FLUX image editing API. For more information, refer to the official [API documentation](https://docs.bfl.ai/flux_2/flux2_image_editing). **Parameters:** * **`prompt`** (`pxt.String`): Text description of the edit to apply. * **`image`** (`pxt.Image`): The base image to edit. * **`model`** (`pxt.String`): The FLUX model to use for editing. See available models at [https://docs.bfl.ai/](https://docs.bfl.ai/). * **`reference_images`** (`pxt.Json[(Image`): Additional reference images (up to 7) for multi-reference editing. * **`width`** (`Any`): Output width in pixels (multiple of 16). Matches input if not specified. * **`height`** (`Any`): Output height in pixels (multiple of 16). Matches input if not specified. * **`seed`** (`Any`): Random seed for reproducible results. * **`safety_tolerance`** (`Any`): Moderation level from 0 (strict) to 6 (permissive). Default 2. * **`output_format`** (`Any`): Image format, 'jpeg' or 'png'. Default 'jpeg'. * **`steps`** (`Any`): Number of inference steps (flux-2-flex only, max 50). * **`guidance`** (`Any`): Guidance scale 1.5-10 (flux-2-flex only). Default 4.5. **Returns:** * `pxt.Image`: An edited PIL Image. **Examples:** Edit an image to change its background: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} t.add_computed_column( edited=bfl.edit( 'Change the background to a sunset beach', t.original_image, model='flux-2-pro', ) ) ``` Multi-reference editing with additional images: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} t.add_computed_column( edited=bfl.edit( 'Combine the person from the first image with the background from the second', t.person_image, model='flux-kontext-pro', reference_images=[t.background_image], ) ) ``` ## udf  expand() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf expand( prompt: pxt.String, image: pxt.Image, *, model: pxt.String, top: pxt.Int = 0, bottom: pxt.Int = 0, left: pxt.Int = 0, right: pxt.Int = 0, seed: pxt.Int | None = None, safety_tolerance: pxt.Int | None = None, output_format: pxt.String | None = None ) -> pxt.Image ``` Expand an image by adding pixels on any side using FLUX Expand models. Outpaint an image by specifying how many pixels to add to each edge. The expansion maintains context from the original image. This UDF wraps the BFL FLUX Expand API. For more information, refer to the official [API documentation](https://docs.bfl.ai/flux_tools/flux_1_expand). **Parameters:** * **`prompt`** (`pxt.String`): Text description to guide the expansion. * **`image`** (`pxt.Image`): The base image to expand. * **`model`** (`pxt.String`): The FLUX Expand model to use. See available models at [https://docs.bfl.ai/](https://docs.bfl.ai/). * **`top`** (`pxt.Int`): Pixels to add to the top edge. * **`bottom`** (`pxt.Int`): Pixels to add to the bottom edge. * **`left`** (`pxt.Int`): Pixels to add to the left edge. * **`right`** (`pxt.Int`): Pixels to add to the right edge. * **`seed`** (`pxt.Int | None`): Random seed for reproducible results. * **`safety_tolerance`** (`pxt.Int | None`): Moderation level from 0 (strict) to 6 (permissive). Default 2. * **`output_format`** (`pxt.String | None`): Image format, 'jpeg' or 'png'. Default 'jpeg'. **Returns:** * `pxt.Image`: An expanded PIL Image. **Examples:** Expand an image to create a wider landscape: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} t.add_computed_column( wide=bfl.expand( 'Continue the landscape scenery', t.original_image, model='flux-pro-1.0-expand', left=256, right=256, ) ) ``` ## udf  fill() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf fill( prompt: pxt.String, image: pxt.Image, mask: pxt.Image, *, model: pxt.String, steps: pxt.Int | None = None, guidance: pxt.Float | None = None, seed: pxt.Int | None = None, safety_tolerance: pxt.Int | None = None, output_format: pxt.String | None = None ) -> pxt.Image ``` Inpaint an image using FLUX Fill models. Fill specified areas of an image based on a mask and text prompt. The mask can be a separate image or applied to the alpha channel of the input image. This UDF wraps the BFL FLUX Fill API. For more information, refer to the official [API documentation](https://docs.bfl.ai/flux_tools/flux_1_fill). **Parameters:** * **`prompt`** (`pxt.String`): Text description of what to fill in the masked area. * **`image`** (`pxt.Image`): The base image to inpaint. * **`mask`** (`pxt.Image`): Mask image where white areas indicate regions to fill (black areas preserved). * **`model`** (`pxt.String`): The FLUX Fill model to use. See available models at [https://docs.bfl.ai/](https://docs.bfl.ai/). * **`steps`** (`pxt.Int | None`): Number of inference steps (max 50). Default 50. * **`guidance`** (`pxt.Float | None`): Guidance scale for generation. Default 30. * **`seed`** (`pxt.Int | None`): Random seed for reproducible results. * **`safety_tolerance`** (`pxt.Int | None`): Moderation level from 0 (strict) to 6 (permissive). Default 2. * **`output_format`** (`pxt.String | None`): Image format, 'jpeg' or 'png'. Default 'jpeg'. **Returns:** * `pxt.Image`: An inpainted PIL Image. **Examples:** Fill a masked region with generated content: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} t.add_computed_column( filled=bfl.fill( 'A beautiful garden with flowers', t.original_image, t.mask_image, model='flux-pro-1.0-fill', ) ) ``` ## udf  generate() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf generate( prompt: pxt.String, *, model: pxt.String, width: pxt.Int | None = None, height: pxt.Int | None = None, seed: pxt.Int | None = None, safety_tolerance: pxt.Int | None = None, output_format: pxt.String | None = None, steps: pxt.Int | None = None, guidance: pxt.Float | None = None ) -> pxt.Image ``` Generate an image from a text prompt using FLUX models. This UDF wraps the BFL FLUX API endpoints. For more information, refer to the official [API documentation](https://docs.bfl.ai/flux_2/flux2_text_to_image). **Parameters:** * **`prompt`** (`pxt.String`): Text description of the image to generate. * **`model`** (`pxt.String`): The FLUX model to use. See available models at [https://docs.bfl.ai/](https://docs.bfl.ai/). * **`width`** (`pxt.Int | None`): Output width in pixels (multiple of 16). Default 1024. * **`height`** (`pxt.Int | None`): Output height in pixels (multiple of 16). Default 1024. * **`seed`** (`pxt.Int | None`): Random seed for reproducible results. * **`safety_tolerance`** (`pxt.Int | None`): Moderation level from 0 (strict) to 6 (permissive). Default 2. * **`output_format`** (`pxt.String | None`): Image format, 'jpeg' or 'png'. Default 'jpeg'. * **`steps`** (`pxt.Int | None`): Number of inference steps (flux-2-flex only, max 50). * **`guidance`** (`pxt.Float | None`): Guidance scale 1.5-10 (flux-2-flex only). Default 4.5. **Returns:** * `pxt.Image`: A generated PIL Image. **Examples:** Generate images using default dimensions: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} t.add_computed_column(image=bfl.generate(t.prompt, model='flux-2-pro')) ``` Generate with custom dimensions: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} t.add_computed_column( image=bfl.generate( t.prompt, model='flux-2-pro', width=1920, height=1080 ) ) ``` Generate with specific seed for reproducibility: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} t.add_computed_column( image=bfl.generate(t.prompt, model='flux-2-pro', seed=42) ) ``` # CellError Source: https://docs.pixeltable.com/sdk/latest/cellerror View Source on GitHub # class  pixeltable.CellError Error info for a cell. ## attr  errormsg ``` errormsg: str ``` ⚠️ **No documentation** ## attr  errortype ``` errortype: str ``` ⚠️ **No documentation** # CodecContextMetadata Source: https://docs.pixeltable.com/sdk/latest/codeccontextmetadata View Source on GitHub # class  pixeltable.functions.CodecContextMetadata Metadata about a stream's codec. ## attr  channels ``` channels: int | None ``` Number of audio channels. Present only for audio streams. ## attr  codec\_tag ``` codec_tag: str ``` Four-character codec tag, unicode-escaped. ## attr  name ``` name: str ``` Codec name (e.g. `'h264'`, `'aac'`). ## attr  pix\_fmt ``` pix_fmt: str | None ``` Pixel format (e.g. `'yuv420p'`). Present only for video streams. ## attr  profile ``` profile: str | None ``` Codec profile (e.g. `'High'`, `'LC'`), or `None` if unavailable. # ColumnMetadata Source: https://docs.pixeltable.com/sdk/latest/columnmetadata View Source on GitHub # class  pixeltable.ColumnMetadata Metadata for a column of a Pixeltable table. ## attr  comment ``` comment: str | None ``` User-provided column comment. ## attr  computed\_with ``` computed_with: str | None ``` Expression used to compute this column; `None` if this is not a computed column. ## attr  custom\_metadata ``` custom_metadata: Any ``` User-defined JSON metadata for this column, if any. ## attr  defined\_in ``` defined_in: str | None ``` Name of the table where this column was originally defined. If the current table is a view, then `defined_in` may differ from the current table name. ## attr  depends\_on ``` depends_on: list[tuple[str, str]] | None ``` List of dependencies (table name, column name) if this is a computed column, else `None`. ## attr  destination ``` destination: str | None ``` The object store destination defined for this column's computed files, if it defines one. An instance-wide default destination is not reported here, because it is configuration rather than part of the schema. A destination defined as a config var reads as `$` rather than as the location it currently resolves to; see [`ConfigVar`](./configvar). ## attr  is\_builtin ``` is_builtin: bool | None ``` If False, this computed column makes calls to custom UDFs; `None` if this is not a computed column. ## attr  is\_computed ``` is_computed: bool ``` `True` if this column is a computed column. ## attr  is\_iterator\_col ``` is_iterator_col: bool ``` `True` if this column is produced by an iterator (only applicable to views). ## attr  is\_primary\_key ``` is_primary_key: bool ``` `True` if this column is part of the table's primary key. ## attr  is\_stored ``` is_stored: bool ``` `True` if this is a stored column; `False` if it is dynamically computed. ## attr  media\_validation ``` media_validation: Literal['on_read', 'on_write'] | None ``` The media validation policy for this column. `None` if the type of this column is not a media type. ## attr  name ``` name: str ``` The name of the column. ## attr  type\_ ``` type_: str ``` The type specifier of the column. ## attr  version\_added ``` version_added: int ``` The table version when this column was added. # ColumnRef Source: https://docs.pixeltable.com/sdk/latest/columnref View Source on GitHub # class  pixeltable.exprs.ColumnRef A Pixeltable expression that references a column of a table. A `ColumnRef` is created by column access on a [`Table`](./table), such as `t.col`. Not thread-safe. ## method  embedding() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} embedding(*, idx: str | None = None) -> ColumnRef ``` Return a reference to the values of an embedding index on this column. If an embedding index is defined on a column, the usual way to use that index is via a [`similarity()`](./columnref#method-similarity) lookup. Sometimes it is also useful to directly access the index values (i.e., the embedding vectors themselves). Calling `embedding()` returns a new `ColumnRef` expression of type `pxt.Array[(dim,), prec]`, where `dim` and `prec` are the dimensionality and precision of the column's embedding index. If there is more than one embedding index defined on this column, then the `idx` parameter must be provided to specify which index to reference. If there is only one index, then `idx` is optional. Args: idx: An optional embedding index name. *Required* if there is more than one embedding index defined on this column. Returns: A new `ColumnRef` referencing the values of the specified embedding index on this column. Raises: `pxt.Error` if there is no embedding index defined on this column, if `idx` is not provided when there are multiple embedding indices, or if `idx` does not match any embedding index defined on this column. Examples: All of these examples assume that `t` is a table with an image column `t.image`. Add an embedding index to `t.image` using the `clip()` embedding (this only needs to be done once): ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} from pixeltable.functions.huggingface import clip t.add_embedding_index( t.image, clip.using(model_id='openai/clip-vit-base-patch32') ) ``` Reference the embedding index values directly: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} t.select(t.image, t.image.embedding()) ``` ## method  similarity() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} similarity( item: Any = None, *, string: str | None = None, image: str | PIL.Image.Image | None = None, audio: str | None = None, video: str | None = None, document: str | None = None, vector: np.ndarray | None = None, idx: str | None = None, _tbl_path: catalog.TablePath | None = None ) -> Expr ``` Return a new expression representing the similarity score between the values of this column and the given (constant) item. In order for this to work, there must be an embedding index defined on this column that supports the modality of the given item (string, image, audio, video, document). Similarity will be scored according to the metric defined by the embedding index. Exactly one of `string`, `image`, `audio`, `video`, `document`, or `vector` must be provided. The `item` parameter is deprecated and exists for backward compatibility only. If `string`, `image`, `audio`, `video`, or `document` is provided, then an embedding vector will be computed for the given input as defined by the embedding index and used to determine similarity. If `vector` is provided, then it must be a 1-dimensional array of the same dimensionality as the index, and similarity will be determined directly against the vector. The optional `idx` parameter specifies the name of the embedding index to use. If there is more than one embedding index defined on this column, then `idx` *must* be provided. **Parameters:** * **`string`** (`str | None`): A string to compare against the values of this column. * **`image`** (`str | PIL.Image.Image | None`): An image to compare against the values of this column (either a local file path, a URL, or an in-memory `PIL.Image.Image`). * **`audio`** (`str | None`): An audio file to compare against the values of this column (a local file path or a URL). * **`video`** (`str | None`): A video file to compare against the values of this column (a local file path or a URL). * **`document`** (`str | None`): A document file to compare against the values of this column (a local file path or a URL). * **`vector`** (`np.ndarray | None`): A 1-dimensional NumPy array to compare against the values of this column. * **`idx`** (`str | None`): An optional embedding index name. *Required* if there is more than one embedding index defined on this column. * **`item`** (`Any`): **Deprecated** as of version 0.5.7. **Returns:** * `Expr`: A new expression representing the similarity score between the values of this column and the given item. **Examples:** All of these examples assume that `t` is a table with an image column `t.image`. Add an embedding index to `t.image` using the `clip()` embedding (this only needs to be done once): ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} from pixeltable.functions.huggingface import clip t.add_embedding_index( t.image, clip.using(model_id='openai/clip-vit-base-patch32') ) ``` Do a nearest neighbor search against a string (with `k=5`): ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} sim = t.image.similarity(string='a photograph of a cat') t.select(t.image, sim).order_by(sim, asc=False).head(5) ``` Do a nearest neighbor search against an image: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} sim = t.image.similarity(image='https://example.com/reference-cat.jpg') t.select(t.image, sim).order_by(sim, asc=False).head(5) ``` # ColumnSpec Source: https://docs.pixeltable.com/sdk/latest/columnspec View Source on GitHub # class  pixeltable.types.ColumnSpec Column specification, a dictionary representation of a column's schema. Exactly one of `type` or `value` must be included in the dictionary. ## attr  comment ``` comment: str ``` Optional comment for the column. Displayed in .describe() output. ## attr  custom\_metadata ``` custom_metadata: Any ``` User-defined metadata to associate with the column. ## attr  destination ``` destination: str | Path | ConfigVar[URI] ``` Destination for storing computed output files. Only applicable for computed columns. Can be: * A local pathname (such as `path/to/outputs/`), or * The URI of an object store (such as `s3://my-bucket/outputs/`). ## attr  media\_validation ``` media_validation: Literal['on_read', 'on_write'] ``` When to validate media; `'on_read'` or `'on_write'`. ## attr  primary\_key ``` primary_key: bool ``` Whether this column is part of the primary key. Defaults to `False`. ## attr  stored ``` stored: bool ``` Whether to store the column data. Defaults to `True`. ## attr  type ``` type: TypeForm ``` The column type (e.g., `pxt.Image`, `str`). Required unless `value` is specified. ## attr  value ``` value: 'exprs.Expr' ``` A Pixeltable expression for computed columns. Mutually exclusive with `type`. # ConfigVar Source: https://docs.pixeltable.com/sdk/latest/configvar View Source on GitHub # class  pixeltable.ConfigVar A reference to a database variable, defined at module scope. A definition names the variable; the target it is applied to supplies the value. Define a variable and apply it to a column: > > > MEDIA\_DEST = pxt.ConfigVar('media\_dest', pxt.URI) > > > ... > > > ... > > > ... class Videos(TableModel, name='videos'): > > > ... clip = pxt.Column(value=..., destination=MEDIA\_DEST) Code that runs on the target reads the bound value with `value()`. ## method  value() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} value() -> ConfVarT ``` The bound value, converted to the declared type. Raises if the target has no binding for it. **Examples:** Read a var from a udf, which runs on the target: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf def summarize(text: str) -> str: return _call(text, dest=MEDIA_DEST.value()) ``` # ContainerMetadata Source: https://docs.pixeltable.com/sdk/latest/containermetadata View Source on GitHub # class  pixeltable.functions.ContainerMetadata Metadata for a media container, as returned by [`audio.get_metadata()`](./audio#func-get_metadata) or [`video.get_metadata()`](./video#func-get_metadata). ## attr  bit\_exact ``` bit_exact: bool ``` Whether the container was opened in bit-exact mode. ## attr  bit\_rate ``` bit_rate: int | None ``` Overall bit rate of the container in bits per second, or `None` if unknown. ## attr  metadata ``` metadata: dict[str, str] ``` Additional container-level metadata tags (e.g. title, encoder). ## attr  size ``` size: int | None ``` Size of the container in bytes, or `None` if unknown. ## attr  streams ``` streams: list[StreamMetadata] ``` Per-stream metadata for each stream in the container. # date Source: https://docs.pixeltable.com/sdk/latest/date View Source on GitHub # module  pixeltable.functions.date Pixeltable UDFs for `DateType`. Usage example: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} import pixeltable as pxt t = pxt.get_table(...) t.select(t.date_col.year, t.date_col.weekday()).collect() ``` ## udf  add\_days() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf add_days(self: pxt.Date, n: pxt.Int) -> pxt.Date ``` Add `n` days to the date. Equivalent to [`date + timedelta(days=n)`](https://docs.python.org/3/library/datetime.html#datetime.timedelta). ## udf  day() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf day(self: pxt.Date) -> pxt.Int ``` Between 1 and the number of days in the given month of the given year. Equivalent to [`date.day`](https://docs.python.org/3/library/datetime.html#datetime.date.day). ## udf  isocalendar() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf isocalendar(self: pxt.Date) -> IsoCalendar ``` Return a dictionary with three entries: `'year'`, `'week'`, and `'weekday'`. Equivalent to [`date.isocalendar()`](https://docs.python.org/3/library/datetime.html#datetime.date.isocalendar). ## udf  isoformat() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf isoformat( self: pxt.Date, sep: pxt.String = 'T', timespec: pxt.String = 'auto' ) -> pxt.String ``` Return a string representing the date and time in ISO 8601 format. Equivalent to [`date.isoformat()`](https://docs.python.org/3/library/datetime.html#datetime.date.isoformat). **Parameters:** * **`sep`** (`pxt.String`): Separator between date and time. * **`timespec`** (`pxt.String`): The number of additional terms in the output. See the [`date.isoformat()`](https://docs.python.org/3/library/datetime.html#datetime.date.isoformat) documentation for more details. ## udf  isoweekday() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf isoweekday(self: pxt.Date) -> pxt.Int ``` Return the day of the week as an integer, where Monday is 1 and Sunday is 7. Equivalent to [`date.isoweekday()`](https://docs.python.org/3/library/datetime.html#datetime.date.isoweekday). ## udf  make\_date() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf make_date(year: pxt.Int, month: pxt.Int, day: pxt.Int) -> pxt.Date ``` Create a date. Equivalent to [`datetime()`](https://docs.python.org/3/library/datetime.html#datetime.date). ## udf  month() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf month(self: pxt.Date) -> pxt.Int ``` Between 1 and 12 inclusive. Equivalent to [`date.month`](https://docs.python.org/3/library/datetime.html#datetime.date.month). ## udf  strftime() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf strftime(self: pxt.Date, format: pxt.String) -> pxt.String ``` Return a string representing the date and time, controlled by an explicit format string. Equivalent to [`date.strftime()`](https://docs.python.org/3/library/datetime.html#datetime.date.strftime). **Parameters:** * **`format`** (`pxt.String`): The format string to control the output. For a complete list of formatting directives, see [`strftime()` and `strptime()` Behavior](https://docs.python.org/3/library/datetime.html#strftime-and-strptime-behavior). ## udf  toordinal() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf toordinal(self: pxt.Date) -> pxt.Int ``` Return the proleptic Gregorian ordinal of the date, where January 1 of year 1 has ordinal 1. Equivalent to [`date.toordinal()`](https://docs.python.org/3/library/datetime.html#datetime.date.toordinal). ## udf  weekday() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf weekday(self: pxt.Date) -> pxt.Int ``` Between 0 (Monday) and 6 (Sunday) inclusive. Equivalent to [`date.weekday()`](https://docs.python.org/3/library/datetime.html#datetime.date.weekday). ## udf  year() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf year(self: pxt.Date) -> pxt.Int ``` Between 1 and 9999 inclusive. (Between [`MINYEAR`](https://docs.python.org/3/library/datetime.html#datetime.MINYEAR) and [`MAXYEAR`](https://docs.python.org/3/library/datetime.html#datetime.MAXYEAR) as defined by the Python `datetime` library). Equivalent to [`date.year`](https://docs.python.org/3/library/datetime.html#datetime.date.year). # deepseek Source: https://docs.pixeltable.com/sdk/latest/deepseek View Source on GitHub # module  pixeltable.functions.deepseek Pixeltable UDFs for Deepseek AI models. Provides integration with Deepseek's language models for chat completions and other AI capabilities. ## udf  chat\_completions() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf chat_completions( messages: pxt.Json[(Json, ...)], *, model: pxt.String, model_kwargs: pxt.Json | None = None, tools: pxt.Json[(Json, ...)] | None = None, tool_choice: pxt.Json | None = None ) -> pxt.Json ``` Creates a model response for the given chat conversation. Equivalent to the Deepseek `chat/completions` API endpoint. For additional details, see: [https://api-docs.deepseek.com/api/create-chat-completion](https://api-docs.deepseek.com/api/create-chat-completion) Deepseek uses the OpenAI SDK, so you will need to install the `openai` package to use this UDF. Request throttling: Applies the rate limit set in the config (section `deepseek`, key `rate_limit`). If no rate limit is configured, uses a default of 600 RPM. **Requirements:** * `pip install openai` **Parameters:** * **`messages`** (`pxt.Json[(Json`): A list of messages to use for chat completion, as described in the Deepseek API documentation. * **`model`** (`Any`): The model to use for chat completion. * **`model_kwargs`** (`Any`): Additional keyword args for the Deepseek `chat/completions` API. For details on the available parameters, see: [https://api-docs.deepseek.com/api/create-chat-completion](https://api-docs.deepseek.com/api/create-chat-completion) * **`tools`** (`Any`): An optional list of Pixeltable tools to use for the request. * **`tool_choice`** (`Any`): An optional tool choice configuration. **Returns:** * `pxt.Json`: A dictionary containing the response and other metadata. **Examples:** Add a computed column that applies the model `deepseek-v4-flash` to an existing Pixeltable column `tbl.prompt` of the table `tbl`: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} messages = [ {'role': 'system', 'content': 'You are a helpful assistant.'}, {'role': 'user', 'content': tbl.prompt}, ] tbl.add_computed_column( response=chat_completions(messages, model='deepseek-v4-flash') ) ``` # DirContents Source: https://docs.pixeltable.com/sdk/latest/dircontents View Source on GitHub # class  pixeltable.DirContents Represents the contents of a Pixeltable directory. ## attr  dirs ``` dirs: list[str] ``` List of directory paths contained in this directory. ## attr  tables ``` tables: list[str] ``` List of table paths contained in this directory. # DirectoryNode Source: https://docs.pixeltable.com/sdk/latest/directorynode View Source on GitHub # class  pixeltable.DirectoryNode A directory entry in a [`TreeNode`](./treenode) tree. ## attr  entries ``` entries: list['TreeNode'] ``` ⚠️ **No documentation** ## attr  kind ``` kind: Literal['directory'] ``` ⚠️ **No documentation** ## attr  name ``` name: str ``` ⚠️ **No documentation** ## attr  path ``` path: str ``` ⚠️ **No documentation** # document Source: https://docs.pixeltable.com/sdk/latest/document View Source on GitHub # module  pixeltable.functions.document Pixeltable UDFs for `DocumentType`. ## iterator  document\_splitter() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.iterator document_splitter( document: pxt.Document, separators: pxt.String, *, elements: pxt.Json[(String, ...)] | None = None, limit: pxt.Int | None = None, overlap: pxt.Int | None = None, metadata: pxt.String = '', skip_tags: pxt.Json[(String, ...)] | None = None, spacy_model: pxt.String = 'en_core_web_sm', tiktoken_encoding: pxt.String | None = 'cl100k_base', tiktoken_target_model: pxt.String | None = None, image_dpi: pxt.Int = 300, image_format: pxt.String = 'png' ) ``` Iterator over chunks of a document. The document is chunked according to the specified `separators`. Chunked text will be cleaned with `ftfy.fix_text` to fix up common problems with unicode sequences. **Outputs**: One row per chunk, with the following columns, depending on the specified `elements` and `metadata`: * `text` (`pxt.String`): The text of the chunk. Present if `'text'` is specified in `elements`. * `image` (`pxt.Image`): The image extracted from the chunk. Present if `'image'` is specified in `elements`. * `title` (`pxt.String | None`): The document title. Present if `'title'` is specified in `metadata`. * `heading` (`Heading | None`): The heading hierarchy at the start of the chunk, with the text of each currently open heading level under the keys `h1` through `h6` (HTML and Markdown only). Present if `'heading'` is specified in `metadata`. * `sourceline` (`pxt.Int | None`): The source line number of the start of the chunk (HTML only). Present if `'sourceline'` is specified in `metadata`. * `page` (`pxt.Int | None`): The page number of the chunk (PDF only). Present if `'page'` is specified in `metadata`. * `bounding_box` (`BoundingBox | None`): The bounding box of the chunk on the page, with float entries `x1`, `y1`, `x2`, `y2` (PDF only). Present if `'bounding_box'` is specified in `metadata`. **Parameters:** * **`separators`** (`pxt.String`): separators to use to chunk the document. Options are: `'heading'`, `'paragraph'`, `'sentence'`, `'token_limit'`, `'char_limit'`, `'page'`. This may be a comma-separated string, e.g., `'heading,token_limit'`. * **`elements`** (`pxt.Json[(String`): list of elements to extract from the document. Options are: `'text'`, `'image'`. Defaults to `['text']` if not specified. The `'image'` element is only supported for the `'page'` separator on PDF documents. * **`limit`** (`Any`): the maximum number of tokens or characters in each chunk, if `'token_limit'` or `'char_limit'` is specified. * **`metadata`** (`Any`): additional metadata fields to include in the output. Options are: `'title'`, `'heading'` (HTML and Markdown), `'sourceline'` (HTML), `'page'` (PDF), `'bounding_box'` (PDF). The input may be a comma-separated string, e.g., `'title,heading,sourceline'`. * **`skip_tags`** (`Any`): list of HTML tags to skip when processing HTML documents. * **`spacy_model`** (`Any`): Name of the spaCy model to use for sentence segmentation. This parameter is ignored unless the `'sentence'` separator is specified. * **`tiktoken_encoding`** (`Any`): Name of the tiktoken encoding to use when counting tokens. This parameter is ignored unless the `'token_limit'` separator is specified. * **`tiktoken_target_model`** (`Any`): Name of the target model to use when counting tokens with tiktoken. If specified, this parameter overrides `tiktoken_encoding`. This parameter is ignored unless the `'token_limit'` separator is specified. * **`image_dpi`** (`Any`): DPI to use when extracting images from PDFs. Defaults to 300. * **`image_format`** (`Any`): format to use when extracting images from PDFs. Defaults to 'png'. **Examples:** All these examples assume an existing table `tbl` with a column `doc` of type `pxt.Document`. Create a view that splits all documents into chunks of up to 300 tokens: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} pxt.create_view( 'chunks', tbl, iterator=document_splitter( tbl.doc, separators='token_limit', limit=300 ), ) ``` Create a view that splits all documents along sentence boundaries, including title and heading metadata: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} pxt.create_view( 'sentence_chunks', tbl, iterator=document_splitter( tbl.doc, separators='sentence', metadata='title,heading' ), ) ``` # Expr Source: https://docs.pixeltable.com/sdk/latest/expr View Source on GitHub # class  pixeltable.exprs.Expr A Pixeltable expression. All Pixeltable expressions, including [column references](./columnref) (such as `t.col`), UDF calls (`t.my_string.lower()`), and compound expressions (`t.col + 5`) are instances of this class. Not thread-safe: Expr and subclasses contain execution state and are never thread-safe. ## method  astype() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} astype(new_type: ts.ColumnType | TypeForm) -> exprs.TypeCast ``` Return a new expression that casts this expression to a different type. This represents a type *cast*, not a type *coercion*, so it will not mutate the underlying data; it simply changes the static type given by the expression. **Parameters:** * **`new_type`** (`ts.ColumnType | TypeForm`): The type to cast to. **Examples:** Given an existing column `t.json_col` of type `pxt.Json`, cast that column to type `pxt.String`: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} t.json_col.astype(pxt.String) ``` This will assume that all values in `t.json_col` are *actually* strings, and it will result in an error at runtime if there are values in `t.json_col` that are not strings. It will *not* convert those values to a string representation. (For that, use the \[`dumps()`]\[pixeltable.functions.json.dumps] UDF instead.) ## method  isin() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} isin(value_set: Any) -> exprs.InPredicate ``` Return a new, boolean-valued expression that is `True` whenever this expression is in `value_set`. **Parameters:** * **`value_set`** (`Any`): Either another expression that evaluates to a set of values, or a constant collection of values. If the latter, can be any `Iterable`. **Examples:** These examples assume that `t` is a table with a column `int_col` of type `pxt.Int`, and another column `list_col` of type `pxt.Json`, containing lists of integers. Select all rows where `int_col` is in the constant set `{1, 3, 22}`: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} t.where(t.int_col.isin({1, 3, 22})).select() ``` Select all rows where `int_col` is in the set of values in that row's `list_col`: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} t.where(t.int_col.isin(t.list_col)).select() ``` # fabric Source: https://docs.pixeltable.com/sdk/latest/fabric View Source on GitHub # module  pixeltable.functions.fabric Pixeltable UDFs that wrap Azure OpenAI endpoints via Microsoft Fabric. These functions provide seamless access to Azure OpenAI models within Microsoft Fabric notebook environments. Authentication and endpoint discovery are handled automatically using Fabric's built-in service discovery and token utilities. **Note:** These functions only work within Microsoft Fabric notebook environments. For more information on Fabric AI services, see: [https://learn.microsoft.com/en-us/fabric/data-science/ai-services/ai-services-overview](https://learn.microsoft.com/en-us/fabric/data-science/ai-services/ai-services-overview) ## udf  chat\_completions() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf chat_completions( messages: pxt.Json[(Json, ...)], *, model: pxt.String, api_version: pxt.String | None = None, model_kwargs: pxt.Json | None = None ) -> pxt.Json ``` Creates a model response for the given chat conversation using Azure OpenAI in Fabric. Equivalent to the Azure OpenAI `chat/completions` API endpoint. For additional details, see: [https://learn.microsoft.com/en-us/azure/ai-services/openai/reference](https://learn.microsoft.com/en-us/azure/ai-services/openai/reference) **Automatic authentication:** Authentication is handled automatically in Fabric notebooks using token-based authentication. No API keys are required. **Supported models in Fabric:** * `gpt-5` (reasoning model) * `gpt-4.1` * `gpt-4.1-mini` Request throttling: Applies the rate limit set in the config (section `fabric.rate_limits`, key `chat`). If no rate limit is configured, uses a default of 600 RPM. **Requirements:** * Microsoft Fabric notebook environment * `synapse-ml-fabric` package (pre-installed in Fabric) **Parameters:** * **`messages`** (`pxt.Json[(Json`): A list of message dicts with 'role' and 'content' keys, as described in the Azure OpenAI API documentation. * **`model`** (`Any`): The deployment name to use (e.g., 'gpt-5', 'gpt-4.1', 'gpt-4.1-mini'). * **`api_version`** (`Any`): Optional API version override. If not specified, defaults to '2025-04-01-preview' for reasoning models (gpt-5) and '2024-02-15-preview' for standard models. * **`model_kwargs`** (`Any`): Additional keyword args for the Azure OpenAI `chat/completions` API. For details on available parameters, see: [https://learn.microsoft.com/en-us/azure/ai-services/openai/reference](https://learn.microsoft.com/en-us/azure/ai-services/openai/reference) **Note:** Reasoning models (gpt-5) use `max_completion_tokens` instead of `max_tokens` and do not support the `temperature` parameter. **Returns:** * `pxt.Json`: A dictionary containing the response and other metadata. **Examples:** Add a computed column that applies the model `gpt-4.1` to an existing Pixeltable column `tbl.prompt` of the table `tbl`: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} from pixeltable.functions import fabric messages = [ {'role': 'system', 'content': 'You are a helpful assistant.'}, {'role': 'user', 'content': tbl.prompt}, ] tbl.add_computed_column( response=fabric.chat_completions(messages, model='gpt-4.1') ) ``` Using a reasoning model (gpt-5): ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} tbl.add_computed_column( reasoning_response=fabric.chat_completions( messages, model='gpt-5', model_kwargs={'max_completion_tokens': 5000}, ) ) ``` ## udf  embeddings() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf embeddings( input: pxt.String, *, model: pxt.String, api_version: pxt.String = '2024-02-15-preview', model_kwargs: pxt.Json | None = None ) -> pxt.Array[(None,), float32] ``` Creates an embedding vector representing the input text using Azure OpenAI in Fabric. Equivalent to the Azure OpenAI `embeddings` API endpoint. For additional details, see: [https://learn.microsoft.com/en-us/azure/ai-services/openai/reference](https://learn.microsoft.com/en-us/azure/ai-services/openai/reference) **Automatic authentication:** Authentication is handled automatically in Fabric notebooks using token-based authentication. No API keys are required. **Supported models in Fabric:** * `text-embedding-ada-002` * `text-embedding-3-small` * `text-embedding-3-large` Request throttling: Applies the rate limit set in the config (section `fabric.rate_limits`, key `embeddings`). If no rate limit is configured, uses a default of 600 RPM. Batches up to 32 inputs per request for efficiency. **Requirements:** * Microsoft Fabric notebook environment * `synapse-ml-fabric` package (pre-installed in Fabric) **Parameters:** * **`input`** (`pxt.String`): The text to embed (automatically batched). * **`model`** (`pxt.String`): The embedding model deployment name * **`api_version`** (`pxt.String`): The API version to use (default: '2024-02-15-preview'). * **`model_kwargs`** (`pxt.Json | None`): Additional keyword args for the Azure OpenAI `embeddings` API. For details on available parameters, see: [https://learn.microsoft.com/en-us/azure/ai-services/openai/reference](https://learn.microsoft.com/en-us/azure/ai-services/openai/reference) **Returns:** * `pxt.Array[(None,), float32]`: An array representing the embedding vector for the input text. **Examples:** Add a computed column that applies the model `text-embedding-3-small` to an existing Pixeltable column `tbl.text` of the table `tbl`: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} from pixeltable.functions import fabric tbl.add_computed_column( embed=fabric.embeddings(tbl.text, model='text-embedding-3-small') ) ``` Add an embedding index to an existing column `text`: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} tbl.add_embedding_index( 'text', embedding=fabric.embeddings.using(model='text-embedding-3-large'), ) ``` # fal Source: https://docs.pixeltable.com/sdk/latest/fal View Source on GitHub # module  pixeltable.functions.fal Pixeltable UDFs that wrap various endpoints from the fal.ai API. In order to use them, you must first `pip install fal-client` and configure your fal.ai credentials, as described in the [Working with fal.ai](https://docs.pixeltable.com/howto/providers/working-with-fal) tutorial. ## udf  run() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf run(input: pxt.Json, *, app: pxt.String) -> pxt.Json ``` Run a model on fal.ai. Uses fal's queue-based subscribe mechanism for reliable execution. For additional details, see: [https://fal.ai/docs](https://fal.ai/docs) Request throttling: Applies the rate limit set in the config (section `fal`, key `rate_limit`). If no rate limit is configured, uses a default of 600 RPM. **Requirements:** * `pip install fal-client` **Parameters:** * **`input`** (`pxt.Json`): The input parameters for the model. * **`app`** (`pxt.String`): The name or ID of the fal.ai application to run (e.g., 'fal-ai/flux/schnell'). **Returns:** * `pxt.Json`: The output of the model as a JSON object. **Examples:** Add a computed column that applies the model `fal-ai/flux/schnell` to an existing Pixeltable column `tbl.prompt` of the table `tbl`: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} input = {'prompt': tbl.prompt} tbl.add_computed_column(response=run(input, app='fal-ai/flux/schnell')) ``` Add a computed column that uses the model `fal-ai/fast-sdxl` to generate images from an existing Pixeltable column `tbl.prompt`: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} input = { 'prompt': tbl.prompt, 'image_size': 'square', 'num_inference_steps': 25, } tbl.add_computed_column(response=run(input, app='fal-ai/fast-sdxl')) tbl.add_computed_column( image=tbl.response['images'][0]['url'].astype(pxt.Image) ) ``` # FastAPIRouter Source: https://docs.pixeltable.com/sdk/latest/fastapirouter View Source on GitHub # class  pixeltable.serving.FastAPIRouter A FastAPI `APIRouter` that exposes Pixeltable table operations as HTTP endpoints. `FastAPIRouter` is for apps that already have a FastAPI server. If you do not have one, use `pxt service` from the CLI; Pixeltable creates and runs the FastAPI app for you. Learn more here: [HTTP Serving](https://docs.pixeltable.com/howto/deployment/serving). ## method  add\_delete\_route() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} add_delete_route( t: RouteTarget, *, path: str, match_columns: ColumnsParam | None = None, background: bool = False ) -> None ``` Add a POST endpoint that deletes rows from `t` matching the given match column values. The request body contains the match column values as JSON fields. The endpoint deletes every row where each match column equals the provided value, and returns the number of rows affected. **Parameters:** * **`t`** (`RouteTarget`): The table to delete from, or the model that defines it. * **`path`** (`str`): The URL path for the endpoint. * **`match_columns`** (`ColumnsParam | None`): Columns to match on (AND-ed equality). Defaults to the table's primary key. Must be non-empty. * **`background`** (`bool`, default: `False`): If True, return immediately with `{"id": ..., "job_url": ...}` and run the operation in a background thread. Poll `job_url` for the result. **Examples:** ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} router.add_delete_route(t, path='/delete') ``` ```bash theme={"theme":{"light":"light-plus","dark":"dark-plus"}} curl -X POST http://localhost:8000/delete \ -H 'Content-Type: application/json' \ -d '{"id": 42}' # {"num_rows": 1} ``` ## method  add\_insert\_route() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} add_insert_route( t: RouteTarget, *, path: str, inputs: ColumnsParam | None = None, uploadfile_inputs: ColumnsParam | None = None, outputs: ColumnsParam | None = None, return_fileresponse: bool = False, export_sql: SqlExport | None = None, background: bool = False ) -> None ``` Add a POST endpoint that inserts a single row into `t` and returns the resulting row. The request body contains the input column values as JSON fields (or as [multipart form data](https://fastapi.tiangolo.com/tutorial/request-files/) when `uploadfile_inputs` is used). The response is a JSON object with the output column values, or a [`FileResponse`](https://fastapi.tiangolo.com/advanced/custom-response/#fileresponse) when `return_fileresponse=True`. **Parameters:** * **`t`** (`RouteTarget`): The table to insert into, or the model that defines it. * **`path`** (`str`): The URL path for the endpoint. * **`inputs`** (`ColumnsParam | None`): Columns to accept as request fields. Defaults to all non-computed columns. Together with `uploadfile_inputs`, they must include every required (non-nullable, non-computed) column. * **`uploadfile_inputs`** (`ColumnsParam | None`): Columns to accept as [`UploadFile`](https://fastapi.tiangolo.com/tutorial/request-files/) fields (must be media-typed). These are sent as multipart form data; all other inputs become [`Form`](https://fastapi.tiangolo.com/tutorial/request-forms/) fields. * **`outputs`** (`ColumnsParam | None`): Columns to include in the response. Defaults to all columns (including inputs). * **`return_fileresponse`** (`bool`, default: `False`): If True, return the single media-typed output column as a [`FileResponse`](https://fastapi.tiangolo.com/advanced/custom-response/#fileresponse). Requires exactly one media-typed output column. * **`export_sql`** (`SqlExport | None`): If set, export each inserted row into an external RDBMS table after the Pixeltable insert succeeds. See [`SqlExport`](./sqlexport) for the target specification and supported `method` values. The row written is the response body: same columns as `outputs`, with media-typed columns rendered as URL strings (so the corresponding target columns must be string-typed). Schema compatibility against the response columns is validated once at registration time; the target table must already exist or registration fails. Mutually exclusive with `return_fileresponse`. Compatible with `background=True` (the SQL write runs in the worker thread). Note: when paired with `method='update'`, a Pixeltable insert triggers a target-side update -- this is intentional, supporting the append-only-source / current-state-view pattern. If the external write fails after the Pixeltable insert has already succeeded, the request fails with HTTP 500; no rollback is performed. * **`background`** (`bool`, default: `False`): If True, return immediately with `{"id": ..., "job_url": ...}` and run the insert in a background thread. Poll `job_url` for the result. Mutually exclusive with `return_fileresponse`. **Examples:** JSON request/response: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} router.add_insert_route( t, path='/generate', inputs=['prompt'], outputs=['result'] ) ``` ```bash theme={"theme":{"light":"light-plus","dark":"dark-plus"}} curl -X POST http://localhost:8000/generate \ -H 'Content-Type: application/json' \ -d '{"prompt": "a sunset over the ocean"}' # {"prompt": "a sunset over the ocean", "result": "..."} ``` File upload with `FileResponse`: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} router.add_insert_route( t, path='/resize', inputs=['width', 'height'], uploadfile_inputs=['image'], outputs=['resized'], return_fileresponse=True, ) ``` ```bash theme={"theme":{"light":"light-plus","dark":"dark-plus"}} curl -X POST http://localhost:8000/resize \ -F image=@photo.jpg -F width=640 -F height=480 \ --output resized.jpg # saves the resized image to resized.jpg ``` Export each inserted row into an external RDBMS table: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} router.add_insert_route( t, path='/generate', inputs=['prompt'], outputs=['prompt', 'result'], export_sql=SqlExport( db_connect='postgresql+psycopg://user:pw@host/analytics', table='generations', db_schema='public', ), ) ``` Each successful POST inserts a row into the Pixeltable table and then inserts the same row (columns: `prompt`, `result`) to `public.generations` in the target database. Background processing: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} router.add_insert_route(t, path='/slow', background=True) ``` ```bash theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # submit curl -X POST http://localhost:8000/slow -d '{"prompt": "hello"}' # {"id": "abc123", "job_url": "http://localhost:8000/jobs/abc123"} ``` ```bash theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # poll curl http://localhost:8000/jobs/abc123 # {"status": "done", "result": {...}} ``` ## method  add\_query\_route() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} add_query_route( *, path: str, query: pxt.Function, inputs: ColumnsParam | None = None, uploadfile_inputs: ColumnsParam | None = None, one_row: bool = False, return_fileresponse: bool = False, background: bool = False, method: Literal['get', 'post'] = 'post' ) -> None ``` Add an endpoint that executes a `@pxt.query` or `pxt.retrieval_udf` and returns the results. By default the endpoint accepts POST requests with a JSON [`Body`](https://fastapi.tiangolo.com/tutorial/body/) and returns `{"rows": [{...}, ...]}`. Use `method='get'` for [`Query`](https://fastapi.tiangolo.com/tutorial/query-params/) parameters instead. **Parameters:** * **`path`** (`str`): The URL path for the endpoint. * **`query`** (`pxt.Function`): The query to execute, created with `@pxt.query` or `pxt.retrieval_udf()`. * **`inputs`** (`ColumnsParam | None`): Parameters to accept as request fields. Defaults to all query parameters. * **`uploadfile_inputs`** (`ColumnsParam | None`): Parameters to accept as [`UploadFile`](https://fastapi.tiangolo.com/tutorial/request-files/) fields (must be media-typed). * **`one_row`** (`bool`, default: `False`): If True, expect exactly one result row and return it as a plain JSON object (not wrapped in `{"rows": [...]}`). 0 rows produces a 404, >1 rows a 409. * **`return_fileresponse`** (`bool`, default: `False`): If True, return the single media-typed result column as a [`FileResponse`](https://fastapi.tiangolo.com/advanced/custom-response/#fileresponse). Requires `one_row` semantics (0 rows -> 404, >1 rows -> 409). Mutually exclusive with `background`. * **`background`** (`bool`, default: `False`): If True, return immediately with `{"id": ..., "job_url": ...}` and run the query in a background thread. Poll `job_url` for the result. Mutually exclusive with `return_fileresponse`. * **`method`** (`Literal['get', 'post']`, default: `'post'`): HTTP method for the endpoint (`'get'` or `'post'`). **Examples:** Multi-row JSON response: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} router.add_query_route(path='/search', query=search_docs) ``` ```bash theme={"theme":{"light":"light-plus","dark":"dark-plus"}} curl -X POST http://localhost:8000/search \ -H 'Content-Type: application/json' \ -d '{"query_text": "hello"}' # {"rows": [{"id": 1, "text": "hello world", "score": 0.95}, ...]} ``` Single-row lookup: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} router.add_query_route(path='/lookup', query=lookup_by_id, one_row=True) ``` ```bash theme={"theme":{"light":"light-plus","dark":"dark-plus"}} curl -X POST http://localhost:8000/lookup -d '{"id": 42}' # {"id": 42, "name": "Alice", "email": "alice@example.com"} ``` GET with query-string parameters: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} router.add_query_route(path='/lookup', query=lookup_by_id, method='get') ``` ```bash theme={"theme":{"light":"light-plus","dark":"dark-plus"}} curl 'http://localhost:8000/lookup?id=42' # {"id": 42, "name": "Alice", "email": "alice@example.com"} ``` `FileResponse`: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} router.add_query_route( path='/thumbnail', query=get_thumbnail, return_fileresponse=True ) ``` ```bash theme={"theme":{"light":"light-plus","dark":"dark-plus"}} curl -X POST http://localhost:8000/thumbnail -d '{"id": 1}' --output thumb.jpg # saves the thumbnail image to thumb.jpg ``` ## method  add\_update\_route() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} add_update_route( t: RouteTarget, *, path: str, inputs: ColumnsParam | None = None, outputs: ColumnsParam | None = None, return_fileresponse: bool = False, export_sql: SqlExport | None = None, background: bool = False ) -> None ``` Add a POST endpoint that updates a single row in `t` and returns the updated row. The row to update is identified by its primary key, which must be included in the request body alongside the input column values. The update is performed via a single-row [`batch_update()`](./table#method-batch_update) call, using the primary key columns to identify the row and the columns referenced in `inputs` as the values to set. The request body contains values for the primary key columns plus the input columns as JSON fields. The response is a JSON object with the output column values, or a [`FileResponse`](https://fastapi.tiangolo.com/advanced/custom-response/#fileresponse) when `return_fileresponse=True`. Note: media-typed columns (image, video, audio, document) are excluded from `inputs` and from the default input set. **Parameters:** * **`t`** (`RouteTarget`): The table to update, or the model that defines it. * **`path`** (`str`): The URL path for the endpoint. * **`inputs`** (`ColumnsParam | None`): Columns to accept as request fields, excluding primary key and media-typed columns (which cannot be updated). Defaults to all non-computed, non-primary-key, non-media columns. * **`outputs`** (`ColumnsParam | None`): Columns to include in the response. Defaults to all columns (including inputs). * **`return_fileresponse`** (`bool`, default: `False`): If True, return the single media-typed output column as a [`FileResponse`](https://fastapi.tiangolo.com/advanced/custom-response/#fileresponse). Requires exactly one media-typed output column. * **`export_sql`** (`SqlExport | None`): If set, export each updated row into an external RDBMS table after the Pixeltable update succeeds. See [`SqlExport`](./sqlexport) for the target specification and supported `method` values. The row written is the response body: same columns as `outputs`, with media-typed columns rendered as URL strings (so the corresponding target columns must be string-typed). Schema compatibility is validated once at registration time; the target table must already exist or registration fails. Mutually exclusive with `return_fileresponse`. Compatible with `background=True`. Note: with `method='insert'` (the default), every update appends a new row to the target table -- the target acts as an audit log, not a current-state view. Use `method='update'` to keep the target as a current-state view keyed on the target's primary key. If the external write fails after the Pixeltable update has already succeeded, the request fails with HTTP 500; no rollback is performed. * **`background`** (`bool`, default: `False`): If True, return immediately with `{"id": ..., "job_url": ...}` and run the update in a background thread. Poll `job_url` for the result. Mutually exclusive with `return_fileresponse`. **Examples:** JSON request/response (table has primary key `id`): ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} router.add_update_route( t, path='/update', inputs=['prompt'], outputs=['prompt', 'result'] ) ``` ```bash theme={"theme":{"light":"light-plus","dark":"dark-plus"}} curl -X POST http://localhost:8000/update \ -H 'Content-Type: application/json' \ -d '{"id": 1, "prompt": "a sunset over the ocean"}' # {"prompt": "a sunset over the ocean", "result": "..."} ``` Append every update to an external audit table: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} router.add_update_route( t, path='/update', inputs=['prompt'], outputs=['id', 'prompt', 'result'], export_sql=SqlExport( db_connect='postgresql+psycopg://user:pw@host/analytics', table='update_log', ), ) ``` Background processing: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} router.add_update_route(t, path='/slow-update', background=True) ``` ```bash theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # submit curl -X POST http://localhost:8000/slow-update \ -H 'Content-Type: application/json' \ -d '{"id": 1, "prompt": "hello"}' # {"id": "abc123", "job_url": "http://localhost:8000/jobs/abc123"} # poll curl http://localhost:8000/jobs/abc123 # {"status": "done", "result": {...}} ``` ## method  insert\_route() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} insert_route( t: RouteTarget, *, path: str, inputs: ColumnsParam | None = None, uploadfile_inputs: ColumnsParam | None = None, outputs: ColumnsParam | None = None, export_sql: SqlExport | None = None, background: bool = False ) -> Callable[[Callable[..., pydantic.BaseModel]], Callable[..., pydantic.BaseModel]] ``` Decorator that registers a POST endpoint performing a `Table.insert()` followed by user-defined post-processing. The request body carries the input column values (JSON, or multipart form data when `uploadfile_inputs` is used). After inserting the row, the decorated function is called with the requested output columns as keyword arguments (parameter names and Pixeltable types must match `outputs`). Its return value must be a Pydantic model and is returned as the HTTP response body. Media-typed outputs (image, video, audio, document) are delivered to the function as `/media/` URL strings -- annotate those parameters as `str` (or `str | None` if the column is nullable), not as `pxt.Image` / `pxt.Video` / etc. **Parameters:** * **`t`** (`RouteTarget`): The table to insert into, or the model that defines it. * **`path`** (`str`): The URL path for the endpoint. * **`inputs`** (`ColumnsParam | None`): Columns to accept as request fields. Defaults to all non-computed columns. Together with `uploadfile_inputs`, they must include every required (non-nullable, non-computed) column. * **`uploadfile_inputs`** (`ColumnsParam | None`): Columns to accept as [`UploadFile`](https://fastapi.tiangolo.com/tutorial/request-files/) fields (must be media-typed). These are sent as multipart form data; all other inputs become [`Form`](https://fastapi.tiangolo.com/tutorial/request-forms/) fields. * **`outputs`** (`ColumnsParam | None`): Columns from the inserted row to pass to the decorated function as keyword arguments. Defaults to all columns. * **`export_sql`** (`SqlExport | None`): If set, export the decorated function's return value into an external RDBMS table after the Pixeltable insert succeeds. See [`SqlExport`](./sqlexport) for the target specification and supported `method` values. The row written is the user function's pydantic return value (its fields, not the source columns), so the target table schema must match those fields. Media-typed fields are modeled as strings (URL form). Schema compatibility is validated once at registration time; the target table must already exist or registration fails. Compatible with `background=True` (the SQL write runs in the worker thread). If the external write fails after the Pixeltable insert has already succeeded, the request fails with HTTP 500; no rollback is performed. * **`background`** (`bool`, default: `False`): If True, return immediately with `{"id": ..., "job_url": ...}` and run the insert plus post-processing in a background thread. Poll `job_url` for the result; the decorated function's return value is delivered as the job result. **Examples:** ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} class GenerateResponse(pydantic.BaseModel): caption: str score: float @router.insert_route( t, path='/generate', inputs=['prompt'], outputs=['caption', 'score'], background=False, ) def format_response(*, caption: str, score: float) -> GenerateResponse: return GenerateResponse( caption=caption.strip(), score=round(score, 3) ) ``` ```bash theme={"theme":{"light":"light-plus","dark":"dark-plus"}} curl -X POST http://localhost:8000/generate \ -H 'Content-Type: application/json' \ -d '{"prompt": "a sunset over the ocean"}' # {"caption": "orange sky above calm water", "score": 0.932} ``` Export the post-processed response into an external RDBMS table: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @router.insert_route( t, path='/generate', inputs=['prompt'], outputs=['caption', 'score'], export_sql=SqlExport( db_connect='postgresql+psycopg://user:pw@host/analytics', table='captions', ), ) def format_response(*, caption: str, score: float) -> GenerateResponse: return GenerateResponse( caption=caption.strip(), score=round(score, 3) ) ``` Each successful POST inserts a row into the Pixeltable table and then appends a row with columns `caption`, `score` (the response model fields) to `captions`. ## method  update\_route() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} update_route( t: RouteTarget, *, path: str, inputs: ColumnsParam | None = None, outputs: ColumnsParam | None = None, export_sql: SqlExport | None = None, background: bool = False ) -> Callable[[Callable[..., pydantic.BaseModel]], Callable[..., pydantic.BaseModel]] ``` Decorator that registers a POST endpoint performing a `Table.batch_update()` followed by user-defined post-processing. The request body carries values for the primary key columns (to identify the row) plus the input column values as JSON fields. After updating the row, the decorated function is called with the requested output columns as keyword arguments (parameter names and Pixeltable types must match `outputs`). Its return value must be a Pydantic model and is returned as the HTTP response body. Media-typed outputs (image, video, audio, document) are delivered to the function as `/media/` URL strings -- annotate those parameters as `str` (or `str | None` if the column is nullable), not as `pxt.Image` / `pxt.Video` / etc. If the row does not exist, the endpoint returns HTTP 404 without calling the decorated function. Note: media-typed columns (image, video, audio, document) and primary key columns cannot be used as `inputs`. Primary key columns are always part of the request body for row identification. **Parameters:** * **`t`** (`RouteTarget`): The table to update, or the model that defines it. * **`path`** (`str`): The URL path for the endpoint. * **`inputs`** (`ColumnsParam | None`): Columns to accept as update fields. Defaults to all non-computed, non-primary-key, non-media columns. * **`outputs`** (`ColumnsParam | None`): Columns from the updated row to pass to the decorated function as keyword arguments. Defaults to all columns. * **`export_sql`** (`SqlExport | None`): If set, export the decorated function's return value into an external RDBMS table after the Pixeltable update succeeds. See [`SqlExport`](./sqlexport) for the target specification and supported `method` values. The row written is the user function's pydantic return value (its fields, not the source columns), so the target table schema must match those fields. Media-typed fields are modeled as strings (URL form). Schema compatibility is validated once at registration time; the target table must already exist or registration fails. Compatible with `background=True`. Note: with `method='insert'` (the default), every update appends a new row to the target table -- the target acts as an audit log, not a current-state view. Use `method='update'` to keep the target as a current-state view keyed on the target's primary key. If the external write fails after the Pixeltable update has already succeeded, the request fails with HTTP 500; no rollback is performed. * **`background`** (`bool`, default: `False`): If True, return immediately with `{"id": ..., "job_url": ...}` and run the update plus post-processing in a background thread. Poll `job_url` for the result; the decorated function's return value is delivered as the job result. **Examples:** ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} class ItemResponse(pydantic.BaseModel): id: int summary: str score: float @router.update_route( t, path='/update', inputs=['text'], outputs=['id', 'text', 'score'] ) def format_response(*, id: int, text: str, score: float) -> ItemResponse: return ItemResponse(id=id, summary=text[:100], score=round(score, 3)) ``` ```bash theme={"theme":{"light":"light-plus","dark":"dark-plus"}} curl -X POST http://localhost:8000/update \ -H 'Content-Type: application/json' \ -d '{"id": 42, "text": "new content"}' # {"id": 42, "summary": "new content", "score": 0.871} ``` Append every post-processed update into an external audit table: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @router.update_route( t, path='/update', inputs=['text'], outputs=['id', 'text', 'score'], export_sql=SqlExport( db_connect='postgresql+psycopg://user:pw@host/analytics', table='item_log', ), ) def format_response(*, id: int, text: str, score: float) -> ItemResponse: return ItemResponse(id=id, summary=text[:100], score=round(score, 3)) ``` Background processing: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @router.update_route(t, path='/slow-update', background=True) def format_response(*, id: int, result: str) -> MyResponse: return MyResponse(id=id, result=result.strip()) ``` ```bash theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # submit curl -X POST http://localhost:8000/slow-update \ -H 'Content-Type: application/json' \ -d '{"id": 1, "text": "hello"}' # {"id": "abc123", "job_url": "http://localhost:8000/jobs/abc123"} # poll curl http://localhost:8000/jobs/abc123 # {"status": "done", "result": {"id": 1, "result": "hello"}} ``` # fireworks Source: https://docs.pixeltable.com/sdk/latest/fireworks View Source on GitHub # module  pixeltable.functions.fireworks Pixeltable UDFs that wrap various endpoints from the Fireworks AI API. In order to use them, you must first `pip install fireworks-ai` and configure your Fireworks AI credentials, as described in the [Working with Fireworks](https://docs.pixeltable.com/howto/providers/working-with-fireworks) tutorial. ## udf  chat\_completions() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf chat_completions( messages: pxt.Json[(Json, ...)], *, model: pxt.String, model_kwargs: pxt.Json | None = None ) -> pxt.Json ``` Creates a model response for the given chat conversation. Equivalent to the Fireworks AI `chat/completions` API endpoint. For additional details, see: [https://docs.fireworks.ai/api-reference/post-chatcompletions](https://docs.fireworks.ai/api-reference/post-chatcompletions) Request throttling: Applies the rate limit set in the config (section `fireworks`, key `rate_limit`). If no rate limit is configured, uses a default of 600 RPM. **Requirements:** * `pip install fireworks-ai` **Parameters:** * **`messages`** (`pxt.Json[(Json`): A list of messages comprising the conversation so far. * **`model`** (`Any`): The name of the model to use. * **`model_kwargs`** (`Any`): Additional keyword args for the Fireworks `chat_completions` API. For details on the available parameters, see: [https://docs.fireworks.ai/api-reference/post-chatcompletions](https://docs.fireworks.ai/api-reference/post-chatcompletions) **Returns:** * `pxt.Json`: A dictionary containing the response and other metadata. **Examples:** Add a computed column that applies the model `accounts/fireworks/models/nemotron-lightning-3p5-30b-a3b` to an existing Pixeltable column `tbl.prompt` of the table `tbl`: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} messages = [{'role': 'user', 'content': tbl.prompt}] tbl.add_computed_column( response=chat_completions( messages, model='accounts/fireworks/models/nemotron-lightning-3p5-30b-a3b', ) ) ``` # functions Source: https://docs.pixeltable.com/sdk/latest/functions View Source on GitHub # module  pixeltable.functions General Pixeltable UDFs. This parent module contains general-purpose UDFs that apply to multiple data types. ## func  filter() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} filter( expr: pixeltable.exprs.expr.Expr, predicate: Callable[[pixeltable.exprs.expr.Expr], Any] ) -> pixeltable.exprs.expr.Expr ``` Keeps the elements of a JSON array for which a predicate holds, producing a new array. `filter()` is used like a UDF, for example in `select()` or `add_computed_column()`. **Parameters:** * **`expr`** (`pixeltable.exprs.expr.Expr`): The array to filter; an expression of type `pxt.Json` that resolves to a JSON array. Its element type is preserved in the result. * **`predicate`** (`typing.Callable[[pixeltable.exprs.expr.Expr], typing.Any]`): A Python function (typically a lambda) that decides which elements to keep. It receives `x`, a stand-in for a single array element, and returns a boolean condition; the element is kept when it is true. Operate on `x` exactly as you would on a column: comparisons, indexing (`x[0]`), field access (`x.field`), and JSON methods all work. Combine multiple conditions with `&` and `|`, and negate with `~` (not Python `and`/`or`/`not`). Example: `lambda x: (x > 0) & (x < 10)`. **Returns:** * `pixeltable.exprs.expr.Expr`: A new array containing the elements of `expr` for which `predicate` is true, unchanged. If `expr` is `null` or does not resolve to a JSON array, the result is `null`. **Examples:** Given a table `tbl` with a `pxt.Json` column `data` holding lists of numbers, add a column that keeps only the positive numbers: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} tbl.add_computed_column( positives=pxt.functions.filter(tbl.data, lambda x: x > 0) ) ``` When `data` holds lists of objects such as `{'score': 0.9, 'label': 'cat'}`, keep only the high-confidence ones: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} tbl.select( confident=pxt.functions.filter(tbl.data, lambda x: x.score >= 0.9) ).collect() ``` ## func  map() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} map( expr: pixeltable.exprs.expr.Expr, fn: Callable[[pixeltable.exprs.expr.Expr], Any] ) -> pixeltable.exprs.expr.Expr ``` Applies a function to each element of a JSON array, producing a new array. `map()` is used like a UDF, for example in `select()` or `add_computed_column()`. **Parameters:** * **`expr`** (`pixeltable.exprs.expr.Expr`): The array to map over; an expression of type `pxt.Json` that resolves to a JSON array. If its elements have a known type (e.g. the column is declared `pxt.Json[[int]]`), that type is available to `fn` and carries through to the result. * **`fn`** (`typing.Callable[[pixeltable.exprs.expr.Expr], typing.Any]`): A Python function (typically a lambda) applied to each element to produce its replacement. It receives `x`, a stand-in for a single array element, and returns the value to store in its place. Operate on `x` exactly as you would on a column: arithmetic, indexing (`x[0]`), field access (`x.field`), and JSON methods (`x.len()`, `x.sum()`, etc.) all work. **Returns:** * `pixeltable.exprs.expr.Expr`: A new array holding `fn(x)` for each element `x` of `expr`. If `expr` is `null` or does not resolve to a JSON array, the result is `null`. **Examples:** Given a table `tbl` with a `pxt.Json` column `data` holding lists of numbers, add a column that doubles each number: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} tbl.add_computed_column( doubled=pxt.functions.map(tbl.data, lambda x: x * 2) ) ``` When `data` holds lists of objects such as `{'score': 0.9, 'label': 'cat'}`, extract each score: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} tbl.select( scores=pxt.functions.map(tbl.data, lambda x: x.score) ).collect() ``` ## func  sort() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} sort( expr: pixeltable.exprs.expr.Expr, key: Optional[Callable[[pixeltable.exprs.expr.Expr], Any]] = None, *, asc: bool = True ) -> pixeltable.exprs.expr.Expr ``` Sorts the elements of a JSON array, producing a new array. `sort()` is used like a UDF, for example in `select()` or `add_computed_column()`. **Parameters:** * **`expr`** (`pixeltable.exprs.expr.Expr`): The array to sort; an expression of type `pxt.Json` that resolves to a JSON array. Its element type is preserved in the result. * **`key`** (`typing.Optional[typing.Callable[[pixeltable.exprs.expr.Expr], typing.Any]]`): An optional Python function (typically a lambda) that produces the value each element is ordered by. It receives `x`, a stand-in for a single array element, and returns the sort key. Operate on `x` exactly as you would on a column: arithmetic, indexing (`x[0]`), field access (`x.field`), and JSON methods all work. When `key` is omitted, the elements are ordered by their own natural ordering. * **`asc`** (`bool`, default: `True`): Whether to sort in ascending (the default) or descending order. **Returns:** * `pixeltable.exprs.expr.Expr`: A new array with the elements of `expr` in sorted order. If `expr` is `null` or does not resolve to a JSON array, the result is `null`. Sorting a list of scalars without a `key`, or by non-orderable keys, raises if the values are not mutually comparable (matching Python's `sorted()`). **Examples:** Given a table `tbl` with a `pxt.Json` column `data` holding lists of numbers, add a column that sorts each list in descending order: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} tbl.add_computed_column(ranked=pxt.functions.sort(tbl.data, asc=False)) ``` When `data` holds lists of objects such as `{'score': 0.9, 'label': 'cat'}`, sort each list by score: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} tbl.select( by_score=pxt.functions.sort( tbl.data, key=lambda x: x.score, asc=False ) ).collect() ``` ## uda  count() ```python Signatures theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Signature 1: @pxt.uda count(val: pxt.String | None) -> pxt.Int # Signature 2: @pxt.uda count(val: pxt.Bool | None) -> pxt.Int # Signature 3: @pxt.uda count(val: pxt.Int | None) -> pxt.Int # Signature 4: @pxt.uda count(val: pxt.Float | None) -> pxt.Int # Signature 5: @pxt.uda count(val: pxt.Timestamp | None) -> pxt.Int # Signature 6: @pxt.uda count(val: pxt.Json | None) -> pxt.Int # Signature 7: @pxt.uda count(val: pxt.Array | None) -> pxt.Int # Signature 8: @pxt.uda count(val: pxt.Image | None) -> pxt.Int # Signature 9: @pxt.uda count(val: pxt.Video | None) -> pxt.Int # Signature 10: @pxt.uda count(val: pxt.Audio | None) -> pxt.Int # Signature 11: @pxt.uda count(val: pxt.Document | None) -> pxt.Int # Signature 12: @pxt.uda count(val: pxt.Date | None) -> pxt.Int # Signature 13: @pxt.uda count(val: pxt.UUID | None) -> pxt.Int # Signature 14: @pxt.uda count(val: pxt.Binary | None) -> pxt.Int ``` Aggregate function that counts the number of non-null values in a column or grouping. **Parameters:** * **`val`** (`String | None`): The value to count. **Returns:** * `pxt.Int`: The count of non-null values. **Examples:** Count the number of non-null values in the `value` column of the table `tbl`: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} tbl.select(pxt.functions.count(tbl.value)).collect() ``` Group by the `category` column and compute the count of non-null values in the `value` column for each category, assigning the name `'category_count'` to the new column: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} tbl.group_by(tbl.category).select( tbl.category, category_count=pxt.functions.count(tbl.value) ).collect() ``` ## uda  max() ```python Signatures theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Signature 1: @pxt.uda max(val: pxt.String | None) -> pxt.String | None # Signature 2: @pxt.uda max(val: pxt.Int | None) -> pxt.Int | None # Signature 3: @pxt.uda max(val: pxt.Float | None) -> pxt.Float | None # Signature 4: @pxt.uda max(val: pxt.Bool | None) -> pxt.Bool | None # Signature 5: @pxt.uda max(val: pxt.Timestamp | None) -> pxt.Timestamp | None ``` Aggregate function that computes the maximum value in a column or grouping. **Parameters:** * **`val`** (`String | None`): The value to compare. **Returns:** * `pxt.String | None`: The maximum value, or `None` if there are no non-null values. **Examples:** Compute the maximum value in the `value` column of the table `tbl`: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} tbl.select(pxt.functions.max(tbl.value)).collect() ``` Group by the `category` column and compute the maximum value in the `value` column for each category, assigning the name `'category_max'` to the new column: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} tbl.group_by(tbl.category).select( tbl.category, category_max=pxt.functions.max(tbl.value) ).collect() ``` ## uda  mean() ```python Signatures theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Signature 1: @pxt.uda mean(val: pxt.Int | None) -> pxt.Float | None # Signature 2: @pxt.uda mean(val: pxt.Float | None) -> pxt.Float | None ``` Aggregate function that computes the mean (average) of non-null values of a numeric column or grouping. **Parameters:** * **`val`** (`Int | None`): The numeric value to include in the mean. **Returns:** * `pxt.Float | None`: The mean of the non-null values, or `None` if there are no non-null values. **Examples:** Compute the mean of the values in the `value` column of the table `tbl`: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} tbl.select(pxt.functions.mean(tbl.value)).collect() ``` Group by the `category` column and compute the mean of the `value` column for each category, assigning the name `'category_mean'` to the new column: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} tbl.group_by(tbl.category).select( tbl.category, category_mean=pxt.functions.mean(tbl.value) ).collect() ``` ## uda  min() ```python Signatures theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Signature 1: @pxt.uda min(val: pxt.String | None) -> pxt.String | None # Signature 2: @pxt.uda min(val: pxt.Int | None) -> pxt.Int | None # Signature 3: @pxt.uda min(val: pxt.Float | None) -> pxt.Float | None # Signature 4: @pxt.uda min(val: pxt.Bool | None) -> pxt.Bool | None # Signature 5: @pxt.uda min(val: pxt.Timestamp | None) -> pxt.Timestamp | None ``` Aggregate function that computes the minimum value in a column or grouping. **Parameters:** * **`val`** (`String | None`): The value to compare. **Returns:** * `pxt.String | None`: The minimum value, or `None` if there are no non-null values. **Examples:** Compute the minimum value in the `value` column of the table `tbl`: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} tbl.select(pxt.functions.min(tbl.value)).collect() ``` Group by the `category` column and compute the minimum value in the `value` column for each category, assigning the name `'category_min'` to the new column: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} tbl.group_by(tbl.category).select( tbl.category, category_min=pxt.functions.min(tbl.value) ).collect() ``` ## uda  sum() ```python Signatures theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Signature 1: @pxt.uda sum(val: pxt.Int | None) -> pxt.Int | None # Signature 2: @pxt.uda sum(val: pxt.Float | None) -> pxt.Float | None ``` Aggregate function that computes the sum of non-null values of a numeric column or grouping. **Parameters:** * **`val`** (`Int | None`): The numeric value to add to the sum. **Returns:** * `pxt.Int | None`: The sum of the non-null values, or `None` if there are no non-null values. **Examples:** Sum the values in the `value` column of the table `tbl`: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} tbl.select(pxt.functions.sum(tbl.value)).collect() ``` Group by the `category` column and compute the sum of the `value` column for each category, assigning the name `'category_total'` to the new column: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} tbl.group_by(tbl.category).select( tbl.category, category_total=pxt.functions.sum(tbl.value) ).collect() ``` # gemini Source: https://docs.pixeltable.com/sdk/latest/gemini View Source on GitHub # module  pixeltable.functions.gemini Pixeltable UDFs that wrap various endpoints from the Google Gemini API. In order to use them, you must first `pip install google-genai` and configure your Gemini credentials, as described in the [Working with Gemini](https://docs.pixeltable.com/howto/providers/working-with-gemini) tutorial. Supports two authentication methods: * Google AI Studio: set `GOOGLE_API_KEY` or `GEMINI_API_KEY` (or put `api_key` in the `gemini` section of the Pixeltable config file). * Vertex AI: set `GOOGLE_GENAI_USE_VERTEXAI=true` and `GOOGLE_CLOUD_PROJECT` (and optionally `GOOGLE_CLOUD_LOCATION`), then authenticate via Application Default Credentials (e.g. `gcloud auth application-default login`). ## func  invoke\_tools() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} invoke_tools( tools: pixeltable.func.tools.Tools, response: pixeltable.exprs.expr.Expr ) -> pixeltable.exprs.inline_expr.InlineDict ``` Converts an OpenAI response dict to Pixeltable tool invocation format and calls `tools._invoke()`. ## udf  embed\_content() ```python Signatures theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Signature 1: @pxt.udf embed_content( contents: pxt.String, *, model: pxt.String, config: pxt.Json | None ) -> pxt.Array[(None,), float32] # Signature 2: @pxt.udf embed_content( contents: pxt.Image, *, model: pxt.String, config: pxt.Json | None ) -> pxt.Array[(None,), float32] # Signature 3: @pxt.udf embed_content( contents: pxt.Audio, *, model: pxt.String, config: pxt.Json | None ) -> pxt.Array[(None,), float32] # Signature 4: @pxt.udf embed_content( contents: pxt.Video, *, model: pxt.String, config: pxt.Json | None ) -> pxt.Array[(None,), float32] # Signature 5: @pxt.udf embed_content( contents: pxt.Document, *, model: pxt.String, config: pxt.Json | None ) -> pxt.Array[(None,), float32] ``` Generate embeddings for text, images, video, and other content. For more information on Gemini embeddings API, see: [https://ai.google.dev/gemini-api/docs/embeddings](https://ai.google.dev/gemini-api/docs/embeddings) **Requirements:** * `pip install google-genai` **Parameters:** * **`contents`** (`String`): The string, image, audio, video, or document to embed. * **`model`** (`String`): The Gemini model to use. * **`config`** (`Json | None`, default: `Literal(None)`): Configuration for embedding generation, corresponding to keyword arguments of `genai.types.EmbedContentConfig`. For details on the parameters, see: [https://googleapis.github.io/python-genai/genai.html#genai.types.EmbedContentConfig](https://googleapis.github.io/python-genai/genai.html#genai.types.EmbedContentConfig) **Returns:** * `pxt.Array[(None,), float32]`: The corresponding embedding vector. **Examples:** Add a computed column with embeddings to an existing table with a `text` column: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} t.add_computed_column( embedding=embed_content(t.text, model='gemini-embedding-2') ) ``` Add an embedding index on `text` column: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} t.add_embedding_index( t.text, embedding=embed_content.using(model='gemini-embedding-2') ) ``` ## udf  generate\_content() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf generate_content( contents: pxt.Json, *, model: pxt.String, config: pxt.Json | None = None, tools: pxt.Json[(Json, ...)] | None = None ) -> pxt.Json ``` Generate content from the specified model. Request throttling: Applies the rate limit set in the config (section `gemini.rate_limits`; use the model id as the key). If no rate limit is configured, uses a default of 600 RPM. **Requirements:** * `pip install google-genai` **Parameters:** * **`contents`** (`pxt.Json`): The input content to generate from. Can be a prompt, or a list containing images and text prompts, as described in: [https://ai.google.dev/gemini-api/docs/text-generation](https://ai.google.dev/gemini-api/docs/text-generation) and [https://ai.google.dev/gemini-api/docs/image-generation](https://ai.google.dev/gemini-api/docs/image-generation) for image generation. * **`model`** (`pxt.String`): The name of the model to use. * **`config`** (`pxt.Json | None`): Configuration for generation, corresponding to keyword arguments of `genai.types.GenerateContentConfig`. For details on the parameters, see: [https://googleapis.github.io/python-genai/genai.html#genai.types.GenerateContentConfig](https://googleapis.github.io/python-genai/genai.html#genai.types.GenerateContentConfig) * **`tools`** (`pxt.Json[(Json`): An optional list of Pixeltable tools to use. It is also possible to specify tools manually via the `config['tools']` parameter, but at most one of `config['tools']` or `tools` may be used. **Returns:** * `pxt.Json`: A dictionary containing the response and other metadata. **Examples:** Add a computed column that applies the model `gemini-2.5-flash` to an existing Pixeltable column `tbl.prompt` of the table `tbl`: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} tbl.add_computed_column( response=generate_content(tbl.prompt, model='gemini-2.5-flash') ) ``` Generate an image with a Nano Banana model (Gemini image-generation models such as `gemini-3.1-flash-image-preview`) and extract the PIL image from the response using JSON subscripting. Image bytes in `inline_data.data` are decoded into PIL images automatically. Pass `response_modalities=['IMAGE']` so the response contains a single image part: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} tbl.add_computed_column( response=generate_content( tbl.prompt, model='gemini-3.1-flash-image-preview', config={'response_modalities': ['IMAGE']}, ) ) tbl.add_computed_column( image=tbl.response.candidates[0].content.parts[0].inline_data.data ) ``` ## udf  generate\_images() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf generate_images( prompt: pxt.String, *, model: pxt.String, config: pxt.Json | None = None ) -> pxt.Image ``` Generates images based on a text description and configuration. For additional details, see: [https://ai.google.dev/gemini-api/docs/imagen](https://ai.google.dev/gemini-api/docs/imagen) Note: This function is for Imagen models only. For Gemini image-generation models (Nano Banana, e.g. `gemini-3.1-flash-image-preview`), use [`generate_content`](./gemini#func-generate_content) instead. Imagen requires Vertex AI mode; `google-genai` 2.x no longer reaches it through the Developer API. Request throttling: Applies the rate limit set in the config (section `imagen.rate_limits`; use the model id as the key). If no rate limit is configured, uses a default of 600 RPM. **Requirements:** * `pip install google-genai` **Parameters:** * **`prompt`** (`pxt.String`): A text description of the images to generate. * **`model`** (`pxt.String`): The model to use. * **`config`** (`pxt.Json | None`): Configuration for generation, corresponding to keyword arguments of `genai.types.GenerateImagesConfig`. For details on the parameters, see: [https://googleapis.github.io/python-genai/genai.html#genai.types.GenerateImagesConfig](https://googleapis.github.io/python-genai/genai.html#genai.types.GenerateImagesConfig) **Returns:** * `pxt.Image`: The generated image. **Examples:** Add a computed column that applies the model `imagen-4.0-generate-001` to an existing Pixeltable column `tbl.prompt` of the table `tbl`: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} tbl.add_computed_column( response=generate_images(tbl.prompt, model='imagen-4.0-generate-001') ) ``` ## udf  generate\_speech() ```python Signatures theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Signature 1: @pxt.udf generate_speech( text: pxt.String, *, model: pxt.String, voice: pxt.String, config: pxt.Json | None ) -> pxt.Audio # Signature 2: @pxt.udf generate_speech( text: pxt.String, *, model: pxt.String, voices: pxt.Json, config: pxt.Json | None ) -> pxt.Audio ``` Generates speech audio from text using Gemini's text-to-speech capability. For additional details, see: [https://ai.google.dev/gemini-api/docs/speech-generation](https://ai.google.dev/gemini-api/docs/speech-generation) Request throttling: Applies the rate limit set in the config (section `gemini.rate_limits`; use the model id as the key). If no rate limit is configured, uses a default of 600 RPM. **Requirements:** * `pip install google-genai` **Parameters:** * **`text`** (`String`): The text to synthesize into speech. * **`model`** (`String`): The model to use (e.g. `'gemini-2.5-flash-preview-tts'`). * **`voice`** (`String`): The voice profile to use. Supported voices include `'Kore'`, `'Puck'`, `'Charon'`, `'Fenrir'`, `'Aoede'`, `'Leda'`, `'Orus'`, `'Zephyr'`, and others. See the [speech generation docs](https://ai.google.dev/gemini-api/docs/speech-generation) for the full list. Mutually exclusive with `voices`. * **`voices`** (`Json`): A mapping from speaker alias (as used in the text) to voice name. For example, `{'Alice': 'Kore', 'Bob': 'Puck'}`. Mutually exclusive with `voice`. * **`config`** (`Json | None`, default: `Literal(None)`): Additional configuration, corresponding to keyword arguments of `genai.types.GenerateContentConfig`. Keys such as `response_modalities` and `speech_config` are set automatically and should not be included. **Returns:** * `pxt.Audio`: An audio file (WAV, 24 kHz mono 16-bit) containing the synthesized speech. **Examples:** Add a computed column that generates speech from text: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} tbl.add_computed_column( audio=generate_speech( tbl.text, model='gemini-2.5-flash-preview-tts', voice='Kore' ) ) ``` ## udf  generate\_videos() ```python Signatures theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Signature 1: @pxt.udf generate_videos( prompt: pxt.String | None, image: pxt.Image | None, *, model: pxt.String, config: pxt.Json | None ) -> pxt.Video # Signature 2: @pxt.udf generate_videos( prompt: pxt.String | None, images: pxt.Json[(Image, ...)] | None, *, model: pxt.String, config: pxt.Json | None, reference_types: pxt.Json[(String, ...)] | None ) -> pxt.Video ``` Generates videos based on a text description and configuration. For additional details, see: [https://ai.google.dev/gemini-api/docs/video](https://ai.google.dev/gemini-api/docs/video) At least one of `prompt` or `image` must be provided. When `image` is a single image, it is used as the first frame of the generated video. When `image` is a list of images, they are used as reference images to guide the style or asset appearance throughout the video (Veo 3.1+). See the overloaded signature for details. Request throttling: Applies the rate limit set in the config (section `veo.rate_limits`; use the model id as the key). If no rate limit is configured, uses a default of 600 RPM. **Requirements:** * `pip install google-genai` **Parameters:** * **`prompt`** (`String | None`, default: `Literal(None)`): A text description of the videos to generate. * **`image`** (`Image | None`, default: `Literal(None)`): A single image to use as the first frame of the video, or as `images` a list of up to 3 reference images for Veo 3.1 (see overloaded signature). * **`model`** (`String`): The model to use. * **`config`** (`Json | None`, default: `Literal(None)`): Configuration for generation, corresponding to keyword arguments of `genai.types.GenerateVideosConfig`. For details on the parameters, see: [https://googleapis.github.io/python-genai/genai.html#genai.types.GenerateVideosConfig](https://googleapis.github.io/python-genai/genai.html#genai.types.GenerateVideosConfig) **Returns:** * `pxt.Video`: The generated video. **Examples:** Add a computed column that applies the model `veo-3.0-generate-001` to an existing Pixeltable column `tbl.prompt` of the table `tbl`: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} tbl.add_computed_column( response=generate_videos(tbl.prompt, model='veo-3.0-generate-001') ) ``` Use reference images with Veo 3.1 to guide video generation: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} tbl.add_computed_column( response=generate_videos( tbl.prompt, images=[tbl.ref_img1, tbl.ref_img2], reference_types=['asset', 'asset'], model='veo-3.1-generate-preview', ) ) ``` ## udf  transcribe() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf transcribe( audio: pxt.Audio, *, model: pxt.String, prompt: pxt.String, config: pxt.Json | None = None ) -> pxt.String ``` Transcribes audio to text using Gemini's audio understanding capability. For additional details, see: [https://ai.google.dev/gemini-api/docs/audio](https://ai.google.dev/gemini-api/docs/audio) Request throttling: Applies the rate limit set in the config (section `gemini.rate_limits`; use the model id as the key). If no rate limit is configured, uses a default of 600 RPM. **Requirements:** * `pip install google-genai` **Parameters:** * **`audio`** (`pxt.Audio`): The audio file to transcribe. * **`model`** (`pxt.String`): The model to use (e.g. `'gemini-2.5-flash'`). * **`prompt`** (`pxt.String`): The instruction prompt sent alongside the audio. For example, `'Generate a transcript of the speech.'` or `'Summarize the audio content.'`. * **`config`** (`pxt.Json | None`): Additional configuration, corresponding to keyword arguments of `genai.types.GenerateContentConfig`. **Returns:** * `pxt.String`: The transcribed text. **Examples:** Add a computed column that transcribes audio: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} tbl.add_computed_column( transcript=transcribe(tbl.audio, model='gemini-2.5-flash') ) ``` # groq Source: https://docs.pixeltable.com/sdk/latest/groq View Source on GitHub # module  pixeltable.functions.groq Pixeltable UDFs that wrap various endpoints from the Groq API. In order to use them, you must first `pip install groq` and configure your Groq credentials, as described in the [Working with Groq](https://docs.pixeltable.com/howto/providers/working-with-groq) tutorial. ## func  invoke\_tools() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} invoke_tools( tools: pixeltable.func.tools.Tools, response: pixeltable.exprs.expr.Expr ) -> pixeltable.exprs.inline_expr.InlineDict ``` Converts an OpenAI response dict to Pixeltable tool invocation format and calls `tools._invoke()`. ## udf  chat\_completions() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf chat_completions( messages: pxt.Json[(Json, ...)], *, model: pxt.String, model_kwargs: pxt.Json | None = None, tools: pxt.Json[(Json, ...)] | None = None, tool_choice: pxt.Json | None = None ) -> pxt.Json ``` Chat Completion API. Equivalent to the Groq `chat/completions` API endpoint. For additional details, see: [https://console.groq.com/docs/api-reference#chat-create](https://console.groq.com/docs/api-reference#chat-create) Request throttling: Applies the rate limit set in the config (section `groq`, key `rate_limit`). If no rate limit is configured, uses a default of 600 RPM. **Requirements:** * `pip install groq` **Parameters:** * **`messages`** (`pxt.Json[(Json`): A list of messages comprising the conversation so far. * **`model`** (`Any`): ID of the model to use. (See overview here: [https://console.groq.com/docs/models](https://console.groq.com/docs/models)) * **`model_kwargs`** (`Any`): Additional keyword args for the Groq `chat/completions` API. For details on the available parameters, see: [https://console.groq.com/docs/api-reference#chat-create](https://console.groq.com/docs/api-reference#chat-create) **Returns:** * `pxt.Json`: A dictionary containing the response and other metadata. **Examples:** Add a computed column that applies the model `openai/gpt-oss-20b` to an existing Pixeltable column `tbl.prompt` of the table `tbl`: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} messages = [{'role': 'user', 'content': tbl.prompt}] tbl.add_computed_column( response=chat_completions(messages, model='openai/gpt-oss-20b') ) ``` # huggingface Source: https://docs.pixeltable.com/sdk/latest/huggingface View Source on GitHub # module  pixeltable.functions.huggingface Pixeltable UDFs that wrap various models from the Hugging Face `transformers` package. These UDFs will cause Pixeltable to invoke the relevant models locally. In order to use them, you must first `pip install transformers` (or in some cases, `sentence-transformers`, as noted in the specific UDFs). ## UDFs ## udf  automatic\_speech\_recognition() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf automatic_speech_recognition( audio: pxt.Audio, *, model_id: pxt.String, language: pxt.String | None = None, chunk_length_s: pxt.Int | None = None, return_timestamps: pxt.Bool = False ) -> pxt.String ``` Transcribes speech to text using a pretrained ASR model. `model_id` should be a reference to a pretrained [automatic-speech-recognition model](https://huggingface.co/models?pipeline_tag=automatic-speech-recognition). This is a **generic function** that works with many ASR model families. For production use with specific models, consider specialized functions like `whisper.transcribe()` or `speech2text_for_conditional_generation()`. **Requirements:** * `pip install torch transformers torchaudio torchcodec` **Recommended Models:** * **OpenAI Whisper**: `openai/whisper-tiny.en`, `openai/whisper-small`, `openai/whisper-base` * **Facebook Wav2Vec2**: `facebook/wav2vec2-base-960h`, `facebook/wav2vec2-large-960h-lv60-self` * **Microsoft SpeechT5**: `microsoft/speecht5_asr` * **Meta MMS (Multilingual)**: `facebook/mms-1b-all` **Parameters:** * **`audio`** (`pxt.Audio`): The audio file(s) to transcribe. * **`model_id`** (`pxt.String`): The pretrained ASR model to use. * **`language`** (`pxt.String | None`): Language code for multilingual models (e.g., 'en', 'es', 'fr'). * **`chunk_length_s`** (`pxt.Int | None`): Maximum length of audio chunks in seconds for long audio processing. * **`return_timestamps`** (`pxt.Bool`): Whether to return word-level timestamps (model dependent). **Returns:** * `pxt.String`: The transcribed text. **Examples:** Add a computed column that transcribes audio files: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} tbl.add_computed_column( transcription=automatic_speech_recognition( tbl.audio_file, model_id='openai/whisper-tiny.en', # Recommended ) ) ``` Transcribe with language specification: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} tbl.add_computed_column( transcription=automatic_speech_recognition( tbl.audio_file, model_id='facebook/mms-1b-all', language='en' ) ) ``` ## udf  clip() ```python Signatures theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Signature 1: @pxt.udf clip( text: pxt.String, *, model_id: pxt.String ) -> pxt.Array[(None,), float32] # Signature 2: @pxt.udf clip( image: pxt.Image, *, model_id: pxt.String ) -> pxt.Array[(None,), float32] ``` Computes a CLIP embedding for the specified text or image. `model_id` should be a reference to a pretrained [CLIP Model](https://huggingface.co/docs/transformers/model_doc/clip). **Requirements:** * `pip install torch transformers` **Parameters:** * **`text`** (`String`): The string to embed. * **`model_id`** (`String`): The pretrained model to use for the embedding. **Returns:** * `pxt.Array[(None,), float32]`: An array containing the output of the embedding model. **Examples:** Add a computed column that applies the model `openai/clip-vit-base-patch32` to an existing Pixeltable column `tbl.text` of the table `tbl`: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} tbl.add_computed_column( result=clip(tbl.text, model_id='openai/clip-vit-base-patch32') ) ``` The same would work with an image column `tbl.image` in place of `tbl.text`. ## udf  cross\_encoder() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf cross_encoder( sentences1: pxt.String, sentences2: pxt.String, *, model_id: pxt.String ) -> pxt.Float ``` Performs predicts on the given sentence pair. `model_id` should be a pretrained Cross-Encoder model, as described in the [Cross-Encoder Pretrained Models](https://www.sbert.net/docs/cross_encoder/pretrained_models.html) documentation. **Requirements:** * `pip install torch sentence-transformers` **Parameters:** * **`sentences1`** (`pxt.String`): The first sentence to be paired. * **`sentences2`** (`pxt.String`): The second sentence to be paired. * **`model_id`** (`pxt.String`): The identifier of the cross-encoder model to use. **Returns:** * `pxt.Float`: The similarity score between the inputs. **Examples:** Add a computed column that applies the model `ms-marco-MiniLM-L-4-v2` to the sentences in columns `tbl.sentence1` and `tbl.sentence2`: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} tbl.add_computed_column( result=sentence_transformer( tbl.sentence1, tbl.sentence2, model_id='ms-marco-MiniLM-L-4-v2' ) ) ``` ## udf  detr\_for\_object\_detection() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf detr_for_object_detection( image: pxt.Image, *, model_id: pxt.String, threshold: pxt.Float = 0.5, revision: pxt.String | None = None ) -> DetrForObjectDetectionResponse ``` Computes DETR object detections for the specified image. `model_id` should be a reference to a pretrained [DETR Model](https://huggingface.co/docs/transformers/model_doc/detr). **Requirements:** * `pip install torch transformers` **Parameters:** * **`image`** (`pxt.Image`): The image to embed. * **`model_id`** (`pxt.String`): The pretrained model to use for object detection. * **`threshold`** (`pxt.Float`): Confidence threshold for filtering detections. * **`revision`** (`pxt.String | None`): The specific model revision to use (e.g., a branch, tag, or git identifier). If not specified, uses the default revision for the model (typically `'main'`). **Returns:** * `DetrForObjectDetectionResponse`: A dictionary containing the output of the object detection model, in the following format: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} { # list of confidence scores for each detected object 'scores': [0.99, 0.999], # list of COCO class labels for each detected object 'labels': [25, 25], # corresponding text names of class labels 'label_text': ['giraffe', 'giraffe'], # list of bounding boxes for each detected object, as [x1, y1, x2, y2] 'boxes': [ [51.942, 356.174, 181.481, 413.975], [383.225, 58.66, 605.64, 361.346], ], } ``` **Examples:** Add a computed column that applies the model `facebook/detr-resnet-50` to an existing Pixeltable column `image` of the table `tbl`: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} tbl.add_computed_column( detections=detr_for_object_detection( tbl.image, model_id='facebook/detr-resnet-50', threshold=0.8 ) ) ``` ## udf  detr\_for\_segmentation() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf detr_for_segmentation( image: pxt.Image, *, model_id: pxt.String, threshold: pxt.Float = 0.5 ) -> DetrForSegmentationResponse ``` Computes DETR panoptic segmentation for the specified image. `model_id` should be a reference to a pretrained [DETR Model](https://huggingface.co/docs/transformers/model_doc/detr) with a segmentation head. **Requirements:** * `pip install torch transformers timm` **Parameters:** * **`image`** (`pxt.Image`): The image to segment. * **`model_id`** (`pxt.String`): The pretrained model to use for segmentation (e.g., 'facebook/detr-resnet-50-panoptic'). * **`threshold`** (`pxt.Float`): Confidence threshold for filtering segments. **Returns:** * `DetrForSegmentationResponse`: A dictionary containing the output of the segmentation model, in the following format: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} { 'segmentation': np.ndarray, # (H, W) array where each pixel value is a segment ID 'segments_info': [ { 'id': 1, # segment ID (matches pixel values in segmentation array) 'label_id': 0, # class label index 'label_text': 'person', # human-readable class name 'score': 0.98, # confidence score 'was_fused': False, # whether segment was fused from multiple instances }, ..., ], } ``` **Examples:** Add a computed column that applies the model `facebook/detr-resnet-50-panoptic` to an existing Pixeltable column `image` of the table `tbl`: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} tbl.add_computed_column( segmentation=detr_for_segmentation( tbl.image, model_id='facebook/detr-resnet-50-panoptic', threshold=0.5, ) ) ``` ## udf  detr\_to\_coco() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf detr_to_coco(image: pxt.Image, detr_info: pxt.Json) -> CocoResponse ``` Converts the output of a DETR object detection model to COCO format. **Parameters:** * **`image`** (`pxt.Image`): The image for which detections were computed. * **`detr_info`** (`pxt.Json`): The output of a DETR object detection model, as returned by `detr_for_object_detection`. **Returns:** * `CocoResponse`: A dictionary containing the data from `detr_info`, converted to COCO format. **Examples:** Add a computed column that converts the output `tbl.detections` to COCO format, where `tbl.image` is the image for which detections were computed: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} tbl.add_computed_column( detections_coco=detr_to_coco(tbl.image, tbl.detections) ) ``` ## udf  image\_captioning() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf image_captioning( image: pxt.Image, *, model_id: pxt.String, model_kwargs: pxt.Json | None = None ) -> pxt.String ``` Generates captions for images using a pretrained image captioning model. `model_id` should be a reference to a pretrained [image-to-text model](https://huggingface.co/models?pipeline_tag=image-to-text) such as BLIP, Git, or LLaVA. **Requirements:** * `pip install torch transformers` **Parameters:** * **`image`** (`pxt.Image`): The image to caption. * **`model_id`** (`pxt.String`): The pretrained model to use for captioning. * **`model_kwargs`** (`pxt.Json | None`): Additional keyword arguments to pass to the model's `generate` method, such as `max_length`. **Returns:** * `pxt.String`: The generated caption text. **Examples:** Add a computed column `caption` to an existing table `tbl` that generates captions using the `Salesforce/blip-image-captioning-base` model: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} tbl.add_computed_column( caption=image_captioning( tbl.image, model_id='Salesforce/blip-image-captioning-base', model_kwargs={'max_length': 30}, ) ) ``` ## udf  image\_to\_image() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf image_to_image( image: pxt.Image, prompt: pxt.String, *, model_id: pxt.String, seed: pxt.Int | None = None, model_kwargs: pxt.Json | None = None ) -> pxt.Image ``` Transforms input images based on text prompts using a pretrained image-to-image model. `model_id` should be a reference to a pretrained [image-to-image model](https://huggingface.co/models?pipeline_tag=image-to-image) such as Stable Diffusion. **Requirements:** * `pip install torch transformers diffusers accelerate` **Parameters:** * **`image`** (`pxt.Image`): The input image to transform. * **`prompt`** (`pxt.String`): The text prompt describing the desired transformation. * **`model_id`** (`pxt.String`): The pretrained image-to-image model to use. * **`seed`** (`pxt.Int | None`): Random seed for reproducibility. * **`model_kwargs`** (`pxt.Json | None`): Additional keyword arguments to pass to the model, such as `strength`, `guidance_scale`, or `num_inference_steps`. **Returns:** * `pxt.Image`: The transformed image. **Examples:** Add a computed column that transforms images based on prompts: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} tbl.add_computed_column( transformed=image_to_image( tbl.source_image, tbl.transformation_prompt, model_id='stable-diffusion-v1-5/stable-diffusion-v1-5', ) ) ``` With custom transformation strength: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} tbl.add_computed_column( transformed=image_to_image( tbl.source_image, tbl.transformation_prompt, model_id='stable-diffusion-v1-5/stable-diffusion-v1-5', model_kwargs={'strength': 0.75, 'num_inference_steps': 50}, ) ) ``` ## udf  image\_to\_video() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf image_to_video( image: pxt.Image, *, model_id: pxt.String, num_frames: pxt.Int = 25, fps: pxt.Int = 6, seed: pxt.Int | None = None, model_kwargs: pxt.Json | None = None ) -> pxt.Video ``` Generates videos from input images using a pretrained image-to-video model. `model_id` should be a reference to a pretrained [image-to-video model](https://huggingface.co/models?pipeline_tag=image-to-video). **Requirements:** * `pip install torch transformers diffusers accelerate` **Parameters:** * **`image`** (`pxt.Image`): The input image to animate into a video. * **`model_id`** (`pxt.String`): The pretrained image-to-video model to use. * **`num_frames`** (`pxt.Int`): Number of video frames to generate. * **`fps`** (`pxt.Int`): Frames per second for the output video. * **`seed`** (`pxt.Int | None`): Random seed for reproducibility. * **`model_kwargs`** (`pxt.Json | None`): Additional keyword arguments to pass to the model, such as `num_inference_steps`, `motion_bucket_id`, or `guidance_scale`. **Returns:** * `pxt.Video`: The generated video file. **Examples:** Add a computed column that creates videos from images: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} tbl.add_computed_column( video=image_to_video( tbl.input_image, model_id='stabilityai/stable-video-diffusion-img2vid-xt', num_frames=25, fps=7, ) ) ``` ## udf  question\_answering() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf question_answering( context: pxt.String, question: pxt.String, *, model_id: pxt.String ) -> QuestionAnsweringResponse ``` Answers questions based on provided context using a pretrained QA model. `model_id` should be a reference to a pretrained [question answering model](https://huggingface.co/models?pipeline_tag=question-answering) such as BERT or RoBERTa. **Requirements:** * `pip install torch transformers` **Parameters:** * **`context`** (`pxt.String`): The context text containing the answer. * **`question`** (`pxt.String`): The question to answer. * **`model_id`** (`pxt.String`): The pretrained QA model to use. **Returns:** * `QuestionAnsweringResponse`: A dictionary containing the answer, confidence score, and start/end positions. **Examples:** Add a computed column that answers questions based on document context: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} tbl.add_computed_column( answer=question_answering( tbl.document_text, tbl.question, model_id='deepset/roberta-base-squad2', ) ) ``` ## udf  sam3\_for\_segmentation() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf sam3_for_segmentation( image: pxt.Image, *, model_id: pxt.String = 'facebook/sam3', text: pxt.String | None = None, input_boxes: pxt.Json[(Json[(Float, ...)], ...)] | None = None, input_boxes_labels: pxt.Json[(Int, ...)] | None = None, threshold: pxt.Float = 0.5, mask_threshold: pxt.Float = 0.5, revision: pxt.String | None = None ) -> SamForSegmentationResponse ``` Computes SAM 3 (Segment Anything Model 3) Promptable Concept Segmentation for the specified image. `model_id` should be a reference to a pretrained [SAM 3 Model](https://huggingface.co/docs/transformers/model_doc/sam3) such as `facebook/sam3`. SAM 3 performs Promptable Concept Segmentation (PCS): given a concept prompt (a short text noun phrase, a set of bounding boxes, or both), it detects and segments every object instance in the image that matches the concept. **Requirements:** * `pip install torch torchvision transformers` * `facebook/sam3` is a gated repository. Request access on its [model page](https://huggingface.co/facebook/sam3), then authenticate with `huggingface-cli login` (or set the `HF_TOKEN` environment variable) before calling this UDF. **Parameters:** * **`image`** (`pxt.Image`): The image to segment. * **`model_id`** (`pxt.String`): The pretrained SAM 3 model to use. * **`text`** (`pxt.String | None`): Optional concept prompt as a short noun phrase (e.g., `'yellow school bus'`). * **`input_boxes`** (`pxt.Json[(Json[(Float`): Optional list of bounding boxes in `[x1, y1, x2, y2]` pixel coordinates that constrain the concept (e.g., `[[100, 150, 500, 450]]`). * **`input_boxes_labels`** (`Any`): Optional list of integer labels for each box: `1` for positive (include this region), `0` for negative (exclude this region). If `input_boxes` is provided and this is `None`, all boxes default to positive. * **`threshold`** (`Any`): Confidence threshold for filtering detections. * **`mask_threshold`** (`Any`): Threshold applied to mask logits to produce binary masks. * **`revision`** (`Any`): The specific model revision to use (e.g., a branch, tag, or git identifier). If not specified, uses the default revision for the model. **Returns:** * `SamForSegmentationResponse`: A `SamForSegmentationResponse` containing: * `scores`: confidence score per detected instance, shape `(num_instances,)`. * `boxes`: bounding box `[x1, y1, x2, y2]` per detected instance in absolute pixel coordinates, shape `(num_instances, 4)`. * `masks`: binary mask per detected instance, shape `(num_instances, H, W)`. **Examples:** Add a computed column that segments every "cat" in an existing Pixeltable column `image` of the table `tbl`: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} tbl.add_computed_column(seg=sam3_for_segmentation(tbl.image, text='cat')) ``` Use a bounding-box prompt to segment a specific object: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} tbl.add_computed_column( seg=sam3_for_segmentation( tbl.image, input_boxes=[[100, 150, 500, 450]] ) ) ``` Combine a text prompt with a negative box to exclude a region: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} tbl.add_computed_column( seg=sam3_for_segmentation( tbl.image, text='handle', input_boxes=[[40, 183, 318, 204]], input_boxes_labels=[0], ) ) ``` ## udf  sam\_automatic\_mask\_generation() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf sam_automatic_mask_generation( image: pxt.Image, *, model_id: pxt.String = 'facebook/sam3', points_per_batch: pxt.Int = 64, points_per_crop: pxt.Int = 32, pred_iou_thresh: pxt.Float = 0.7, stability_score_thresh: pxt.Float = 0.95, crops_n_layers: pxt.Int = 0, revision: pxt.String | None = None ) -> SamForSegmentationResponse ``` Segments every object in an image with SAM (Segment Anything Model), with no prompt. `model_id` should be a reference to a pretrained [SAM Model](https://huggingface.co/docs/transformers/model_doc/sam3) such as `facebook/sam3` or `facebook/sam2-hiera-large`. This UDF is not tied to a specific SAM version; it works with any SAM model that supports automatic mask generation. Works by sampling a grid of points across the image and returns a binary mask for every distinct object it finds (the "segment everything" mode of SAM 1 and SAM 2). Masks are filtered by predicted quality and de-duplicated. The masks are class-agnostic: no labels or persistent object ids are produced, since the model is not told what to look for. **Requirements:** * `pip install torch transformers` * `facebook/sam3` is a gated repository. Request access on its [model page](https://huggingface.co/facebook/sam3), then authenticate with `huggingface-cli login` (or set the `HF_TOKEN` environment variable) before calling this UDF. **Parameters:** * **`image`** (`pxt.Image`): The image to segment. * **`model_id`** (`pxt.String`): The pretrained SAM model to use. * **`points_per_batch`** (`pxt.Int`): Number of grid points run through the model at once. Higher values are faster but use more memory. * **`points_per_crop`** (`pxt.Int`): Number of grid points sampled along each side of the image. Higher values find more (and smaller) objects at the cost of speed. * **`pred_iou_thresh`** (`pxt.Float`): Quality threshold on the model's predicted mask IoU; masks scoring below it are discarded. * **`stability_score_thresh`** (`pxt.Float`): Quality threshold on mask stability under mask-logit threshold changes; masks scoring below it are discarded. * **`crops_n_layers`** (`pxt.Int`): Number of additional image-crop layers to run. Values above `0` improve recall on small objects at the cost of speed. * **`revision`** (`pxt.String | None`): The specific model revision to use (e.g., a branch, tag, or git identifier). If not specified, uses the default revision for the model. **Returns:** * `SamForSegmentationResponse`: A `SamForSegmentationResponse` containing: * `scores`: predicted quality score per mask, shape `(num_masks,)`. * `boxes`: bounding box `[x1, y1, x2, y2]` per mask in absolute pixel coordinates, shape `(num_masks, 4)`. * `masks`: binary mask per detected object, shape `(num_masks, H, W)`. **Examples:** Add a computed column that segments every object in an existing Pixeltable column `image` of the table `tbl`: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} tbl.add_computed_column(seg=sam_automatic_mask_generation(tbl.image)) ``` ## udf  sentence\_transformer() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf sentence_transformer( sentence: pxt.String, *, model_id: pxt.String, normalize_embeddings: pxt.Bool = False ) -> pxt.Array[(None,), float32] ``` Computes sentence embeddings. `model_id` should be a pretrained Sentence Transformers model, as described in the [Sentence Transformers Pretrained Models](https://sbert.net/docs/sentence_transformer/pretrained_models.html) documentation. **Requirements:** * `pip install torch sentence-transformers` **Parameters:** * **`sentence`** (`pxt.String`): The sentence to embed. * **`model_id`** (`pxt.String`): The pretrained model to use for the encoding. * **`normalize_embeddings`** (`pxt.Bool`): If `True`, normalizes embeddings to length 1; see the [Sentence Transformers API Docs](https://sbert.net/docs/package_reference/sentence_transformer/SentenceTransformer.html) for more details **Returns:** * `pxt.Array[(None,), float32]`: An array containing the output of the embedding model. **Examples:** Add a computed column that applies the model `all-mpnet-base-2` to an existing Pixeltable column `tbl.sentence` of the table `tbl`: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} tbl.add_computed_column( result=sentence_transformer( tbl.sentence, model_id='all-mpnet-base-v2' ) ) ``` ## udf  speech2text\_for\_conditional\_generation() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf speech2text_for_conditional_generation( audio: pxt.Audio, *, model_id: pxt.String, language: pxt.String | None = None ) -> pxt.String ``` Transcribes or translates speech to text using a Speech2Text model. `model_id` should be a reference to a pretrained [Speech2Text](https://huggingface.co/docs/transformers/en/model_doc/speech_to_text) model. **Requirements:** * `pip install torch torchaudio sentencepiece transformers` **Parameters:** * **`audio`** (`pxt.Audio`): The audio clip to transcribe or translate. * **`model_id`** (`pxt.String`): The pretrained model to use for the transcription or translation. * **`language`** (`pxt.String | None`): If using a multilingual translation model, the language code to translate to. If not provided, the model's default language will be used. If the model is not translation model, is not a multilingual model, or does not support the specified language, an error will be raised. **Returns:** * `pxt.String`: The transcribed or translated text. **Examples:** Add a computed column that applies the model `facebook/s2t-small-librispeech-asr` to an existing Pixeltable column `audio` of the table `tbl`: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} tbl.add_computed_column( transcription=speech2text_for_conditional_generation( tbl.audio, model_id='facebook/s2t-small-librispeech-asr' ) ) ``` Add a computed column that applies the model `facebook/s2t-medium-mustc-multilingual-st` to an existing Pixeltable column `audio` of the table `tbl`, translating the audio to French: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} tbl.add_computed_column( translation=speech2text_for_conditional_generation( tbl.audio, model_id='facebook/s2t-medium-mustc-multilingual-st', language='fr', ) ) ``` ## udf  summarization() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf summarization( text: pxt.String, *, model_id: pxt.String, model_kwargs: pxt.Json | None = None ) -> pxt.String ``` Summarizes text using a pretrained summarization model. `model_id` should be a reference to a pretrained [summarization model](https://huggingface.co/models?pipeline_tag=summarization) such as BART, T5, or Pegasus. **Requirements:** * `pip install torch transformers` **Parameters:** * **`text`** (`pxt.String`): The text to summarize. * **`model_id`** (`pxt.String`): The pretrained model to use for summarization. * **`model_kwargs`** (`pxt.Json | None`): Additional keyword arguments to pass to the model's `generate` method, such as `max_length`. **Returns:** * `pxt.String`: The generated summary text. **Examples:** Add a computed column that summarizes documents: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} tbl.add_computed_column( summary=text_summarization( tbl.document_text, model_id='facebook/bart-large-cnn', max_length=100, ) ) ``` ## udf  text\_classification() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf text_classification( text: pxt.String, *, model_id: pxt.String, top_k: pxt.Int = 5 ) -> pxt.Json[(Json[{'label': Int, 'label_text': String, 'score': Float}], ...)] ``` Classifies text using a pretrained classification model. `model_id` should be a reference to a pretrained [text classification model](https://huggingface.co/models?pipeline_tag=text-classification) such as BERT, RoBERTa, or DistilBERT. **Requirements:** * `pip install torch transformers` **Parameters:** * **`text`** (`pxt.String`): The text to classify. * **`model_id`** (`pxt.String`): The pretrained model to use for classification. * **`top_k`** (`pxt.Int`): The number of top predictions to return. **Returns:** * `pxt.Json[(Json[{'label': Int, 'label_text': String, 'score': Float}], ...)]`: A dictionary containing classification results with scores, labels, and label text. **Examples:** Add a computed column for sentiment analysis: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} tbl.add_computed_column( sentiment=text_classification( tbl.review_text, model_id='cardiffnlp/twitter-roberta-base-sentiment-latest', ) ) ``` ## udf  text\_generation() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf text_generation( text: pxt.String, *, model_id: pxt.String, model_kwargs: pxt.Json | None = None ) -> pxt.String ``` Generates text using a pretrained language model. `model_id` should be a reference to a pretrained [text generation model](https://huggingface.co/models?pipeline_tag=text-generation). **Requirements:** * `pip install torch transformers` **Parameters:** * **`text`** (`pxt.String`): The input text to continue/complete. * **`model_id`** (`pxt.String`): The pretrained model to use for text generation. * **`model_kwargs`** (`pxt.Json | None`): Additional keyword arguments to pass to the model's `generate` method, such as `max_length`, `temperature`, etc. See the [Hugging Face text\_generation documentation](https://huggingface.co/docs/inference-providers/en/tasks/text-generation) for details. **Returns:** * `pxt.String`: The generated text completion. **Examples:** Add a computed column that generates text completions using the `Qwen/Qwen3-0.6B` model: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} tbl.add_computed_column( completion=text_generation( tbl.prompt, model_id='Qwen/Qwen3-0.6B', model_kwargs={'temperature': 0.5, 'max_length': 150}, ) ) ``` ## udf  text\_to\_image() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf text_to_image( prompt: pxt.String, *, model_id: pxt.String, height: pxt.Int = 512, width: pxt.Int = 512, seed: pxt.Int | None = None, model_kwargs: pxt.Json | None = None ) -> pxt.Image ``` Generates images from text prompts using a pretrained text-to-image model. `model_id` should be a reference to a pretrained [text-to-image model](https://huggingface.co/models?pipeline_tag=text-to-image) such as Stable Diffusion. **Requirements:** * `pip install torch transformers diffusers accelerate` **Parameters:** * **`prompt`** (`pxt.String`): The text prompt describing the desired image. * **`model_id`** (`pxt.String`): The pretrained text-to-image model to use. * **`height`** (`pxt.Int`): Height of the generated image in pixels. * **`width`** (`pxt.Int`): Width of the generated image in pixels. * **`seed`** (`pxt.Int | None`): Optional random seed for reproducibility. * **`model_kwargs`** (`pxt.Json | None`): Additional keyword arguments to pass to the model, such as `num_inference_steps`, `guidance_scale`, or `negative_prompt`. **Returns:** * `pxt.Image`: The generated Image. **Examples:** Add a computed column that generates images from text prompts: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} tbl.add_computed_column( generated_image=text_to_image( tbl.prompt, model_id='stable-diffusion-v1.5/stable-diffusion-v1-5', height=512, width=512, model_kwargs={'num_inference_steps': 25}, ) ) ``` ## udf  text\_to\_speech() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf text_to_speech( text: pxt.String, *, model_id: pxt.String, speaker_id: pxt.Int | None = None, vocoder: pxt.String | None = None ) -> pxt.Audio ``` Converts text to speech using a pretrained TTS model. `model_id` should be a reference to a pretrained [text-to-speech model](https://huggingface.co/models?pipeline_tag=text-to-speech). **Requirements:** * `pip install torch transformers datasets soundfile` **Parameters:** * **`text`** (`pxt.String`): The text to convert to speech. * **`model_id`** (`pxt.String`): The pretrained TTS model to use. * **`speaker_id`** (`pxt.Int | None`): Speaker ID for multi-speaker models. * **`vocoder`** (`pxt.String | None`): Optional vocoder model for higher quality audio. **Returns:** * `pxt.Audio`: The generated audio file. **Examples:** Add a computed column that converts text to speech: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} tbl.add_computed_column( audio=text_to_speech( tbl.text_content, model_id='microsoft/speecht5_tts', speaker_id=0 ) ) ``` ## udf  token\_classification() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf token_classification( text: pxt.String, *, model_id: pxt.String, aggregation_strategy: pxt.String = 'simple' ) -> pxt.Json[(Json[{'word': String, 'entity_group': String, 'score': Float, 'start': Int, 'end': Int}], ...)] ``` Extracts named entities from text using a pretrained named entity recognition (NER) model. `model_id` should be a reference to a pretrained [token classification model](https://huggingface.co/models?pipeline_tag=token-classification) for NER. **Requirements:** * `pip install torch transformers` **Parameters:** * **`text`** (`pxt.String`): The text to analyze for named entities. * **`model_id`** (`pxt.String`): The pretrained model to use. * **`aggregation_strategy`** (`pxt.String`): Method used to aggregate tokens. **Returns:** * `pxt.Json[(Json[{'word': String, 'entity_group': String, 'score': Float, 'start': Int, 'end': Int}], ...)]`: A list of dictionaries containing entity information (text, label, confidence, start, end). **Examples:** Add a computed column that extracts named entities: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} tbl.add_computed_column( entities=token_classification( tbl.text, model_id='dbmdz/bert-large-cased-finetuned-conll03-english', ) ) ``` ## udf  translation() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf translation( text: pxt.String, *, model_id: pxt.String, src_lang: pxt.String | None = None, target_lang: pxt.String | None = None ) -> pxt.String ``` Translates text using a pretrained translation model. `model_id` should be a reference to a pretrained [translation model](https://huggingface.co/models?pipeline_tag=translation) such as MarianMT or T5. **Requirements:** * `pip install torch transformers sentencepiece` **Parameters:** * **`text`** (`pxt.String`): The text to translate. * **`model_id`** (`pxt.String`): The pretrained translation model to use. * **`src_lang`** (`pxt.String | None`): Source language code (optional, can be inferred from model). * **`target_lang`** (`pxt.String | None`): Target language code (optional, can be inferred from model). **Returns:** * `pxt.String`: The translated text. **Examples:** Add a computed column that translates text: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} tbl.add_computed_column( french_text=translation( tbl.english_text, model_id='Helsinki-NLP/opus-mt-en-fr', src_lang='en', target_lang='fr', ) ) ``` ## udf  vit\_for\_image\_classification() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf vit_for_image_classification( image: pxt.Image, *, model_id: pxt.String, top_k: pxt.Int = 5 ) -> VitForImageClassificationResponse ``` Computes image classifications for the specified image using a Vision Transformer (ViT) model. `model_id` should be a reference to a pretrained [ViT Model](https://huggingface.co/docs/transformers/en/model_doc/vit). **Note:** Be sure the model is a ViT model that is trained for image classification (that is, a model designed for use with the [ViTForImageClassification](https://huggingface.co/docs/transformers/en/model_doc/vit#transformers.ViTForImageClassification) class), such as `google/vit-base-patch16-224`. General feature-extraction models such as `google/vit-base-patch16-224-in21k` will not produce the desired results. **Requirements:** * `pip install torch transformers` **Parameters:** * **`image`** (`pxt.Image`): The image to classify. * **`model_id`** (`pxt.String`): The pretrained model to use for the classification. * **`top_k`** (`pxt.Int`): The number of classes to return. **Returns:** * `VitForImageClassificationResponse`: A dictionary containing the output of the image classification model, in the following format: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} { # list of probabilities of the top-k most likely classes 'scores': [0.325, 0.198, 0.105], # list of class IDs for the top-k most likely classes 'labels': [340, 353, 386], # corresponding text names of the top-k most likely classes 'label_text': [ 'zebra', 'gazelle', 'African elephant, Loxodonta africana', ], } ``` **Examples:** Add a computed column that applies the model `google/vit-base-patch16-224` to an existing Pixeltable column `image` of the table `tbl`, returning the 10 most likely classes for each image: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} tbl.add_computed_column( image_class=vit_for_image_classification( tbl.image, model_id='google/vit-base-patch16-224', top_k=10 ) ) ``` # image Source: https://docs.pixeltable.com/sdk/latest/image View Source on GitHub # module  pixeltable.functions.image Pixeltable UDFs for `ImageType`. Example: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} import pixeltable as pxt t = pxt.get_table(...) t.select(t.img_col.convert('L')).collect() ``` ## iterator  tile\_iterator() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.iterator tile_iterator( image: pxt.Image, tile_size: pxt.Json[(Int, Int)], *, overlap: pxt.Json[(Int, Int)] = (0, 0) ) ``` Iterator over tiles of an image. Each image will be divided into tiles of size `tile_size`, and the tiles will be iterated over in row-major order (left-to-right, then top-to-bottom). An optional `overlap` parameter may be specified. If the tiles do not exactly cover the image, then the rightmost and bottommost tiles will be padded with blackspace, so that the output images all have the exact size `tile_size`. **Outputs**: One row per tile, with the following columns: * `tile` (`pxt.Image`): The image tile * `tile_coord` (`pxt.Json`): The (x, y) coordinates of the tile in the grid of tiles * `tile_box` (`pxt.Json`): The (x1, y1, x2, y2) pixel coordinates of the tile in the original image **Parameters:** * **`image`** (`pxt.Image`): Image to split into tiles. * **`tile_size`** (`pxt.Json[(Int`): Size of each tile, as a pair of integers `(width, height)`. * **`overlap`** (`Any`): Amount of overlap between adjacent tiles, as a pair of integers `(width, height)`. **Examples:** This example assumes an existing table `tbl` with a column `img` of type `pxt.Image`. Create a view that splits all images into 256x256 tiles with 32 pixels of overlap: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} pxt.create_view( 'image_tiles', tbl, iterator=tile_iterator( tbl.img, tile_size=(256, 256), overlap=(32, 32) ), ) ``` ## uda  stitch\_tiles() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.uda stitch_tiles(*args, **kwargs) -> pxt.Image | None ``` Reconstructs a single image from the tiles produced by [`tile_iterator`](./image#iterator-tile_iterator), pasting each tile back at its original position. This is the inverse of [`tile_iterator`](./image#iterator-tile_iterator): aggregate the tiles of an image (grouped by a column that identifies the source image) to obtain the full image, optionally after transforming each tile first (e.g., drawing a segmentation overlay on it). The reconstruction is naive: tiles are pasted in the order they arrive, so where tiles overlap, later tiles overwrite earlier ones. It composites pixels and does not merge or deduplicate detections across tile seams. Call as `stitch_tiles(order_key, tile, tile_box, width, height)`. `order_by=` is invalid. Args: order\_key: Positional only. The expression that orders the tiles within a group, typically the tile position emitted by `tile_iterator`. tile: The image tile to paste, the same size as the tiles emitted by [`tile_iterator`](./image#iterator-tile_iterator). All tiles in a group must have the same mode (and palette, for palette images). tile\_box: The `(x1, y1, x2, y2)` pixel coordinates of the tile in the original image, as emitted by `tile_iterator`. width: The width of the original image, in pixels. height: The height of the original image, in pixels. Returns: The reconstructed image, of size `(width, height)` and in the mode of the tiles, or `None` if there are no non-null tiles. Examples: Stitch per-tile overlays back into one full-resolution image per source image. Assumes a view `tiles` created with [`tile_iterator`](./image#iterator-tile_iterator), with an `overlay` column computed per tile: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} tiles.group_by(tiles.image).select( stitched=stitch_tiles( tiles.pos, tiles.overlay, tiles.tile_box, tiles.image.width, tiles.image.height, ) ).collect() ``` For the full pipeline, from tiling through segmentation to stitching, see the [Promptable segmentation with SAM 3](https://docs.pixeltable.com/howto/cookbooks/images/img-promptable-segmentation) cookbook. ## udf  alpha\_composite() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf alpha_composite(im1: pxt.Image, im2: pxt.Image) -> pxt.Image ``` Alpha composite `im2` over `im1`. Equivalent to [`PIL.Image.alpha_composite()`](https://pillow.readthedocs.io/en/stable/reference/Image.html#PIL.Image.alpha_composite) ## udf  b64\_encode() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf b64_encode( img: pxt.Image, image_format: pxt.String = 'png' ) -> pxt.String ``` Convert image to a base64-encoded string. **Parameters:** * **`img`** (`pxt.Image`): image * **`image_format`** (`pxt.String`): image format [supported by PIL](https://pillow.readthedocs.io/en/stable/handbook/image-file-formats.html#fully-supported-formats) ## udf  blend() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf blend( im1: pxt.Image, im2: pxt.Image, alpha: pxt.Float ) -> pxt.Image ``` Return a new image by interpolating between two input images, using a constant alpha. Equivalent to [`PIL.Image.blend()`](https://pillow.readthedocs.io/en/stable/reference/Image.html#PIL.Image.blend) ## udf  composite() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf composite( image1: pxt.Image, image2: pxt.Image, mask: pxt.Image ) -> pxt.Image ``` Return a composite image by blending two images using a mask. Equivalent to [`PIL.Image.composite()`](https://pillow.readthedocs.io/en/stable/reference/Image.html#PIL.Image.composite) ## udf  convert() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf convert(self: pxt.Image, mode: pxt.String) -> pxt.Image ``` Convert the image to a different mode. Equivalent to [`PIL.Image.Image.convert()`](https://pillow.readthedocs.io/en/stable/reference/Image.html#PIL.Image.Image.convert). **Parameters:** * **`mode`** (`pxt.String`): The mode to convert to. See the [Pillow documentation](https://pillow.readthedocs.io/en/stable/handbook/concepts.html#concept-modes) for a list of supported modes. ## udf  crop() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf crop( self: pxt.Image, box: pxt.Json[(Int, Int, Int, Int)] ) -> pxt.Image ``` Return a rectangular region from the image. The box is a 4-tuple defining the left, upper, right, and lower pixel coordinates. Equivalent to [`PIL.Image.Image.crop()`](https://pillow.readthedocs.io/en/stable/reference/Image.html#PIL.Image.Image.crop) ## udf  effect\_spread() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf effect_spread(self: pxt.Image, distance: pxt.Int) -> pxt.Image ``` Randomly spread pixels in an image. Equivalent to [`PIL.Image.Image.effect_spread()`](https://pillow.readthedocs.io/en/stable/reference/Image.html#PIL.Image.Image.effect_spread) **Parameters:** * **`distance`** (`pxt.Int`): The distance to spread pixels. ## udf  entropy() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf entropy( self: pxt.Image, mask: pxt.Image | None = None, extrema: pxt.Json[(Json, ...)] | None = None ) -> pxt.Float ``` Returns the entropy of the image, optionally using a mask and extrema. Equivalent to [`PIL.Image.Image.entropy()`](https://pillow.readthedocs.io/en/stable/reference/Image.html#PIL.Image.Image.entropy) **Parameters:** * **`mask`** (`pxt.Image | None`): An optional mask image. * **`extrema`** (`pxt.Json[(Json`): An optional list of extrema. ## udf  get\_metadata() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf get_metadata(self: pxt.Image) -> ImageMetadata ``` Return metadata for the image. ## udf  getbands() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf getbands(self: pxt.Image) -> pxt.Json[(String, ...)] ``` Return a tuple containing the names of the image bands. Equivalent to [`PIL.Image.Image.getbands()`](https://pillow.readthedocs.io/en/stable/reference/Image.html#PIL.Image.Image.getbands) ## udf  getbbox() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf getbbox( self: pxt.Image, *, alpha_only: pxt.Bool = True ) -> pxt.Json[(Int, Int, Int, Int)] | None ``` Return a bounding box for the non-zero regions of the image. Equivalent to [`PIL.Image.Image.getbbox()`](https://pillow.readthedocs.io/en/stable/reference/Image.html#PIL.Image.Image.getbbox) **Parameters:** * **`alpha_only`** (`pxt.Bool`): If `True`, and the image has an alpha channel, trim transparent pixels. Otherwise, trim pixels when all channels are zero. ## udf  getchannel() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf getchannel(self: pxt.Image, channel: pxt.Int) -> pxt.Image ``` Return an L-mode image containing a single channel of the original image. Equivalent to [`PIL.Image.Image.getchannel()`](https://pillow.readthedocs.io/en/stable/reference/Image.html#PIL.Image.Image.getchannel) **Parameters:** * **`channel`** (`pxt.Int`): The channel to extract. This is a 0-based index. ## udf  getcolors() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf getcolors( self: pxt.Image, maxcolors: pxt.Int = 256 ) -> pxt.Json[(Json[(Int, Int)], ...)] ``` Return a list of colors used in the image, up to a maximum of `maxcolors`. Equivalent to [`PIL.Image.Image.getcolors()`](https://pillow.readthedocs.io/en/stable/reference/Image.html#PIL.Image.Image.getcolors) **Parameters:** * **`maxcolors`** (`pxt.Int`): The maximum number of colors to return. ## udf  getextrema() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf getextrema(self: pxt.Image) -> pxt.Json[(Int, Int)] ``` Return a 2-tuple containing the minimum and maximum pixel values of the image. Equivalent to [`PIL.Image.Image.getextrema()`](https://pillow.readthedocs.io/en/stable/reference/Image.html#PIL.Image.Image.getextrema) ## udf  getpalette() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf getpalette( self: pxt.Image, mode: pxt.String | None = None ) -> pxt.Json[(Int, ...)] | None ``` Return the palette of the image, optionally converting it to a different mode. Equivalent to [`PIL.Image.Image.getpalette()`](https://pillow.readthedocs.io/en/stable/reference/Image.html#PIL.Image.Image.getpalette) **Parameters:** * **`mode`** (`pxt.String | None`): The mode to convert the palette to. ## udf  getpixel() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf getpixel( self: pxt.Image, xy: pxt.Json[(Json, ...)] ) -> pxt.Json[(Int,)] ``` Return the pixel value at the given position. The position `xy` is a tuple containing the x and y coordinates. Equivalent to [`PIL.Image.Image.getpixel()`](https://pillow.readthedocs.io/en/stable/reference/Image.html#PIL.Image.Image.getpixel) **Parameters:** * **`xy`** (`pxt.Json[(Json`): The coordinates, given as (x, y). ## udf  getprojection() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf getprojection(self: pxt.Image) -> pxt.Json[(Json[(Int, ...)], Json[(Int, ...)])] ``` Return two sequences representing the horizontal and vertical projection of the image. Equivalent to [`PIL.Image.Image.getprojection()`](https://pillow.readthedocs.io/en/stable/reference/Image.html#PIL.Image.Image.getprojection) ## udf  height() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf height(self: pxt.Image) -> pxt.Int ``` Return the height of the image. ## udf  histogram() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf histogram( self: pxt.Image, mask: pxt.Image | None = None, extrema: pxt.Json[(Json, ...)] | None = None ) -> pxt.Json[(Int, ...)] ``` Return a histogram for the image. Equivalent to [`PIL.Image.Image.histogram()`](https://pillow.readthedocs.io/en/stable/reference/Image.html#PIL.Image.Image.histogram) **Parameters:** * **`mask`** (`pxt.Image | None`): An optional mask image. * **`extrema`** (`pxt.Json[(Json`): An optional list of extrema. ## udf  mode() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf mode(self: pxt.Image) -> pxt.String ``` Return the image mode. ## udf  point() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf point( self: pxt.Image, lut: pxt.Json[(Int, ...)], mode: pxt.String | None = None ) -> pxt.Image ``` Map image pixels through a lookup table. Equivalent to [`PIL.Image.Image.point()`](https://pillow.readthedocs.io/en/stable/reference/Image.html#PIL.Image.Image.point) **Parameters:** * **`lut`** (`pxt.Json[(Int`): A lookup table. ## udf  quantize() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf quantize( self: pxt.Image, colors: pxt.Int = 256, method: pxt.Int | None = None, kmeans: pxt.Int = 0, palette: pxt.Image | None = None, dither: pxt.Int = ) -> pxt.Image ``` Convert the image to 'P' mode with the specified number of colors. Equivalent to [`PIL.Image.Image.quantize()`](https://pillow.readthedocs.io/en/stable/reference/Image.html#PIL.Image.Image.quantize) **Parameters:** * **`colors`** (`pxt.Int`): The number of colors to quantize to. * **`method`** (`pxt.Int | None`): The quantization method. See the [Pillow documentation](https://pillow.readthedocs.io/en/stable/reference/Image.html#PIL.Image.Image.quantize) for a list of supported methods. * **`kmeans`** (`pxt.Int`): The number of k-means clusters to use. * **`palette`** (`pxt.Image | None`): The palette to use. * **`dither`** (`pxt.Int`): The dithering method. See the [Pillow documentation](https://pillow.readthedocs.io/en/stable/reference/Image.html#PIL.Image.Image.quantize) for a list of supported methods. ## udf  reduce() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf reduce( self: pxt.Image, factor: pxt.Int, box: pxt.Json[(Int, Int, Int, Int)] | None = None ) -> pxt.Image ``` Reduce the image by the given factor. Equivalent to [`PIL.Image.Image.reduce()`](https://pillow.readthedocs.io/en/stable/reference/Image.html#PIL.Image.Image.reduce) **Parameters:** * **`factor`** (`pxt.Int`): The reduction factor. * **`box`** (`pxt.Json[(Int`): An optional 4-tuple of ints providing the source image region to be reduced. The values must be within (0, 0, width, height) rectangle. If omitted or None, the entire source is used. ## udf  resize() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf resize(self: pxt.Image, size: pxt.Json[(Int, Int)]) -> pxt.Image ``` Return a resized copy of the image. The size parameter is a tuple containing the width and height of the new image. Equivalent to [`PIL.Image.Image.resize()`](https://pillow.readthedocs.io/en/stable/reference/Image.html#PIL.Image.Image.resize) ## udf  rotate() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf rotate(self: pxt.Image, angle: pxt.Int) -> pxt.Image ``` Return a copy of the image rotated by the given angle. Equivalent to [`PIL.Image.Image.rotate()`](https://pillow.readthedocs.io/en/stable/reference/Image.html#PIL.Image.Image.rotate) **Parameters:** * **`angle`** (`pxt.Int`): The angle to rotate the image, in degrees. Positive angles are counter-clockwise. ## udf  thumbnail() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf thumbnail( self: pxt.Image, size: pxt.Json[(Int, Int)], resample: pxt.Int = , reducing_gap: pxt.Float | None = 2.0 ) -> pxt.Image ``` Create a thumbnail of the image. Equivalent to [`PIL.Image.Image.thumbnail()`](https://pillow.readthedocs.io/en/stable/reference/Image.html#PIL.Image.Image.thumbnail) **Parameters:** * **`size`** (`pxt.Json[(Int`): The size of the thumbnail, as a tuple of (width, height). * **`resample`** (`Any`): The resampling filter to use. See the [Pillow documentation](https://pillow.readthedocs.io/en/stable/reference/Image.html#PIL.Image.Image.thumbnail) for a list of supported filters. * **`reducing_gap`** (`Any`): The reducing gap to use. ## udf  to\_video() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf to_video( image: pxt.Image, *, duration: pxt.Float, fps: pxt.Int = 24, video_encoder: pxt.String | None = None, video_encoder_args: pxt.Json | None = None ) -> pxt.Video ``` Convert a still image into a video of a specified duration with ffmpeg's `-loop` option. **Requirements:** * `ffmpeg` needs to be installed and in PATH **Parameters:** * **`image`** (`pxt.Image`): Input image to convert to video. * **`duration`** (`pxt.Float`): Duration of the output video in seconds. * **`fps`** (`pxt.Int`): Frames per second for the output video. * **`video_encoder`** (`pxt.String | None`): Video encoder to use. If not specified, uses the default encoder. * **`video_encoder_args`** (`pxt.Json | None`): Additional arguments to pass to the video encoder. **Returns:** * `pxt.Video`: A video displaying the input image for the specified duration. **Examples:** Create a 5-second video from an image: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} tbl.select(tbl.image.to_video(duration=5.0)).collect() ``` Create a 10-second video at 30 fps from a rotated image: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} tbl.select( tbl.image.rotate(180).to_video(duration=10.0, fps=30) ).collect() ``` ## udf  transpose() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf transpose(self: pxt.Image, method: pxt.Int) -> pxt.Image ``` Transpose the image. Equivalent to [`PIL.Image.Image.transpose()`](https://pillow.readthedocs.io/en/stable/reference/Image.html#PIL.Image.Image.transpose) **Parameters:** * **`method`** (`pxt.Int`): The transpose method. See the [Pillow documentation](https://pillow.readthedocs.io/en/stable/reference/Image.html#PIL.Image.Image.transpose) for a list of supported methods. ## udf  width() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf width(self: pxt.Image) -> pxt.Int ``` Return the width of the image. # IndexMetadata Source: https://docs.pixeltable.com/sdk/latest/indexmetadata View Source on GitHub # class  pixeltable.IndexMetadata Metadata for an index on a Pixeltable table. ## attr  columns ``` columns: list[str] ``` The table columns that are indexed. ## attr  index\_type ``` index_type: Literal['embedding', 'btree'] ``` The type of index. New types may be added in the future. ## attr  name ``` name: str ``` The name of the index. ## attr  parameters ``` parameters: EmbeddingIndexParams | None ``` Parameters specific to the index type. `None` for index types without parameters. # io Source: https://docs.pixeltable.com/sdk/latest/io View Source on GitHub # module  pixeltable.io Functions for importing and exporting Pixeltable data. ## func  export\_csv() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} export_csv( table_or_query: pxt.Table | pxt.Query, file_path: str | Path, *, delimiter: str = ',', quoting: int = 0 ) -> None ``` Exports a query result or table to a CSV file. Pixeltable column types are mapped to CSV values as follows: * String, Int, Float, Bool: native CSV representation * Timestamp, Date: ISO 8601 string representation * UUID: string representation * Json: JSON-encoded string * Array: JSON-encoded string (via `tolist()`) * Binary: excluded from export (not representable in CSV) * Image, Video, Audio, Document: file path or URL string **Parameters:** * **`table_or_query`** (`pxt.Table | pxt.Query`): Table or Query to export. * **`file_path`** (`str | Path`): Path to the output CSV file. * **`delimiter`** (`str`, default: `','`): Field delimiter character. Default `','`. * **`quoting`** (`int`, default: `0`): CSV quoting style (a `csv.QUOTE_*` constant). Default `csv.QUOTE_MINIMAL`. ## func  export\_iceberg() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} export_iceberg( table_or_query: pxt.Table | pxt.Query, catalog: Catalog, table_name: str, *, batch_size_bytes: int = 134217728, if_exists: Literal['error', 'replace', 'append'] = 'error', schema_overrides: Mapping[str, pa.DataType] | None = None ) -> None ``` Exports a query result or table to an Apache Iceberg table. Data is streamed into the Iceberg table via pyarrow [`RecordBatches`](https://arrow.apache.org/docs/python/generated/pyarrow.RecordBatch.html), the size of which can be controlled with the `batch_size_bytes` parameter. **Requirements:** * `pip install pyiceberg` **Parameters:** * **`table_or_query`** (`pxt.Table | pxt.Query`): Pixeltable `Table` or `Query` to export. * **`catalog`** (`Catalog`): An Iceberg `Catalog` instance to write the table into. * **`table_name`** (`str`): Fully-qualified Iceberg table identifier (e.g. `'my_namespace.my_table'`). If the namespace does not exist, it will be created. * **`batch_size_bytes`** (`int`, default: `134217728`): Maximum size in bytes for each in-memory pyarrow batch. * **`if_exists`** (`Literal['error', 'replace', 'append']`, default: `'error'`): Determines the behavior if the table already exists. Must be one of the following: * `'error'`: raise an error * `'replace'`: drop the existing table and create a new one * `'append'`: append to the existing table (source schema must be compatible) * **`schema_overrides`** (`Mapping[str, pa.DataType] | None`): If specified, then for each (name, type) pair in `schema_overrides`, the column with name `name` will be given type `type`, instead of being inferred from the Pixeltable schema. The keys in `schema_overrides` should be the column names of the Pixeltable schema. ## func  export\_images\_as\_fo\_dataset() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} export_images_as_fo_dataset( tbl: pxt.Table, images: exprs.Expr, image_format: str = 'webp', classifications: exprs.Expr | list[exprs.Expr] | dict[str, exprs.Expr] | None = None, detections: exprs.Expr | list[exprs.Expr] | dict[str, exprs.Expr] | None = None ) -> fo.Dataset ``` Export images from a Pixeltable table as a Voxel51 dataset. The data must consist of a single column (or expression) containing image data, along with optional additional columns containing labels. Currently, only classification and detection labels are supported. The [Working with Voxel51 in Pixeltable](https://docs.pixeltable.com/howto/working-with-fiftyone) tutorial contains a fully worked example showing how to export data from a Pixeltable table and load it into Voxel51. Images in the dataset that already exist on disk will be exported directly, in whatever format they are stored in. Images that are not already on disk (such as frames extracted using a [`frame_iterator`](./video#iterator-frame_iterator)) will first be written to disk in the specified `image_format`. The label parameters accept one or more sets of labels of each type. If a single `Expr` is provided, then it will be exported as a single set of labels with a default name such as `classifications`. (The single set of labels may still containing multiple individual labels; see below.) If a list of `Expr`s is provided, then each one will be exported as a separate set of labels with a default name such as `classifications`, `classifications_1`, etc. If a dictionary of `Expr`s is provided, then each entry will be exported as a set of labels with the specified name. **Requirements:** * `pip install fiftyone` **Parameters:** * **`tbl`** (`pxt.Table`): The table from which to export data. * **`images`** (`exprs.Expr`): A column or expression that contains the images to export. * **`image_format`** (`str`, default: `'webp'`): The format to use when writing out images for export. * **`classifications`** (`exprs.Expr | list[exprs.Expr] | dict[str, exprs.Expr] | None`): Optional image classification labels. If a single `Expr` is provided, it must be a table column or an expression that evaluates to a list of dictionaries. Each dictionary in the list corresponds to an image class and must have the following structure: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} {'label': 'zebra', 'confidence': 0.325} ``` If multiple `Expr`s are provided, each one must evaluate to a list of such dictionaries. * **`detections`** (`exprs.Expr | list[exprs.Expr] | dict[str, exprs.Expr] | None`): Optional image detection labels. If a single `Expr` is provided, it must be a table column or an expression that evaluates to a list of dictionaries. Each dictionary in the list corresponds to an image detection, and must have the following structure: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} { 'label': 'giraffe', 'confidence': 0.99, # [x, y, w, h], fractional coordinates 'bounding_box': [0.081, 0.836, 0.202, 0.136], } ``` If multiple `Expr`s are provided, each one must evaluate to a list of such dictionaries. **Returns:** * `'fo.Dataset'`: A Voxel51 dataset. **Examples:** Export the images in the `image` column of the table `tbl` as a Voxel51 dataset, using classification labels from `tbl.classifications`: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} export_images_as_fo_dataset( tbl, tbl.image, classifications=tbl.classifications ) ``` See the [Working with Voxel51 in Pixeltable](https://docs.pixeltable.com/howto/working-with-fiftyone) tutorial for a fully worked example. ## func  export\_json() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} export_json( table_or_query: pxt.Table | pxt.Query, file_path: str | Path ) -> None ``` Exports a query result or table to a JSONL file. Pixeltable column types are mapped to JSON values as follows: * String: string * Int: number * Float: number * Bool: boolean * Timestamp: ISO 8601 string * Date: ISO 8601 string * UUID: string * Json: native JSON value (object, array, etc.) * Array: nested JSON array (via `tolist()`) * Binary: excluded from export (not representable in JSON) * Image, Video, Audio, Document: file path or URL string **Parameters:** * **`table_or_query`** (`pxt.Table | pxt.Query`): Table or Query to export. * **`file_path`** (`str | Path`): Path to the output JSONL file. ## func  export\_lancedb() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} export_lancedb( table_or_query: pxt.Table | pxt.Query, db_uri: Path, table_name: str, batch_size_bytes: int = 134217728, if_exists: Literal['error', 'overwrite', 'append'] = 'error' ) -> None ``` Exports a Query's data to a LanceDB table. This utilizes LanceDB's streaming interface for efficient table creation, via a sequence of in-memory pyarrow `RecordBatches`, the size of which can be controlled with the `batch_size_bytes` parameter. **Requirements:** * `pip install lancedb pylance` **Parameters:** * **`table_or_query `** (`Any`): Table or Query to export. * **`db_uri`** (`Path`): Local Path to the LanceDB database. * **`table_name `** (`Any`): Name of the table in the LanceDB database. * **`batch_size_bytes `** (`Any`): Maximum size in bytes for each batch. * **`if_exists`** (`Literal['error', 'overwrite', 'append']`, default: `'error'`): Determines the behavior if the table already exists. Must be one of the following: * `'error'`: raise an error * `'overwrite'`: overwrite the existing table * `'append'`: append to the existing table ## func  export\_parquet() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} export_parquet( table_or_query: pxt.Table | pxt.Query, parquet_path: Path, partition_size_bytes: int = 100000000, inline_images: bool = False, _write_md: bool = False ) -> None ``` Exports a query result or table to one or more Parquet files. Requires pyarrow to be installed. Pixeltable column types are mapped to Parquet types as follows: * String: string * Int: int64 * Float: float32 * Bool: bool * Timestamp: timestamp\[us, tz=UTC] * Date: date32 * UUID: uuid * Binary: binary * Image: binary (when `inline_images=True`) * Audio, Video, Document: string (file paths) * Array (requires shape to be known): * fixed\_shape\_tensor for fixed-shape arrays * list for ragged arrays (one or more dimensions are None) * Json: struct * Schema is inferred from data via `pyarrow.infer_type()` * Fields that contain empty dicts cannot be mapped to a Parquet type and will result in an exception **Parameters:** * **`table_or_query `** (`Any`): Table or Query to export. * **`parquet_path `** (`Any`): Path to directory to write the parquet files to. * **`partition_size_bytes `** (`Any`): The maximum target size for each chunk. Default 100\_000\_000 bytes. * **`inline_images `** (`Any`): If True, images are stored inline in the parquet file. This is useful for small images, to be imported as pytorch dataset. But can be inefficient for large images, and cannot be imported into pixeltable. If False, will raise an error if the Query has any image column. Default False. ## func  export\_sql() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} export_sql( table_or_query: pixeltable.catalog.table.Table | pixeltable._query.Query, target_table_name: str, *, db_connect_str: str, target_schema_name: str | None = None, if_exists: Literal['error', 'replace', 'insert'] = 'error', if_not_exists: Literal['error', 'create'] = 'create' ) -> None ``` Exports a query result or table to an RDBMS table. **Parameters:** * **`table_or_query `** (`Any`): Table or Query to export. * **`target_table_name `** (`Any`): Name of the target table. * **`db_connect_str `** (`Any`): Connection string to the target database. * **`target_schema_name `** (`Any`): Optional name of the target schema. * **`if_exists `** (`Any`): What to do if the target table already exists. * 'error': raise an error * 'replace': drop the existing table and create a new one * 'insert': insert new rows into the existing table * **`if_not_exists `** (`Any`): What to do if the target table does not exist. * 'error': raise an error * 'create': create the table from the source schema ## func  import\_csv() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} import_csv( tbl_name: str, filepath_or_buffer: str | os.PathLike, schema_overrides: dict[str, Any] | None = None, primary_key: str | list[str] | None = None, comment: str | None = None, **kwargs: Any ) -> pxt.Table ``` Creates a new base table from a csv file. This is a convenience method and is equivalent to calling `import_pandas(table_path, pd.read_csv(filepath_or_buffer, **kwargs), schema=schema)`. See the Pandas documentation for [`read_csv`](https://pandas.pydata.org/docs/reference/api/pandas.read_csv.html) for more details. **Returns:** * `pxt.Table`: A handle to the newly created [`Table`](./table). ## func  import\_excel() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} import_excel( tbl_name: str, io: str | os.PathLike, *, schema_overrides: dict[str, Any] | None = None, primary_key: str | list[str] | None = None, comment: str = '', **kwargs: Any ) -> pxt.Table ``` Creates a new base table from an Excel (.xlsx) file. This is a convenience method and is equivalent to calling `import_pandas(table_path, pd.read_excel(io, *args, **kwargs), schema=schema)`. See the Pandas documentation for [`read_excel`](https://pandas.pydata.org/docs/reference/api/pandas.read_excel.html) for more details. **Returns:** * `pxt.Table`: A handle to the newly created [`Table`](./table). ## func  import\_huggingface\_dataset() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} import_huggingface_dataset( table_path: str, dataset: datasets.Dataset | datasets.DatasetDict | datasets.IterableDataset | datasets.IterableDatasetDict, *, schema_overrides: dict[str, Any] | None = None, primary_key: str | list[str] | None = None, **kwargs: Any ) -> pxt.Table ``` Create a new base table from a Huggingface dataset, or dataset dict with multiple splits. Requires `datasets` library to be installed. HuggingFace feature types are mapped to Pixeltable column types as follows: * `Value(bool)`: `Bool`
`Value(int*/uint*)`: `Int`
`Value(float*)`: `Float`
`Value(string/large_string)`: `String`
`Value(timestamp*)`: `Timestamp`
`Value(date*)`: `Date` * `ClassLabel`: `String` (converted to label names) * `Sequence`/`LargeList` of numeric types: `Array` * `Sequence`/`LargeList` of string: `Json` * `Sequence`/`LargeList` of dicts: `Json` * `Array2D`-`Array5D`: `Array` (preserves shape) * `Image`: `Image` * `Audio`: `Audio` * `Video`: `Video` * `Translation`/`TranslationVariableLanguages`: `Json` **Parameters:** * **`table_path`** (`str`): Path to the table. * **`dataset`** (`datasets.Dataset | datasets.DatasetDict | datasets.IterableDataset | datasets.IterableDatasetDict`): An instance of any of the Huggingface dataset classes: [`datasets.Dataset`](https://huggingface.co/docs/datasets/en/package_reference/main_classes#datasets.Dataset), [`datasets.DatasetDict`](https://huggingface.co/docs/datasets/en/package_reference/main_classes#datasets.DatasetDict), [`datasets.IterableDataset`](https://huggingface.co/docs/datasets/en/package_reference/main_classes#datasets.IterableDataset), [`datasets.IterableDatasetDict`](https://huggingface.co/docs/datasets/en/package_reference/main_classes#datasets.IterableDatasetDict) * **`schema_overrides`** (`dict[str, Any] | None`): If specified, then for each (name, type) pair in `schema_overrides`, the column with name `name` will be given type `type`, instead of being inferred from the `Dataset` or `DatasetDict`. The keys in `schema_overrides` should be the column names of the `Dataset` or `DatasetDict` (whether or not they are valid Pixeltable identifiers). * **`primary_key`** (`str | list[str] | None`): The primary key of the table (see [`create_table()`](./pixeltable#func-create_table)). * **`kwargs`** (`Any`): Additional arguments to pass to `create_table`. An argument of `column_name_for_split` must be provided if the source is a DatasetDict. This column name will contain the split information. If None, no split information will be stored. **Returns:** * `pxt.Table`: A handle to the newly created [`Table`](./table). ## func  import\_json() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} import_json( tbl_path: str, filepath_or_url: str, *, schema_overrides: dict[str, Any] | None = None, primary_key: str | list[str] | None = None, comment: str | None = None, **kwargs: Any ) -> pxt.Table ``` Creates a new base table from a JSON file. This is a convenience method and is equivalent to calling [`create_table()`](./pixeltable#func-create_table) with `pxt.create_table(tbl_path, source=filepath_or_url, extra_args=kwargs, ...)`. The contents of `filepath_or_url` are read and parsed as JSON internally (using `json.loads(**kwargs)`). **Parameters:** * **`tbl_path`** (`str`): The name of the table to create. * **`filepath_or_url`** (`str`): The path or URL of the JSON file. * **`schema_overrides`** (`dict[str, Any] | None`): If specified, then columns in `schema_overrides` will be given the specified types (see [`import_rows()`](./io#func-import_rows)). * **`primary_key`** (`str | list[str] | None`): The primary key of the table (see [`create_table()`](./pixeltable#func-create_table)). * **`comment`** (`str | None`): A comment to attach to the table (see [`create_table()`](./pixeltable#func-create_table)). * **`kwargs`** (`Any`): Additional keyword arguments to pass to `json.loads`. **Returns:** * `pxt.Table`: A handle to the newly created [`Table`](./table). ## func  import\_pandas() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} import_pandas( tbl_name: str, df: pd.DataFrame, *, schema_overrides: dict[str, Any] | None = None, primary_key: str | list[str] | None = None, comment: str = '' ) -> pxt.Table ``` Creates a new base table from a Pandas [`DataFrame`](https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.html), with the specified name. The schema of the table will be inferred from the DataFrame. The column names of the new table will be identical to those in the DataFrame, as long as they are valid Pixeltable identifiers. If a column name is not a valid Pixeltable identifier, it will be normalized according to the following procedure: * first replace any non-alphanumeric characters with underscores; * then, preface the result with the letter 'c' if it begins with a number or an underscore; * then, if there are any duplicate column names, suffix the duplicates with '\_2', '\_3', etc., in column order. **Parameters:** * **`tbl_name`** (`str`): The name of the table to create. * **`df`** (`pd.DataFrame`): The Pandas `DataFrame`. * **`schema_overrides`** (`dict[str, Any] | None`): If specified, then for each (name, type) pair in `schema_overrides`, the column with name `name` will be given type `type`, instead of being inferred from the `DataFrame`. The keys in `schema_overrides` should be the column names of the `DataFrame` (whether or not they are valid Pixeltable identifiers). **Returns:** * `pxt.Table`: A handle to the newly created [`Table`](./table). ## func  import\_parquet() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} import_parquet( table: str, *, parquet_path: str, schema_overrides: dict[str, Any] | None = None, primary_key: str | list[str] | None = None, **kwargs: Any ) -> pxt.Table ``` Creates a new base table from a Parquet file or set of files. Requires pyarrow to be installed. **Parameters:** * **`table`** (`str`): Fully qualified name of the table to import the data into. * **`parquet_path`** (`str`): Path to an individual Parquet file or directory of Parquet files. * **`schema_overrides`** (`dict[str, Any] | None`): If specified, then for each (name, type) pair in `schema_overrides`, the column with name `name` will be given type `type`, instead of being inferred from the Parquet dataset. The keys in `schema_overrides` should be the column names of the Parquet dataset (whether or not they are valid Pixeltable identifiers). * **`primary_key`** (`str | list[str] | None`): The primary key of the table (see [`create_table()`](./pixeltable#func-create_table)). * **`kwargs`** (`Any`): Additional arguments to pass to `create_table`. **Returns:** * `pxt.Table`: A handle to the newly created table. ## func  import\_rows() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} import_rows( tbl_path: str, rows: list[dict[str, Any]], *, schema_overrides: dict[str, Any] | None = None, primary_key: str | list[str] | None = None, comment: str = '' ) -> pxt.Table ``` Creates a new base table from a list of dictionaries. The dictionaries must be of the form `{column_name: value, ...}`. Pixeltable will attempt to infer the schema of the table from the supplied data, using the most specific type that can represent all the values in a column. If `schema_overrides` is specified, then for each entry `(column_name, type)` in `schema_overrides`, Pixeltable will force the specified column to the specified type (and will not attempt any type inference for that column). All column types of the new table will be nullable unless explicitly specified as non-nullable in `schema_overrides`. **Parameters:** * **`tbl_path`** (`str`): The qualified name of the table to create. * **`rows`** (`list[dict[str, Any]]`): The list of dictionaries to import. * **`schema_overrides`** (`dict[str, Any] | None`): If specified, then columns in `schema_overrides` will be given the specified types as described above. * **`primary_key`** (`str | list[str] | None`): The primary key of the table (see [`create_table()`](./pixeltable#func-create_table)). * **`comment`** (`str`, default: `''`): A comment to attach to the table (see [`create_table()`](./pixeltable#func-create_table)). **Returns:** * `pxt.Table`: A handle to the newly created [`Table`](./table). ## func  import\_sql() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} import_sql( selectable: sqlalchemy.sql.selectable.Selectable, conn: sqlalchemy.engine.base.Engine | sqlalchemy.engine.base.Connection, tbl_name: str, *, schema_overrides: dict[str, typing.Any] | None = None, primary_key: str | list[str] | None = None, comment: str | None = None, custom_metadata: Any = None, if_exists: Literal['error', 'append'] = 'error', on_error: Literal['abort', 'ignore'] = 'abort', send_connect_url: bool = False ) -> pixeltable.catalog.table.Table ``` Import a SQL source into a Pixeltable table. Rows are streamed from the source via a server-side cursor and inserted in batches. **Parameters:** * **`selectable`** (`sqlalchemy.sql.selectable.Selectable`): A SQLAlchemy `Selectable` (a `Table`, a `select()` statement, or `text(...).columns(...)`) describing the source rows. * **`conn`** (`sqlalchemy.engine.base.Engine | sqlalchemy.engine.base.Connection`): A SQLAlchemy `Engine` or `Connection` to execute `selectable` against. If a `Connection` is passed, it must remain open and untouched (no commits, rollbacks, or other statements) for the duration of the import; rows are streamed directly from the server-side cursor. * **`tbl_name`** (`str`): Pixeltable path of the destination table. * **`schema_overrides`** (`dict[str, typing.Any] | None`): Optional per-column overrides applied on top of the inferred schema. Keys are column names; values accept any Pixeltable type spec recognized by `pxt.create_table` (eg, `pxt.Image`, `pxt.String | None`). * **`primary_key`** (`str | list[str] | None`): An optional column name or list of column names to use as the primary key(s) of the table. Only applies when a new table is created; ignored when appending to an existing table. * **`comment`** (`str | None`): An optional comment; its meaning is user-defined. Only applies when a new table is created; ignored when appending to an existing table. * **`custom_metadata`** (`typing.Any`): Optional user-defined metadata to associate with the table. Must be a valid JSON-serializable object \[str, int, float, bool, dict, list]. Only applies when a new table is created; ignored when appending to an existing table. * **`if_exists`** (`typing.Literal['error', 'append']`, default: `'error'`): How to handle the destination table. * `'error'`: create the table; fail if it already exists. * `'append'`: append into the table if it already exists (verifying the source schema is compatible); otherwise create it. * **`on_error`** (`typing.Literal['abort', 'ignore']`, default: `'abort'`): Determines the behavior if an error occurs while evaluating a computed column or detecting an invalid media file (such as a corrupt image) for one of the inserted rows. * If `on_error='abort'`, then an exception will be raised and the rows will not be inserted. * If `on_error='ignore'`, then execution will continue and the rows will be inserted. Any cells with errors will have a `None` value for that cell, with information about the error stored in the corresponding `tbl.col_name.errortype` and `tbl.col_name.errormsg` fields. * **`send_connect_url`** (`bool`, default: `False`): Determines where the query is run, and is only relevant when the destination table is hosted. It has no effect on a local table. * If `False`, the query is run locally and the result is sent to the server. * If `True`, the server connects to the source database itself (using the source connection URL, including credentials); this requires the server's host to be able to reach the database and is faster for large imports. **Returns:** * `pixeltable.catalog.table.Table`: The destination `Table`. # jina Source: https://docs.pixeltable.com/sdk/latest/jina View Source on GitHub # module  pixeltable.functions.jina Pixeltable [UDFs](https://docs.pixeltable.com/platform/udfs-in-pixeltable) that wrap [Jina AI](https://jina.ai/) APIs for embeddings and reranking. In order to use them, the API key must be specified either with `JINA_API_KEY` environment variable, or as `api_key` in the `jina` section of the Pixeltable config file. ## udf  embeddings() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf embeddings( input: pxt.String, *, model: pxt.String, task: pxt.String | None = None, dimensions: pxt.Int | None = None, late_chunking: pxt.Bool | None = None ) -> pxt.Array[(None,), float32] ``` Creates embedding vectors for the input text using Jina AI embedding models. Equivalent to the Jina AI embeddings API endpoint. For additional details, see: [https://jina.ai/embeddings/](https://jina.ai/embeddings/) Request throttling: Applies the rate limit set in the config (section `jina`, key `rate_limit`). If no rate limit is configured, uses a default of 600 RPM. **Parameters:** * **`input`** (`pxt.String`): The text to embed. * **`model`** (`pxt.String`): The Jina embedding model to use. See available models at [https://jina.ai/embeddings/](https://jina.ai/embeddings/). * **`task`** (`pxt.String | None`): Task-specific embedding optimization. Options: * `retrieval.query`: For search queries * `retrieval.passage`: For documents/passages to be searched * `separation`: For clustering/separation tasks * `classification`: For classification tasks * `text-matching`: For semantic similarity * **`dimensions`** (`pxt.Int | None`): Output embedding dimensions (optional). If not specified, uses the model's default dimension. * **`late_chunking`** (`pxt.Bool | None`): Enable late chunking for long documents. **Returns:** * `pxt.Array[(None,), float32]`: An array representing the embedding of `input`. **Examples:** Add a computed column that applies jina-embeddings-v3 to an existing column: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} tbl.add_computed_column( embed=jina.embeddings( tbl.text, model='jina-embeddings-v3', task='retrieval.passage' ) ) ``` Add an embedding index: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} tbl.add_embedding_index( 'text', string_embed=jina.embeddings.using(model='jina-embeddings-v3') ) ``` ## udf  rerank() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf rerank( query: pxt.String, documents: pxt.Json[(String, ...)], *, model: pxt.String, top_n: pxt.Int | None = None, return_documents: pxt.Bool | None = None ) -> JinaRerankResponse ``` Reranks documents based on their relevance to a query using Jina AI reranker models. Equivalent to the Jina AI rerank API endpoint. For additional details, see: [https://jina.ai/reranker/](https://jina.ai/reranker/) Request throttling: Applies the rate limit set in the config (section `jina`, key `rate_limit`). If no rate limit is configured, uses a default of 600 RPM. **Parameters:** * **`query`** (`pxt.String`): The query string to rank documents against. * **`documents`** (`pxt.Json[(String`): The list of documents to rerank. * **`model`** (`Any`): The Jina reranker model to use. See available models at [https://jina.ai/reranker/](https://jina.ai/reranker/). * **`top_n`** (`Any`): Number of top results to return. If not specified, returns all documents. * **`return_documents`** (`Any`): Whether to include the original document text in results. **Returns:** * `JinaRerankResponse`: A dictionary containing: * `results`: List of reranking results with `index` and `relevance_score` (and `document` if `return_documents=True`) * `usage`: Token usage information **Examples:** Rerank search results for better relevance: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} tbl.add_computed_column( reranked=jina.rerank( tbl.query, tbl.candidate_docs, model='jina-reranker-v2-base-multilingual', top_n=5, ) ) ``` # json Source: https://docs.pixeltable.com/sdk/latest/json View Source on GitHub # module  pixeltable.functions.json Pixeltable UDFs for `JsonType`. Example: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} import pixeltable as pxt import pixeltable.functions as pxtf t = pxt.get_table(...) t.select(pxtf.json.make_list(t.json_col)).collect() ``` ## iterator  list\_iterator() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.iterator list_iterator( elements: pxt.Json[(Json, ...)] | None = None, *, mode: pxt.String = 'strict', **kwargs ) ``` Iterator over elements of a list or lists. There are two distinct call patterns: either a single positional argument; or one or more keyword arguments. * If a single positional argument is specified, as in `list_iterator(t.col)`, then the elements of `t.col` must contain lists of dictionaries with matching signatures (identical keys and compatible value types). The iterator will yield one new column for each key in the dictionaries, and one output row per element in the lists. * If multiple keyword arguments are specified, as in `list_iterator(val_1=t.col_1, val_2=t.col_2)`, then the elements of each input column must contain lists, but not necessarily lists of dictionaries. The iterator will yield one new column for each keyword argument, zipping together the individual lists. All of the inputs must be *typed* `Json` expressions. Untyped Json will be rejected (the type schema is necessary in order for Pixeltable to determine the types of the output columns). **Parameters:** * **`elements`** (`pxt.Json[(Json`): A list of dictionaries to iterate over. The dictionary keys will be used as column names in the output. Cannot be specified together with keyword arguments. * **`mode`** (`Any`): Only applies when called with keyword arguments. Determines how to handle lists of different lengths: * `'strict'`: Raises an error if the input lists have different lengths. * `'truncated'`: Iterates until the shortest input list is exhausted, ignoring any remaining elements in longer lists. * `'padded'`: Iterates until the longest input list is exhausted, yielding `None` for any missing elements from shorter lists. * **`**kwargs`** (`Any`): One or more lists to iterate over. The kwarg names will be used as column names in the output. Cannot be specified together with `elements`. ## uda  make\_list() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.uda make_list(*args, **kwargs) -> pxt.Json[(Json, ...)] ``` Collects arguments into a list. ## udf  concat() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf concat(self: pxt.Json | None, other: pxt.Json | None) -> pxt.Json ``` Concatenate two JSON arrays into a new array. Returns `null` if either operand is `null`. Raises if either operand is a non-null, non-array value. **Examples:** ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} t.select(t.tags.concat(t.more_tags)).collect() ``` ## udf  contains() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf contains(self: pxt.Json, value: pxt.Json) -> pxt.Bool ``` Return `True` if `value` is an element of a JSON array or a key of a JSON object; `False` otherwise. **Examples:** ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} t.where(t.detections.labels.contains('person')).collect() ``` ## udf  count() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf count(self: pxt.Json, value: pxt.Json) -> pxt.Int ``` Return the number of times `value` occurs in a JSON array. **Examples:** ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} t.select(t.detections.labels.count('person')).collect() ``` ## udf  dumps() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf dumps(obj: pxt.Json) -> pxt.String ``` Serialize a JSON object to a string. Equivalent to [`json.dumps()`](https://docs.python.org/3/library/json.html#json.dumps). **Parameters:** * **`obj`** (`pxt.Json`): A JSON-serializable object (dict, list, or scalar). **Returns:** * `pxt.String`: A JSON-formatted string. ## udf  flatten() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf flatten(self: pxt.Json) -> pxt.Json ``` Concatenate the elements of a JSON array one level deep; non-array elements are kept as-is. **Examples:** ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} t.select(t.chunks.flatten()).collect() ``` ## udf  get() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf get( self: pxt.Json | None, key: pxt.String, default: pxt.Json | None = None ) -> pxt.Json ``` Return the value of `key` if the value is a JSON object containing it, otherwise `default`. **Examples:** ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} t.select(t.metadata.get('author', default='unknown')).collect() ``` ## udf  is\_empty() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf is_empty(self: pxt.Json | None) -> pxt.Bool ``` Return `True` if the value is `null`, an empty array, an empty object, or an empty string; `False` otherwise (including for numbers and booleans). **Examples:** ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} t.where(~t.detections.bboxes.is_empty()).collect() ``` ## udf  items() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf items(self: pxt.Json) -> pxt.Json ``` Return the `[key, value]` pairs of a JSON object as a list. `keys()`, `values()`, and `items()` share the same ordering. **Examples:** ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} t.select(t.metadata.items()).collect() ``` ## udf  keys() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf keys(self: pxt.Json) -> pxt.Json[(String, ...)] ``` Return the keys of a JSON object. `keys()`, `values()`, and `items()` share the same ordering. **Examples:** ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} t.select(t.metadata.keys()).collect() ``` ## udf  len() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf len(self: pxt.Json) -> pxt.Int ``` Return the number of elements in a JSON array, keys in a JSON object, or characters in a JSON string. Not defined for numbers or booleans. A `null` value (or missing path) yields `null`. **Examples:** ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} t.select(t.detections.bboxes.len()).collect() ``` ## udf  max() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf max(self: pxt.Json) -> pxt.Float | None ``` Return the largest number in a JSON array, or `null` if the array is empty. **Examples:** ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} t.select(t.detections.scores.max()).collect() ``` ## udf  mean() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf mean(self: pxt.Json) -> pxt.Float | None ``` Return the arithmetic mean of the numbers in a JSON array, or `null` if the array is empty. **Examples:** ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} t.select(t.detections.scores.mean()).collect() ``` ## udf  merge() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf merge(self: pxt.Json | None, other: pxt.Json | None) -> pxt.Json ``` Merge two JSON objects into a new object; on a key conflict the value from `other` wins. Returns `null` if either operand is `null`. Raises if either operand is a non-null, non-object value. **Examples:** ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} t.select(t.defaults.merge(t.overrides)).collect() ``` ## udf  min() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf min(self: pxt.Json) -> pxt.Float | None ``` Return the smallest number in a JSON array, or `null` if the array is empty. **Examples:** ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} t.select(t.detections.scores.min()).collect() ``` ## udf  sum() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf sum(self: pxt.Json) -> pxt.Float ``` Return the sum of the numbers in a JSON array (0 for an empty array). **Examples:** ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} t.select(t.detections.scores.sum()).collect() ``` ## udf  values() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf values(self: pxt.Json) -> pxt.Json ``` Return the values of a JSON object. `keys()`, `values()`, and `items()` share the same ordering. **Examples:** ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} t.select(t.metadata.values()).collect() ``` # llama_cpp Source: https://docs.pixeltable.com/sdk/latest/llama_cpp View Source on GitHub # module  pixeltable.functions.llama\_cpp Pixeltable UDFs for llama.cpp models. Provides integration with llama.cpp for running quantized language models locally, supporting chat completions and embeddings with GGUF format models. ## udf  create\_chat\_completion() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf create_chat_completion( messages: pxt.Json[(Json, ...)], *, model_path: pxt.String | None = None, repo_id: pxt.String | None = None, repo_filename: pxt.String | None = None, chat_format: pxt.String | None = None, tools: pxt.Json[(Json, ...)] | None = None, tool_choice: pxt.Json | None = None, model_kwargs: pxt.Json | None = None ) -> pxt.Json ``` Generate a chat completion from a list of messages. The model can be specified either as a local path, or as a repo\_id and repo\_filename that reference a pretrained model on the Hugging Face model hub. Exactly one of `model_path` or `repo_id` must be provided; if `model_path` is provided, then an optional `repo_filename` can also be specified. For additional details, see the [llama\_cpp create\_chat\_completion documentation](https://llama-cpp-python.readthedocs.io/en/latest/api-reference/#llama_cpp.Llama.create_chat_completion). **Parameters:** * **`messages`** (`pxt.Json[(Json`): A list of messages to generate a response for. * **`model_path`** (`Any`): Path to the model (if using a local model). * **`repo_id`** (`Any`): The Hugging Face model repo id (if using a pretrained model). * **`repo_filename`** (`Any`): A filename or glob pattern to match the model file in the repo (optional, if using a pretrained model). * **`chat_format`** (`Any`): An optional string specifying the chat format to use with the model. * **`tools`** (`Any`): An optional list of tools (functions) the model may call, specified as `pxt.func.tools.Tools`. * **`tool_choice`** (`Any`): An optional `pxt.func.tools.ToolChoice` controlling which tool(s) the model should use. * **`model_kwargs`** (`Any`): Additional keyword args for the llama\_cpp `create_chat_completion` API, such as `max_tokens`, `temperature`, `top_p`, and `top_k`. For details, see the [llama\_cpp create\_chat\_completion documentation](https://llama-cpp-python.readthedocs.io/en/latest/api-reference/#llama_cpp.Llama.create_chat_completion). # math Source: https://docs.pixeltable.com/sdk/latest/math View Source on GitHub # module  pixeltable.functions.math Pixeltable UDFs for mathematical operations. Example: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} import pixeltable as pxt t = pxt.get_table(...) t.select(t.float_col.floor()).collect() ``` ## udf  abs() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf abs(self: pxt.Float) -> pxt.Float ``` Return the absolute value of the given number. Equivalent to Python [`builtins.abs()`](https://docs.python.org/3/library/functions.html#abs). ## udf  bitwise\_and() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf bitwise_and(self: pxt.Int, other: pxt.Int) -> pxt.Int ``` Bitwise AND of two integers. Equivalent to Python [`self & other`](https://docs.python.org/3/library/stdtypes.html#bitwise-operations-on-integer-types). ## udf  bitwise\_or() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf bitwise_or(self: pxt.Int, other: pxt.Int) -> pxt.Int ``` Bitwise OR of two integers. Equivalent to Python [`self | other`](https://docs.python.org/3/library/stdtypes.html#bitwise-operations-on-integer-types). ## udf  bitwise\_xor() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf bitwise_xor(self: pxt.Int, other: pxt.Int) -> pxt.Int ``` Bitwise XOR of two integers. Equivalent to Python [`self ^ other`](https://docs.python.org/3/library/stdtypes.html#bitwise-operations-on-integer-types). ## udf  ceil() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf ceil(self: pxt.Float) -> pxt.Float ``` Return the ceiling of the given number. Equivalent to Python [`float(math.ceil(self))`](https://docs.python.org/3/library/math.html#math.ceil) if `self` is finite, or `self` itself if `self` is infinite. (This is slightly different from the default behavior of `math.ceil(self)`, which always returns an `int` and raises an error if `self` is infinite. The behavior in Pixeltable generalizes the Python operator and is chosen to align with the SQL standard.) ## udf  floor() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf floor(self: pxt.Float) -> pxt.Float ``` Return the ceiling of the given number. Equivalent to Python [`float(math.floor(self))`](https://docs.python.org/3/library/math.html#math.ceil) if `self` is finite, or `self` itself if `self` is infinite. (This is slightly different from the default behavior of `math.floor(self)`, which always returns an `int` and raises an error if `self` is infinite. The behavior of Pixeltable generalizes the Python operator and is chosen to align with the SQL standard.) ## udf  pow() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf pow(self: pxt.Float, other: pxt.Float) -> pxt.Float ``` Raise `self` to the power of `other`. Equivalent to Python [`builtins.pow()`](https://docs.python.org/3/library/functions.html#pow). ## udf  round() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf round( self: pxt.Float, digits: pxt.Int | None = None ) -> pxt.Float ``` Round a number to a given precision in decimal digits. Equivalent to Python [`builtins.round(self, digits or 0)`](https://docs.python.org/3/library/functions.html#round). Note that if `digits` is not specified, the behavior matches `builtins.round(self, 0)` rather than `builtins.round(self)`; this ensures that the return type is always `float` (as in SQL) rather than `int`. # mistralai Source: https://docs.pixeltable.com/sdk/latest/mistralai View Source on GitHub # module  pixeltable.functions.mistralai Pixeltable UDFs that wrap various endpoints from the Mistral AI API. In order to use them, you must first `pip install mistralai` and configure your Mistral AI credentials, as described in the [Working with Mistral AI](https://docs.pixeltable.com/howto/providers/working-with-mistralai) tutorial. ## udf  chat\_completions() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf chat_completions( messages: pxt.Json[(Json, ...)], *, model: pxt.String, model_kwargs: pxt.Json | None = None ) -> pxt.Json ``` Chat Completion API. Equivalent to the Mistral AI `chat/completions` API endpoint. For additional details, see: [https://docs.mistral.ai/api/#tag/chat](https://docs.mistral.ai/api/#tag/chat) Request throttling: Applies the rate limit set in the config (section `mistral`, key `rate_limit`). If no rate limit is configured, uses a default of 600 RPM. **Requirements:** * `pip install mistralai` **Parameters:** * **`messages`** (`pxt.Json[(Json`): The prompt(s) to generate completions for. * **`model`** (`Any`): ID of the model to use. (See overview here: [https://docs.mistral.ai/getting-started/models/](https://docs.mistral.ai/getting-started/models/)) * **`model_kwargs`** (`Any`): Additional keyword args for the Mistral `chat/completions` API. For details on the available parameters, see: [https://docs.mistral.ai/api/#tag/chat](https://docs.mistral.ai/api/#tag/chat) **Returns:** * `pxt.Json`: A dictionary containing the response and other metadata. **Examples:** Add a computed column that applies the model `mistral-latest-small` to an existing Pixeltable column `tbl.prompt` of the table `tbl`: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} messages = [{'role': 'user', 'content': tbl.prompt}] tbl.add_computed_column( response=completions(messages, model='mistral-latest-small') ) ``` ## udf  embeddings() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf embeddings( input: pxt.String, *, model: pxt.String ) -> pxt.Array[(None,), float32] ``` Embeddings API. Equivalent to the Mistral AI `embeddings` API endpoint. For additional details, see: [https://docs.mistral.ai/api/#tag/embeddings](https://docs.mistral.ai/api/#tag/embeddings) Request throttling: Applies the rate limit set in the config (section `mistral`, key `rate_limit`). If no rate limit is configured, uses a default of 600 RPM. **Requirements:** * `pip install mistralai` **Parameters:** * **`input`** (`pxt.String`): Text to embed. * **`model`** (`pxt.String`): ID of the model to use. (See overview here: [https://docs.mistral.ai/getting-started/models/](https://docs.mistral.ai/getting-started/models/)) **Returns:** * `pxt.Array[(None,), float32]`: An array representing the application of the given embedding to `input`. ## udf  fim\_completions() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf fim_completions( prompt: pxt.String, *, model: pxt.String, model_kwargs: pxt.Json | None = None ) -> pxt.Json ``` Fill-in-the-middle Completion API. Equivalent to the Mistral AI `fim/completions` API endpoint. For additional details, see: [https://docs.mistral.ai/api/#tag/fim](https://docs.mistral.ai/api/#tag/fim) Request throttling: Applies the rate limit set in the config (section `mistral`, key `rate_limit`). If no rate limit is configured, uses a default of 600 RPM. **Requirements:** * `pip install mistralai` **Parameters:** * **`prompt`** (`pxt.String`): The text/code to complete. * **`model`** (`pxt.String`): ID of the model to use. (See overview here: [https://docs.mistral.ai/getting-started/models/](https://docs.mistral.ai/getting-started/models/)) * **`model_kwargs`** (`pxt.Json | None`): Additional keyword args for the Mistral `fim/completions` API. For details on the available parameters, see: [https://docs.mistral.ai/api/#tag/fim](https://docs.mistral.ai/api/#tag/fim) **Returns:** * `pxt.Json`: A dictionary containing the response and other metadata. **Examples:** Add a computed column that applies the model `codestral-latest` to an existing Pixeltable column `tbl.prompt` of the table `tbl`: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} tbl.add_computed_column( response=completions(tbl.prompt, model='codestral-latest') ) ``` # nebius Source: https://docs.pixeltable.com/sdk/latest/nebius View Source on GitHub # module  pixeltable.functions.nebius Pixeltable UDFs for Nebius Token Factory models. Provides integration with Nebius Token Factory's language and embedding models. In order to use them, you must first `pip install openai` and configure your Nebius Token Factory API key, by setting the `NEBIUS_API_KEY` environment variable or `nebius.api_key` in the Pixeltable config file. ## udf  chat\_completions() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf chat_completions( messages: pxt.Json[(Json, ...)], *, model: pxt.String, model_kwargs: pxt.Json | None = None, tools: pxt.Json[(Json, ...)] | None = None, tool_choice: pxt.Json | None = None ) -> pxt.Json ``` Creates a model response for the given chat conversation. Equivalent to the Nebius Token Factory `chat/completions` API endpoint. For additional details, see: [https://docs.tokenfactory.nebius.com/api-reference/inference/create-chat-completion](https://docs.tokenfactory.nebius.com/api-reference/inference/create-chat-completion) Nebius Token Factory exposes an OpenAI-compatible API, so you will need to install the `openai` package to use this UDF. Request throttling: Applies the rate limit set in the config (section `nebius`, key `rate_limit`). If no rate limit is configured, a default rate limit is applied. **Requirements:** * `pip install openai` **Parameters:** * **`messages`** (`pxt.Json[(Json`): A list of messages to use for chat completion, as described in the Nebius API documentation. * **`model`** (`Any`): The model to use for chat completion. * **`model_kwargs`** (`Any`): Additional keyword args for the Nebius `chat/completions` API. For details on the available parameters, see: [https://docs.tokenfactory.nebius.com/api-reference/inference/create-chat-completion](https://docs.tokenfactory.nebius.com/api-reference/inference/create-chat-completion) * **`tools`** (`Any`): An optional list of Pixeltable tools to use for the request. * **`tool_choice`** (`Any`): An optional tool choice configuration. **Returns:** * `pxt.Json`: A dictionary containing the response and other metadata. **Examples:** Add a computed column that applies the model `Qwen/Qwen3-30B-A3B-Instruct-2507` to an existing Pixeltable column `tbl.prompt` of the table `tbl`: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} messages = [ {'role': 'system', 'content': 'You are a helpful assistant.'}, {'role': 'user', 'content': tbl.prompt}, ] tbl.add_computed_column( response=chat_completions( messages, model='Qwen/Qwen3-30B-A3B-Instruct-2507' ) ) ``` You can also include images in the messages list, for vision-capable models such as `openbmb/MiniCPM-V-4_5`, by passing image data directly in the input dictionary, in the `'image_url'` field of the message content, as in this example: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} messages = [ { 'role': 'user', 'content': [ {'type': 'text', 'text': "What's in this image?"}, {'type': 'image_url', 'image_url': tbl.image}, ], } ] tbl.add_computed_column( response=chat_completions(messages, model='openbmb/MiniCPM-V-4_5') ) ``` ## udf  embeddings() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf embeddings( input: pxt.String, *, model: pxt.String, model_kwargs: pxt.Json | None = None ) -> pxt.Array[(None,), float32] ``` Query an embedding model for a given string of text. Equivalent to the Nebius Token Factory `embeddings` API endpoint. For additional details, see: [https://docs.tokenfactory.nebius.com/api-reference/inference/create-embeddings](https://docs.tokenfactory.nebius.com/api-reference/inference/create-embeddings) Request throttling: Applies the rate limit set in the config (section `nebius`, key `rate_limit`). If no rate limit is configured, a default rate limit is applied. **Requirements:** * `pip install openai` **Parameters:** * **`input`** (`pxt.String`): A string providing the text for the model to embed. * **`model`** (`pxt.String`): The name of the embedding model to use. * **`model_kwargs`** (`pxt.Json | None`): Additional keyword args for the Nebius `embeddings` API, e.g. `dimensions` to request a truncated embedding for models that support it (see the note below). **Returns:** * `pxt.Array[(None,), float32]`: An array representing the application of the given embedding to `input`. **Examples:** Add a computed column that applies the model `Qwen/Qwen3-Embedding-8B` to an existing Pixeltable column `tbl.text` of the table `tbl`: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} tbl.add_computed_column( response=embeddings(tbl.text, model='Qwen/Qwen3-Embedding-8B') ) ``` `Qwen/Qwen3-Embedding-8B` produces 4096-dimensional embeddings by default, which exceed the maximum of 4000 dimensions supported by Pixeltable embedding indexes. Request a smaller, indexable size via `model_kwargs`: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} tbl.add_embedding_index( 'text', embedding=embeddings.using( model='Qwen/Qwen3-Embedding-8B', model_kwargs={'dimensions': 1024} ), ) ``` # net Source: https://docs.pixeltable.com/sdk/latest/net View Source on GitHub # module  pixeltable.functions.net Pixeltable UDF for converting media file URIs to presigned HTTP URLs. ## udf  presigned\_url() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf presigned_url(uri: pxt.String, expiration_seconds: pxt.Int) -> pxt.String ``` Convert a blob storage URI to a presigned HTTP URL for direct access. Generates a time-limited, publicly accessible URL from cloud storage URIs (S3, GCS, Azure, etc.) that can be used to serve media files over HTTP. Note: This function uses presigned URLs from storage providers. Provider-specific limitations apply: * Google Cloud Storage: maximum 7-day expiration * AWS S3: requires proper region configuration * Azure: subject to storage account access policies **Parameters:** * **`uri`** (`pxt.String`): The media file URI (e.g., `s3://bucket/path`, `gs://bucket/path`, `azure://container/path`) * **`expiration_seconds`** (`pxt.Int`): How long the URL remains valid **Returns:** * `pxt.String`: A presigned HTTP URL for accessing the file **Examples:** Generate a presigned URL for a video column with 1-hour expiration: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} tbl.select( original_url=tbl.video.fileurl, presigned_url=pxtf.net.presigned_url(tbl.video.fileurl, 3600), ).collect() ``` # ollama Source: https://docs.pixeltable.com/sdk/latest/ollama View Source on GitHub # module  pixeltable.functions.ollama Pixeltable UDFs for Ollama local models. Provides integration with Ollama for running large language models locally, including chat completions and embeddings. ## udf  chat() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf chat( messages: pxt.Json[(Json, ...)], *, model: pxt.String, tools: pxt.Json[(Json, ...)] | None = None, format: pxt.String | None = None, options: pxt.Json | None = None ) -> pxt.Json ``` Generate the next message in a chat with a provided model. **Parameters:** * **`messages`** (`pxt.Json[(Json`): The messages of the chat. * **`model`** (`Any`): The model name. * **`tools`** (`Any`): Tools for the model to use. * **`format`** (`Any`): The format of the response; must be one of `'json'` or `None`. * **`options`** (`Any`): Additional options to pass to the `chat` call, such as `max_tokens`, `temperature`, `top_p`, and `top_k`. For details, see the [Valid Parameters and Values](https://github.com/ollama/ollama/blob/main/docs/modelfile.mdx#valid-parameters-and-values) section of the Ollama documentation. ## udf  embed() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf embed( input: pxt.String, *, model: pxt.String, truncate: pxt.Bool = True, options: pxt.Json | None = None ) -> pxt.Array[(None,), float32] ``` Generate embeddings from a model. **Parameters:** * **`input`** (`pxt.String`): The input text to generate embeddings for. * **`model`** (`pxt.String`): The model name. * **`truncate`** (`pxt.Bool`): Truncates the end of each input to fit within context length. Returns error if false and context length is exceeded. * **`options`** (`pxt.Json | None`): Additional options to pass to the `embed` call. For details, see the [Valid Parameters and Values](https://github.com/ollama/ollama/blob/main/docs/modelfile.mdx#valid-parameters-and-values) section of the Ollama documentation. ## udf  generate() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf generate( prompt: pxt.String, *, model: pxt.String, suffix: pxt.String = '', system: pxt.String = '', template: pxt.String = '', context: pxt.Json[(Int, ...)] | None = None, raw: pxt.Bool = False, format: pxt.String | None = None, options: pxt.Json | None = None ) -> pxt.Json ``` Generate a response for a given prompt with a provided model. **Parameters:** * **`prompt`** (`pxt.String`): The prompt to generate a response for. * **`model`** (`pxt.String`): The model name. * **`suffix`** (`pxt.String`): The text after the model response. * **`format`** (`Any`): The format of the response; must be one of `'json'` or `None`. * **`system`** (`pxt.String`): System message. * **`template`** (`pxt.String`): Prompt template to use. * **`context`** (`pxt.Json[(Int`): The context parameter returned from a previous call to `generate()`. * **`raw`** (`Any`): If `True`, no formatting will be applied to the prompt. * **`options`** (`Any`): Additional options for the Ollama `chat` call, such as `max_tokens`, `temperature`, `top_p`, and `top_k`. For details, see the [Valid Parameters and Values](https://github.com/ollama/ollama/blob/main/docs/modelfile.mdx#valid-parameters-and-values) section of the Ollama documentation. # openai Source: https://docs.pixeltable.com/sdk/latest/openai View Source on GitHub # module  pixeltable.functions.openai Pixeltable UDFs that wrap various endpoints from the OpenAI API. In order to use them, you must first `pip install openai` and configure your OpenAI credentials, as described in the [Working with OpenAI](https://docs.pixeltable.com/howto/providers/working-with-openai) tutorial. ## func  invoke\_tools() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} invoke_tools( tools: pixeltable.func.tools.Tools, response: pixeltable.exprs.expr.Expr ) -> pixeltable.exprs.inline_expr.InlineDict ``` Converts an OpenAI response dict to Pixeltable tool invocation format and calls `tools._invoke()`. ## udf  chat\_completions() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf chat_completions( messages: pxt.Json[(Json, ...)], *, model: pxt.String, model_kwargs: pxt.Json | None = None, tools: pxt.Json[(Json, ...)] | None = None, tool_choice: pxt.Json | None = None ) -> pxt.Json ``` Creates a model response for the given chat conversation. Equivalent to the OpenAI `chat/completions` API endpoint. For additional details, see: [https://platform.openai.com/docs/guides/chat-completions](https://platform.openai.com/docs/guides/chat-completions) Request throttling: Uses the rate limit-related headers returned by the API to throttle requests adaptively, based on available request and token capacity. No configuration is necessary. **Requirements:** * `pip install openai` **Parameters:** * **`messages`** (`pxt.Json[(Json`): A list of messages to use for chat completion, as described in the OpenAI API documentation. * **`model`** (`Any`): The model to use for chat completion. * **`model_kwargs`** (`Any`): Additional keyword args for the OpenAI `chat/completions` API. For details on the available parameters, see: [https://platform.openai.com/docs/api-reference/chat/create](https://platform.openai.com/docs/api-reference/chat/create) **Returns:** * `pxt.Json`: A dictionary containing the response and other metadata. **Examples:** Add a computed column that applies the model `gpt-4o-mini` to an existing Pixeltable column `tbl.prompt` of the table `tbl`: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} messages = [ {'role': 'system', 'content': 'You are a helpful assistant.'}, {'role': 'user', 'content': tbl.prompt}, ] tbl.add_computed_column( response=chat_completions(messages, model='gpt-4o-mini') ) ``` You can also include images in the messages list by passing image data directly in the input dictionary, in the `'image_url'` field of the message content, as in this example: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} messages = [ { 'role': 'user', 'content': [ {'type': 'text', 'text': "What's in this image?"}, {'type': 'image_url', 'image_url': tbl.image}, ], } ] tbl.add_computed_column( response=chat_completions(messages, model='gpt-4o-mini') ) ``` ## udf  embeddings() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf embeddings( input: pxt.String, *, model: pxt.String, model_kwargs: pxt.Json | None = None ) -> pxt.Array[(None,), float32] ``` Creates an embedding vector representing the input text. Equivalent to the OpenAI `embeddings` API endpoint. For additional details, see: [https://platform.openai.com/docs/guides/embeddings](https://platform.openai.com/docs/guides/embeddings) Request throttling: Uses the rate limit-related headers returned by the API to throttle requests adaptively, based on available request and token capacity. No configuration is necessary. **Requirements:** * `pip install openai` **Parameters:** * **`input`** (`pxt.String`): The text to embed. * **`model`** (`pxt.String`): The model to use for the embedding. * **`model_kwargs`** (`pxt.Json | None`): Additional keyword args for the OpenAI `embeddings` API. For details on the available parameters, see: [https://platform.openai.com/docs/api-reference/embeddings](https://platform.openai.com/docs/api-reference/embeddings) **Returns:** * `pxt.Array[(None,), float32]`: An array representing the application of the given embedding to `input`. **Examples:** Add a computed column that applies the model `text-embedding-3-small` to an existing Pixeltable column `tbl.text` of the table `tbl`: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} tbl.add_computed_column( embed=embeddings(tbl.text, model='text-embedding-3-small') ) ``` Add an embedding index to an existing column `text`, using the model `text-embedding-3-small`: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} tbl.add_embedding_index( embedding=embeddings.using(model='text-embedding-3-small') ) ``` ## udf  image\_edits() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf image_edits( image: pxt.Image, *, prompt: pxt.String, model: pxt.String, mask: pxt.Image | None = None, model_kwargs: pxt.Json | None = None ) -> ImagesResponse ``` Creates an edited or extended image given a source image and a prompt. Equivalent to the OpenAI `images/edits` API endpoint. For additional details, see: [https://developers.openai.com/api/docs/guides/image-generation#edit-images](https://developers.openai.com/api/docs/guides/image-generation#edit-images) Request throttling: Applies the rate limit set in the config (section `openai.rate_limits`; use the model id as the key). If no rate limit is configured, uses a default of 600 RPM. **Requirements:** * `pip install openai` **Parameters:** * **`image`** (`pxt.Image`): The source image to edit. * **`prompt`** (`pxt.String`): A text description of the desired edit. * **`model`** (`pxt.String`): The model to use for image editing. * **`mask`** (`pxt.Image | None`): An optional mask image. See: [https://developers.openai.com/api/reference/resources/images/methods/edit](https://developers.openai.com/api/reference/resources/images/methods/edit) * **`model_kwargs`** (`pxt.Json | None`): Additional keyword args for the OpenAI `images/edits` API. For details on the available parameters, see: [https://developers.openai.com/api/reference/resources/images/methods/edit](https://developers.openai.com/api/reference/resources/images/methods/edit) **Returns:** * `ImagesResponse`: A dictionary containing the edited image data. Images will be deserialized into `PIL.Image.Image` objects, and the result dictionary will have the following form: ```json theme={"theme":{"light":"light-plus","dark":"dark-plus"}} { "created": 1234567890, "data": [ PIL.Image.Image(...), ... ], "usage": } ``` **Examples:** Edit an image with a text prompt: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} tbl.add_computed_column( edited=image_edits( tbl.source_image, prompt='Add a sunset background', model='gpt-image-2', ) ) ``` Edit an image with a mask to specify the edit area: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} tbl.add_computed_column( edited=image_edits( tbl.source_image, mask=tbl.mask_image, prompt='Replace with a beach scene', model='gpt-image-2', ) ) ``` ## udf  image\_generations() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf image_generations( prompt: pxt.String, *, model: pxt.String, model_kwargs: pxt.Json | None = None ) -> ImagesResponse ``` Creates an image given a prompt. Equivalent to the OpenAI `images/generations` API endpoint. For additional details, see: [https://platform.openai.com/docs/guides/images](https://platform.openai.com/docs/guides/images) Request throttling: Applies the rate limit set in the config (section `openai.rate_limits`; use the model id as the key). If no rate limit is configured, uses a default of 600 RPM. **Requirements:** * `pip install openai` **Parameters:** * **`prompt`** (`pxt.String`): Prompt for the image. * **`model`** (`pxt.String`): The model to use for the generations. See the OpenAI docs for supported models. * **`model_kwargs`** (`pxt.Json | None`): Additional keyword args for the OpenAI `images/generations` API. For details on the available parameters, see: [https://developers.openai.com/api/reference/resources/images/methods/generate](https://developers.openai.com/api/reference/resources/images/methods/generate) **Returns:** * `ImagesResponse`: A dictionary containing the generated image data. Images will be deserialized into `PIL.Image.Image` objects, and the result dictionary will have the following form: ```json theme={"theme":{"light":"light-plus","dark":"dark-plus"}} { "created": 1234567890, "data": [ PIL.Image.Image(...), PIL.Image.Image(...), ... ], "usage": } ``` **Examples:** Add a computed column that applies the model `gpt-image-1.5` to an existing Pixeltable column `tbl.text` of the table `tbl`: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} tbl.add_computed_column( gen_image=image_generations(tbl.text, model='gpt-image-1.5') ) ``` Generate an image using the `gpt-image-2` model: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} tbl.add_computed_column( gen_image=image_generations(tbl.text, model='gpt-image-2') ) ``` ## udf  image\_variations() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf image_variations( image: pxt.Image, *, model: pxt.String, model_kwargs: pxt.Json | None = None ) -> ImagesResponse ``` Creates a variation of a given image. Equivalent to the OpenAI `images/variations` API endpoint. For additional details, see: [https://developers.openai.com/api/docs/guides/image-generation#image-variations](https://developers.openai.com/api/docs/guides/image-generation#image-variations) Request throttling: Applies the rate limit set in the config (section `openai.rate_limits`; use the model id as the key). If no rate limit is configured, uses a default of 600 RPM. **Requirements:** * `pip install openai` **Parameters:** * **`image`** (`pxt.Image`): The source image to create a variation of. * **`model`** (`pxt.String`): The model to use for creating variations. * **`model_kwargs`** (`pxt.Json | None`): Additional keyword args for the OpenAI `images/variations` API. For details on the available parameters, see: [https://developers.openai.com/api/reference/resources/images/methods/create\_variation](https://developers.openai.com/api/reference/resources/images/methods/create_variation) **Returns:** * `ImagesResponse`: A dictionary containing the variation image data. Images will be deserialized into `PIL.Image.Image` objects, and the result dictionary will have the following form: ```json theme={"theme":{"light":"light-plus","dark":"dark-plus"}} { "created": 1234567890, "data": [ PIL.Image.Image(...), ... ] } ``` **Examples:** Generate a variation of an existing image: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} tbl.add_computed_column( variation=image_variations(tbl.source_image, model='gpt-image-1.5') ) ``` ## udf  moderations() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf moderations( input: pxt.String, *, model: pxt.String = 'omni-moderation-latest' ) -> ModerationCreateResponse ``` Classifies if text is potentially harmful. Equivalent to the OpenAI `moderations` API endpoint. For additional details, see: [https://platform.openai.com/docs/guides/moderation](https://platform.openai.com/docs/guides/moderation) Request throttling: Applies the rate limit set in the config (section `openai.rate_limits`; use the model id as the key). If no rate limit is configured, uses a default of 600 RPM. **Requirements:** * `pip install openai` **Parameters:** * **`input`** (`pxt.String`): Text to analyze with the moderations model. * **`model`** (`pxt.String`): The model to use for moderations. **Returns:** * `ModerationCreateResponse`: Details of the moderations results. **Examples:** Add a computed column that applies the default moderation model to an existing Pixeltable column `tbl.input` of the table `tbl`: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} tbl.add_computed_column(moderations=moderations(tbl.input)) ``` ## udf  responses() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf responses( input: pxt.Json[(Json, ...)], *, model: pxt.String, model_kwargs: pxt.Json | None = None, tools: pxt.Json[(Json, ...)] | None = None, tool_choice: pxt.Json | None = None ) -> pxt.Json ``` Creates a model response for the given input. Equivalent to the OpenAI `responses` API endpoint. For additional details, see: [https://developers.openai.com/api/docs/guides/migrate-to-responses](https://developers.openai.com/api/docs/guides/migrate-to-responses) Request throttling: Uses the rate limit-related headers returned by the API to throttle requests adaptively, based on available request and token capacity. No configuration is necessary. **Requirements:** * `pip install openai` **Parameters:** * **`input`** (`pxt.Json[(Json`): A list of input items for the model, as described in the OpenAI API documentation. * **`model`** (`Any`): The model to use for generating a response. * **`model_kwargs`** (`Any`): Additional keyword args for the OpenAI `responses` API. For details on the available parameters, see: [https://developers.openai.com/api/docs/api-reference/responses/create](https://developers.openai.com/api/docs/api-reference/responses/create) Common options include `instructions`, `temperature`, `max_output_tokens`, `reasoning`, `text` (for structured outputs), `previous_response_id` (for multi-turn), and `store`. **Returns:** * `pxt.Json`: A dictionary containing the response and other metadata. The response text is accessible via the `output_text` field for simple cases, or through the `output` list for structured access. **Examples:** Add a computed column that applies the model `gpt-4o-mini` to an existing Pixeltable column `tbl.prompt` of the table `tbl`: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} messages = [{'role': 'user', 'content': tbl.prompt}] tbl.add_computed_column( response=responses( messages, model='gpt-4o-mini', model_kwargs={'instructions': 'You are a helpful assistant.'}, ) ) ``` You can also include images in the input list by passing image data directly, using the Responses API `input_image` content type: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} messages = [ { 'role': 'user', 'content': [ {'type': 'input_text', 'text': "What's in this image?"}, {'type': 'input_image', 'image_url': tbl.image}, ], } ] tbl.add_computed_column(response=responses(messages, model='gpt-4o-mini')) ``` ## udf  speech() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf speech( input: pxt.String, *, model: pxt.String, voice: pxt.String, model_kwargs: pxt.Json | None = None ) -> pxt.Audio ``` Generates audio from the input text. Equivalent to the OpenAI `audio/speech` API endpoint. For additional details, see: [https://platform.openai.com/docs/guides/text-to-speech](https://platform.openai.com/docs/guides/text-to-speech) Request throttling: Applies the rate limit set in the config (section `openai.rate_limits`; use the model id as the key). If no rate limit is configured, uses a default of 600 RPM. **Requirements:** * `pip install openai` **Parameters:** * **`input`** (`pxt.String`): The text to synthesize into speech. * **`model`** (`pxt.String`): The model to use for speech synthesis. * **`voice`** (`pxt.String`): The voice profile to use for speech synthesis. Supported options include: `alloy`, `echo`, `fable`, `onyx`, `nova`, and `shimmer`. * **`model_kwargs`** (`pxt.Json | None`): Additional keyword args for the OpenAI `audio/speech` API. For details on the available parameters, see: [https://platform.openai.com/docs/api-reference/audio/createSpeech](https://platform.openai.com/docs/api-reference/audio/createSpeech) **Returns:** * `pxt.Audio`: An audio file containing the synthesized speech. **Examples:** Add a computed column that applies the model `tts-1` to an existing Pixeltable column `tbl.text` of the table `tbl`: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} tbl.add_computed_column( audio=speech(tbl.text, model='tts-1', voice='nova') ) ``` ## udf  transcriptions() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf transcriptions( audio: pxt.Audio, *, model: pxt.String, model_kwargs: pxt.Json | None = None ) -> TranscriptionResponse ``` Transcribes audio into the input language. Equivalent to the OpenAI `audio/transcriptions` API endpoint. For additional details, see: [https://platform.openai.com/docs/guides/speech-to-text](https://platform.openai.com/docs/guides/speech-to-text) Request throttling: Applies the rate limit set in the config (section `openai.rate_limits`; use the model id as the key). If no rate limit is configured, uses a default of 600 RPM. **Requirements:** * `pip install openai` **Parameters:** * **`audio`** (`pxt.Audio`): The audio to transcribe. * **`model`** (`pxt.String`): The model to use for speech transcription. * **`model_kwargs`** (`pxt.Json | None`): Additional keyword args for the OpenAI `audio/transcriptions` API. For details on the available parameters, see: [https://platform.openai.com/docs/api-reference/audio/createTranscription](https://platform.openai.com/docs/api-reference/audio/createTranscription) **Returns:** * `TranscriptionResponse`: A dictionary containing the transcription and other metadata. **Examples:** Add a computed column that applies the model `whisper-1` to an existing Pixeltable column `tbl.audio` of the table `tbl`: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} tbl.add_computed_column( transcription=transcriptions( tbl.audio, model='whisper-1', language='en' ) ) ``` ## udf  translations() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf translations( audio: pxt.Audio, *, model: pxt.String, model_kwargs: pxt.Json | None = None ) -> TranslationResponse ``` Translates audio into English. Equivalent to the OpenAI `audio/translations` API endpoint. For additional details, see: [https://platform.openai.com/docs/guides/speech-to-text](https://platform.openai.com/docs/guides/speech-to-text) Request throttling: Applies the rate limit set in the config (section `openai.rate_limits`; use the model id as the key). If no rate limit is configured, uses a default of 600 RPM. **Requirements:** * `pip install openai` **Parameters:** * **`audio`** (`pxt.Audio`): The audio to translate. * **`model`** (`pxt.String`): The model to use for speech transcription and translation. * **`model_kwargs`** (`pxt.Json | None`): Additional keyword args for the OpenAI `audio/translations` API. For details on the available parameters, see: [https://platform.openai.com/docs/api-reference/audio/createTranslation](https://platform.openai.com/docs/api-reference/audio/createTranslation) **Returns:** * `TranslationResponse`: A dictionary containing the translation and other metadata. **Examples:** Add a computed column that applies the model `whisper-1` to an existing Pixeltable column `tbl.audio` of the table `tbl`: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} tbl.add_computed_column( translation=translations(tbl.audio, model='whisper-1', language='en') ) ``` # openrouter Source: https://docs.pixeltable.com/sdk/latest/openrouter View Source on GitHub # module  pixeltable.functions.openrouter Pixeltable UDFs that wrap the OpenRouter API. OpenRouter provides a unified interface to multiple LLM providers. In order to use it, you must first sign up at [https://openrouter.ai](https://openrouter.ai), create an API key, and configure it as described in the Working with OpenRouter tutorial. ## udf  chat\_completions() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf chat_completions( messages: pxt.Json[(Json, ...)], *, model: pxt.String, model_kwargs: pxt.Json | None = None, tools: pxt.Json[(Json, ...)] | None = None, tool_choice: pxt.Json | None = None, provider: pxt.Json | None = None, transforms: pxt.Json[(String, ...)] | None = None ) -> pxt.Json ``` Chat Completion API via OpenRouter. OpenRouter provides access to multiple LLM providers through a unified API. For additional details, see: [https://openrouter.ai/docs](https://openrouter.ai/docs) Supported models can be found at: [https://openrouter.ai/models](https://openrouter.ai/models) Request throttling: Applies the rate limit set in the config (section `openrouter`, key `rate_limit`). If no rate limit is configured, uses a default of 600 RPM. **Requirements:** * `pip install openai` **Parameters:** * **`messages`** (`pxt.Json[(Json`): A list of messages comprising the conversation so far. * **`model`** (`Any`): ID of the model to use (e.g., `'anthropic/claude-3.5-sonnet'`, `'openai/gpt-4'`). * **`model_kwargs`** (`Any`): Additional OpenAI-compatible parameters. * **`tools`** (`Any`): List of tools available to the model. * **`tool_choice`** (`Any`): Controls which (if any) tool is called by the model. * **`provider`** (`Any`): OpenRouter-specific provider preferences (e.g., `{'order': ['Anthropic', 'OpenAI']}`). * **`transforms`** (`Any`): List of message transforms to apply (e.g., `['middle-out']`). **Returns:** * `pxt.Json`: A dictionary containing the response in OpenAI format. **Examples:** Basic chat completion: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} messages = [{'role': 'user', 'content': tbl.prompt}] tbl.add_computed_column( response=chat_completions( messages, model='anthropic/claude-3.5-sonnet' ) ) ``` With provider routing: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} tbl.add_computed_column( response=chat_completions( messages, model='anthropic/claude-3.5-sonnet', provider={'require_parameters': True, 'order': ['Anthropic']}, ) ) ``` With transforms: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} tbl.add_computed_column( response=chat_completions( messages, model='openai/gpt-4', transforms=['middle-out'], # Optimize for long contexts ) ) ``` # pixeltable Source: https://docs.pixeltable.com/sdk/latest/pixeltable View Source on GitHub # module  opentelemetry.instrumentation.pixeltable OpenTelemetry instrumentation for Pixeltable. Translates Pixeltable's instrumentation hooks into OpenTelemetry spans and metrics. Instrumentation is opt-in: either attach to an existing OpenTelemetry SDK with [PixeltableInstrumentor](./pixeltableinstrumentor), or let [init](./pixeltable#func-init) build providers from Pixeltable's `[otel]` config. ## func  init() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} init( *, endpoint: str | None = None, protocol: str | None = None, service_name: str | None = None, headers: str | None = None, span_level: Literal['info', 'debug', 'trace'] | None = None, metrics: bool | None = None, logs: bool | None = None, tracer_provider: Any = None, meter_provider: Any = None ) -> None ``` Configure pixeltable's OpenTelemetry instrumentation and start emitting telemetry. Call once, before the first Pixeltable operation. Each argument overrides the matching `[otel]` config setting and its standard `OTEL_*` environment variable; when an argument is left as None the value resolves from the database's `[[pixeltable.database]]` entry where it sets one (the endpoint and protocol, as `db_`), then that env var, then the `[otel]` config section. Pass `tracer_provider`/`meter_provider` to instrument against an SDK your application owns instead. **Parameters:** * **`endpoint`** (`str | None`): OTLP collector endpoint (eg `http://localhost:4318`); resolves from the database entry's `db_exporter_otlp_endpoint`, then `OTEL_EXPORTER_OTLP_ENDPOINT`, then `otel.exporter_otlp_endpoint`. With no endpoint configured and no application-owned provider, instrumentation stays inert and exports nothing. * **`protocol`** (`str | None`): OTLP transport, `http/protobuf` (default) or `grpc`; resolves from the database entry's `db_exporter_otlp_protocol`, then `OTEL_EXPORTER_OTLP_PROTOCOL`, then `otel.exporter_otlp_protocol`. * **`service_name`** (`str | None`): `service.name` resource attribute (default `pixeltable`); resolves from `otel.service_name` / `OTEL_SERVICE_NAME`. * **`headers`** (`str | None`): OTLP headers as comma-separated `key=value` pairs; resolves from `otel.exporter_otlp_headers` / `OTEL_EXPORTER_OTLP_HEADERS`. * **`span_level`** (`Literal['info', 'debug', 'trace'] | None`): span emission threshold: `info` (default; operation-level spans only), `debug` (adds per-row and per-UDF spans), or `trace`. * **`metrics`** (`bool | None`): force metric export on/off (by default metrics are exported only when an OTLP endpoint is configured). * **`logs`** (`bool | None`): force log export on/off (default off; must be explicitly enabled). * **`tracer_provider`** (`Any`): an existing TracerProvider to instrument against. * **`meter_provider`** (`Any`): an existing MeterProvider to instrument against. **Examples:** ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} import opentelemetry.instrumentation.pixeltable as pxt_otel pxt_otel.init(endpoint='http://localhost:4318') ``` # PixeltableInstrumentor Source: https://docs.pixeltable.com/sdk/latest/pixeltableinstrumentor View Source on GitHub # class  opentelemetry.instrumentation.pixeltable.PixeltableInstrumentor Enables OpenTelemetry instrumentation of pixeltable against the given (or global) providers. `instrument()` accepts `tracer_provider`, `meter_provider`, and `span_level` (span emission threshold: `info` (default), `debug`, or `trace`). ## method  instrument() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} instrument(**kwargs: Any) ``` Instrument the library This method will be called without any optional arguments by the `opentelemetry-instrument` command. This means that calling this method directly without passing any optional values should do the very same thing that the `opentelemetry-instrument` command does. ## method  uninstrument() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} uninstrument(**kwargs: Any) ``` Uninstrument the library See `BaseInstrumentor.instrument` for more information regarding the usage of `kwargs`. # Query Source: https://docs.pixeltable.com/sdk/latest/query View Source on GitHub # class  pixeltable.Query A query over tables in a catalog: it can be executed, and its rows updated or deleted. Thread-safe. ## method  collect() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} collect() -> ResultSet ``` ## method  cursor() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} cursor() -> ResultCursor ``` Return a [`ResultCursor`](./resultcursor) that iterates over the query results row by row. See [`ResultCursor`](./resultcursor) for usage examples and lifecycle details. ## method  distinct() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} distinct() -> Self ``` Remove duplicate rows from this Query. Note that grouping will be applied to the rows based on the select clause of this Query. In the absence of a select clause, by default, all columns are selected in the grouping. **Examples:** Select unique addresses from table `addresses`. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} results = addresses.distinct() ``` Select unique cities in table `addresses` ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} results = addresses.select(addresses.city).distinct() ``` Select unique locations (street, city) in the state of `CA` ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} results = ( addresses.select(addresses.street, addresses.city) .where(addresses.state == 'CA') .distinct() ) ``` ## method  group\_by() ```python Signatures theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Signature 1: group_by(grouping_tbl: catalog.Table, /) -> Self # Signature 2: group_by(*grouping_items: exprs.Expr) -> Self ``` Add a group-by clause to this Query. Variants: * group\_by(base\_tbl): group a component view by their respective base table rows * group\_by(expr1, expr2, expr3): group by the given expressions Note that grouping will be applied to the rows and take effect when used with an aggregation function like sum(), count() etc. **Parameters:** * **`grouping_items`** (`exprs.Expr | catalog.Table`): expressions to group by **Returns:** * `Self`: A new query with the specified group-by clause. **Examples:** Given the Query book from a table t with all its columns and rows: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} book = t.select() ``` Group the above Query book by the 'genre' column (referenced in table t): ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} query = book.group_by(t.genre) ``` Use the above Query grouped by genre to count the number of books for each 'genre': ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} query = ( book.group_by(t.genre).select(t.genre, count=count(t.genre)).show() ) ``` Use the above Query grouped by genre to the total price of books for each 'genre': ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} query = book.group_by(t.genre).select(t.genre, total=sum(t.price)).show() ``` ## method  head() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} head(n: int = 10) -> ResultSet ``` Return the first n rows of the Query, in insertion order of the underlying Table. head() is not supported for joins. **Parameters:** * **`n`** (`int`, default: `10`): Number of rows to select. Default is 10. **Returns:** * `ResultSet`: A ResultSet with the first n rows of the Query. ## method  join() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} join( other: catalog.Table, on: exprs.Expr | Sequence[exprs.ColumnRef] | None = None, how: JoinType.LiteralType = 'inner' ) -> Self ``` Join this Query with a table. **Parameters:** * **`other`** (`catalog.Table`): the table to join with * **`on`** (`exprs.Expr | Sequence[exprs.ColumnRef] | None`): the join condition, which can be either a) references to one or more columns or b) a boolean expression. * column references: implies an equality predicate that matches columns in both this Query and `other` by name. * column in `other`: A column with that same name must be present in this Query, and **it must be unique** (otherwise the join is ambiguous). * column in this Query: A column with that same name must be present in `other`. * boolean expression: The expressions must be valid in the context of the joined tables. * **`how`** (`JoinType.LiteralType`, default: `'inner'`): the type of join to perform. * `'inner'`: only keep rows that have a match in both * `'left'`: keep all rows from this Query and only matching rows from the other table * `'right'`: keep all rows from the other table and only matching rows from this Query * `'full_outer'`: keep all rows from both this Query and the other table * `'cross'`: Cartesian product; no `on` condition allowed **Returns:** * `Self`: A new query. **Examples:** Perform an inner join between t1 and t2 on the column id: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} join1 = t1.join(t2, on=t2.id) ``` Perform a left outer join of join1 with t3, also on id (note that we can't specify `on=t3.id` here, because that would be ambiguous, since both t1 and t2 have a column named id): ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} join2 = join1.join(t3, on=t2.id, how='left') ``` Do the same, but now with an explicit join predicate: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} join2 = join1.join(t3, on=t2.id == t3.id, how='left') ``` Join t with d, which has a composite primary key (columns pk1 and pk2, with corresponding foreign key columns d1 and d2 in t): ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} query = t.join(d, on=(t.d1 == d.pk1) & (t.d2 == d.pk2), how='left') ``` ## method  limit() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} limit(n: int, offset: int | None = None) -> Self ``` Limit the number of rows in the Query, optionally skipping rows for pagination. **Parameters:** * **`n`** (`int`): Number of rows to select. * **`offset`** (`int | None`): Number of rows to skip before returning results. Default is None (no offset). **Returns:** * `Self`: A new query with the specified limited rows. **Examples:** ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} query = t.select() ``` Get the first 10 rows: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} query.limit(10).collect() ``` Get rows 21-30 (skip first 20, return next 10): ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} query.limit(10, offset=20).collect() ``` ## method  order\_by() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} order_by(*expr_list: exprs.Expr, asc: bool = True) -> Self ``` Add an order-by clause to this Query. **Parameters:** * **`expr_list`** (`exprs.Expr`): expressions to order by * **`asc`** (`bool`, default: `True`): whether to order in ascending order (True) or descending order (False). Default is True. **Returns:** * `Self`: A new query with the specified order-by clause. **Examples:** Given the Query book from a table t with all its columns and rows: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} book = t.select() ``` Order the above Query book by two columns (price, pages) in descending order: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} query = book.order_by(t.price, t.pages, asc=False) ``` Order the above Query book by price in descending order, but order the pages in ascending order: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} query = book.order_by(t.price, asc=False).order_by(t.pages) ``` ## method  sample() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} sample( n: int | None = None, n_per_stratum: int | None = None, fraction: float | None = None, seed: int | None = None, stratify_by: Any = None ) -> Self ``` Return a new query specifying a sample of rows from this query, considered in a shuffled order. The size of the sample can be specified in three ways: * `n`: the total number of rows to produce as a sample * `n_per_stratum`: the number of rows to produce per stratum as a sample * `fraction`: the fraction of available rows to produce as a sample The sample can be stratified by one or more columns, which means that the sample will be selected from each stratum separately. The data is shuffled before creating the sample. **Parameters:** * **`n`** (`int | None`): Total number of rows to produce as a sample. * **`n_per_stratum`** (`int | None`): Number of rows to produce per stratum as a sample. This parameter is only valid if `stratify_by` is specified. Only one of `n` or `n_per_stratum` can be specified. * **`fraction`** (`float | None`): Fraction of available rows to produce as a sample. This parameter is not usable with `n` or `n_per_stratum`. The fraction must be between 0.0 and 1.0. * **`seed`** (`int | None`): Random seed for reproducible shuffling * **`stratify_by`** (`Any`): If specified, the sample will be stratified by these values. **Returns:** * `Self`: A new query which specifies the sampled rows **Examples:** Given the Table `person` containing the field 'age', we can create samples of the table in various ways: Sample 100 rows from the above Table: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} query = person.sample(n=100) ``` Sample 10% of the rows from the above Table: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} query = person.sample(fraction=0.1) ``` Sample 10% of the rows from the above Table, stratified by the column 'age': ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} query = person.sample(fraction=0.1, stratify_by=t.age) ``` Equal allocation sampling: Sample 2 rows from each age present in the above Table: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} query = person.sample(n_per_stratum=2, stratify_by=t.age) ``` Sampling is compatible with the where clause, so we can also sample from a filtered Query: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} query = person.where(t.age > 30).sample(n=100) ``` ## method  select() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} select(*items: Any, **named_items: Any) -> Self ``` Select columns or expressions from the Query. **Parameters:** * **`items`** (`Any`): expressions to be selected * **`named_items`** (`Any`): named expressions to be selected **Returns:** * `Self`: A new query with the specified select list. **Examples:** Given the Query person from a table t with all its columns and rows: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} person = t.select() ``` Select the columns 'name' and 'age' (referenced in table t) from the Query person: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} query = person.select(t.name, t.age) ``` Select the columns 'name' (referenced in table t) from the Query person, and a named column 'is\_adult' from the expression `age >= 18` where 'age' is another column in table t: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} query = person.select(t.name, is_adult=(t.age >= 18)) ``` ## method  show() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} show(n: int = 20) -> ResultSet ``` ## method  tail() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} tail(n: int = 10) -> ResultSet ``` Return the last n rows of the Query, in insertion order of the underlying Table. tail() is not supported for joins. **Parameters:** * **`n`** (`int`, default: `10`): Number of rows to select. Default is 10. **Returns:** * `ResultSet`: A ResultSet with the last n rows of the Query. ## method  to\_coco\_dataset() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} to_coco_dataset() -> Path ``` Convert the Query to a COCO dataset. This Query must return a single json-typed output column in the following format: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} { 'image': PIL.Image.Image, 'annotations': [ { 'bbox': [x: int, y: int, w: int, h: int], 'category': str | int, }, ... ], } ``` **Returns:** * `Path`: Path to the COCO dataset file. ## method  to\_pytorch\_dataset() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} to_pytorch_dataset(image_format: str = 'pt') -> torch.utils.data.IterableDataset ``` Convert the Query to a pytorch IterableDataset suitable for parallel loading with torch.utils.data.DataLoader. This method requires pyarrow >= 13, torch and torchvision to work. This method serializes data so it can be read from disk efficiently and repeatedly without re-executing the query. This data is cached to disk for future re-use. **Parameters:** * **`image_format`** (`str`, default: `'pt'`): format of the images. Can be 'pt' (pytorch tensor) or 'np' (numpy array). 'np' means image columns return as an RGB uint8 array of shape HxWxC. 'pt' means image columns return as a CxHxW tensor with values in \[0,1] and type torch.float32. (the format output by torchvision.transforms.ToTensor()) **Returns:** * `'torch.utils.data.IterableDataset'`: A pytorch IterableDataset: Columns become fields of the dataset, where rows are returned as a dictionary compatible with torch.utils.data.DataLoader default collation. ## method  where() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} where(pred: exprs.Expr) -> Self ``` Filter rows based on a predicate. **Parameters:** * **`pred`** (`exprs.Expr`): the predicate to filter rows **Returns:** * `Self`: A new query with the specified predicates replacing the where-clause. **Examples:** Given the Query person from a table t with all its columns and rows: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} person = t.select() ``` Filter the above Query person to only include rows where the column 'age' (referenced in table t) is greater than 30: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} query = person.where(t.age > 30) ``` # replicate Source: https://docs.pixeltable.com/sdk/latest/replicate View Source on GitHub # module  pixeltable.functions.replicate Pixeltable UDFs that wrap various endpoints from the Replicate API. In order to use them, you must first `pip install replicate` and configure your Replicate credentials, as described in the [Working with Replicate](https://docs.pixeltable.com/howto/providers/working-with-replicate) tutorial. ## udf  run() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf run(input: pxt.Json, *, ref: pxt.String) -> pxt.Json ``` Run a model on Replicate. For additional details, see: [https://replicate.com/docs/topics/models/run-a-model](https://replicate.com/docs/topics/models/run-a-model) Request throttling: Applies the rate limit set in the config (section `replicate`, key `rate_limit`). If no rate limit is configured, uses a default of 600 RPM. **Requirements:** * `pip install replicate` **Parameters:** * **`input`** (`pxt.Json`): The input parameters for the model. * **`ref`** (`pxt.String`): The name of the model to run. **Returns:** * `pxt.Json`: The output of the model. **Examples:** Add a computed column that applies the model `meta/meta-llama-3-8b-instruct` to an existing Pixeltable column `tbl.prompt` of the table `tbl`: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} input = { 'system_prompt': 'You are a helpful assistant.', 'prompt': tbl.prompt, } tbl.add_computed_column( response=run(input, ref='meta/meta-llama-3-8b-instruct') ) ``` Add a computed column that uses the model `black-forest-labs/flux-schnell` to generate images from an existing Pixeltable column `tbl.prompt`: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} input = {'prompt': tbl.prompt, 'go_fast': True, 'megapixels': '1'} tbl.add_computed_column( response=run(input, ref='black-forest-labs/flux-schnell') ) tbl.add_computed_column(image=tbl.response.output[0].astype(pxt.Image)) ``` # ResultCursor Source: https://docs.pixeltable.com/sdk/latest/resultcursor View Source on GitHub # class  pixeltable.ResultCursor Cursor that iterates over query results. Wraps a Query and yields Row objects one at a time, avoiding materializing all results into memory. A cursor transitions through three states: pending (created but not yet started), open (actively iterating), and closed (resources released). Iteration auto-opens and auto-closes the cursor, or you can use it as a context manager for explicit lifecycle control. ## method  close() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} close() -> None ``` Release the underlying database transaction and query resources. Safe to call multiple times. Once closed, the cursor cannot be reopened. Also called automatically via the context manager protocol and on garbage collection. ## method  open() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} open() -> None ``` Start the underlying query and prepare the cursor for iteration. Raises an error if the cursor is already open or has been closed. Called automatically when iterating if not already open. # ResultSet Source: https://docs.pixeltable.com/sdk/latest/resultset View Source on GitHub # class  pixeltable.ResultSet A dataset obtained by executing a [`Query`](./query). Returned by [`Query.collect()`](./query#method-collect), [`Query.head()`](./query#method-head), [`Query.tail()`](./query#method-tail), and the equivalent methods on class [`Table`](./table). A `ResultSet` is structured as a table with rows (indexed by integers) and columns (indexed by strings). The column names correspond to the expressions in the query's select list. The values in a `ResultSet` can be accessed in various ways: * `len(result)` returns the number of rows * `result[i]` returns the `i`th row as a `dict` mapping column names to values * `result['col']` returns a `list` of all values in the column named `'col'` * `result[i, 'col']` returns the specific value in the `i`th row and column `'col'` `ResultSet` implements the Sequence protocol, so it can be iterated over and converted to other sequence types in the usual fashion; for example: * `for row in result` (iterates over rows) * `list(result)` (converts to a list of rows) ## method  to\_pandas() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} to_pandas() -> pd.DataFrame ``` Convert the `ResultSet` to a Pandas `DataFrame`. **Returns:** * `pd.DataFrame`: A `DataFrame` with one column per column in the `ResultSet`. ## method  to\_pydantic() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} to_pydantic(model: type[BaseModelT]) -> Iterator[BaseModelT] ``` Convert the `ResultSet` to Pydantic model instances. **Parameters:** * **`model`** (`type[BaseModelT]`): A Pydantic model class. **Returns:** * `Iterator[BaseModelT]`: An iterator over Pydantic model instances, one for each row in the result set. # Row Source: https://docs.pixeltable.com/sdk/latest/row View Source on GitHub # class  pixeltable.Row A dict-like wrapper over a single result row. Supports key access (`row['col']`), membership (`'col' in row`), iteration over keys, and the standard `get()`, `keys()`, `values()`, and `items()` methods. The `errors` property holds error info (`{'errortype': ..., 'errormsg': ...}`) for each cell whose evaluation failed; the `index_values` property holds the values of embedding indexes defined on the row's table. Both are keyed by column or index name. Keys are case-sensitive: every name is the folded (lower-case) spelling stored in the catalog. ## method  to\_json() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} to_json() -> dict[str, Any] ``` Return a JSON-serializable dict of this row's values. * `None`: preserved as `None` * Timestamp, Date: ISO 8601 string * UUID: string * Array: Python list (via `tolist()`) * Json: validated for serializability, kept as native Python * Binary: omitted (not representable in JSON) * All others: unchanged # RowBatch Source: https://docs.pixeltable.com/sdk/latest/rowbatch View Source on GitHub # class  pixeltable.RowBatch A sequence of [`Row`](./row) instances that share a common schema. Supports indexing (`batch[0]`), iteration, `len()`. ## method  to\_json() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} to_json() -> list[dict[str, Any]] ``` Return a JSON-serializable list of row dicts (see [`Row.to_json()`](./row#method-to_json)). # runwayml Source: https://docs.pixeltable.com/sdk/latest/runwayml View Source on GitHub # module  pixeltable.functions.runwayml Pixeltable UDFs that wrap various endpoints from the RunwayML API. In order to use them, you must first `pip install runwayml` and configure your RunwayML credentials by setting the `RUNWAYML_API_SECRET` environment variable. ## udf  image\_to\_video() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf image_to_video( prompt_image: pxt.Image, model: pxt.String, ratio: pxt.String, *, prompt_text: pxt.String | None = None, duration: pxt.Int | None = None, seed: pxt.Int | None = None, audio: pxt.Bool | None = None, model_kwargs: pxt.Json | None = None ) -> pxt.Json ``` Generate videos from images. For additional details, see: [Image to video](https://docs.dev.runwayml.com/api/#tag/Start-generating/paths/~1v1~1image_to_video/post) **Requirements:** * `pip install runwayml` **Parameters:** * **`prompt_image`** (`pxt.Image`): Input image to use as the first frame. * **`model`** (`pxt.String`): The model to use. * **`ratio`** (`pxt.String`): Aspect ratio of the generated video. * **`prompt_text`** (`pxt.String | None`): Text description to guide generation. * **`duration`** (`pxt.Int | None`): Duration in seconds. * **`seed`** (`pxt.Int | None`): Seed for reproducibility. * **`audio`** (`pxt.Bool | None`): Whether to generate audio. * **`model_kwargs`** (`pxt.Json | None`): Additional API parameters. **Returns:** * `pxt.Json`: A dictionary containing the response and metadata. **Examples:** Add a computed column that generates videos from images: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} tbl.add_computed_column( response=image_to_video( tbl.image, model='gen4_turbo', ratio='16:9', prompt_text='Slow motion', duration=5, ) ) tbl.add_computed_column(video=tbl.response['output'].astype(pxt.Video)) ``` ## udf  text\_to\_image() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf text_to_image( prompt_text: pxt.String, reference_images: pxt.Json[(Image, ...)], model: pxt.String, ratio: pxt.String, *, seed: pxt.Int | None = None, model_kwargs: pxt.Json | None = None ) -> pxt.Json ``` Generate images from text prompts and reference images. For additional details, see: [Text/Image to Image](https://docs.dev.runwayml.com/api/#tag/Start-generating/paths/~1v1~1text_to_image/post) **Requirements:** * `pip install runwayml` **Parameters:** * **`prompt_text`** (`pxt.String`): Text description of the image to generate. * **`reference_images`** (`pxt.Json[(Image`): List of 1-3 reference images. * **`model`** (`Any`): The model to use. * **`ratio`** (`Any`): Aspect ratio of the generated image. * **`seed`** (`Any`): Seed for reproducibility. * **`model_kwargs`** (`Any`): Additional API parameters. **Returns:** * `pxt.Json`: A dictionary containing the response and metadata. **Examples:** Add a computed column that generates images from prompts: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} tbl.add_computed_column( response=text_to_image( tbl.prompt, [tbl.ref_image], model='gen4_image', ratio='16:9' ) ) tbl.add_computed_column(image=tbl.response['output'][0].astype(pxt.Image)) ``` ## udf  text\_to\_video() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf text_to_video( prompt_text: pxt.String, model: pxt.String, ratio: pxt.String, *, duration: pxt.Int | None = None, audio: pxt.Bool | None = None, model_kwargs: pxt.Json | None = None ) -> pxt.Json ``` Generate videos from text prompts. For additional details, see: [Text to video](https://docs.dev.runwayml.com/api/#tag/Start-generating/paths/~1v1~1text_to_video/post) **Requirements:** * `pip install runwayml` **Parameters:** * **`prompt_text`** (`pxt.String`): Text description of the video to generate. * **`model`** (`pxt.String`): The model to use. * **`ratio`** (`pxt.String`): Aspect ratio of the generated video. * **`duration`** (`pxt.Int | None`): Duration in seconds. * **`audio`** (`pxt.Bool | None`): Whether to generate audio. * **`model_kwargs`** (`pxt.Json | None`): Additional API parameters. **Returns:** * `pxt.Json`: A dictionary containing the response and metadata. **Examples:** Add a computed column that generates videos from prompts: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} tbl.add_computed_column( response=text_to_video( tbl.prompt, model='veo3.1', ratio='16:9', duration=4 ) ) tbl.add_computed_column(video=tbl.response['output'].astype(pxt.Video)) ``` ## udf  video\_to\_video() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf video_to_video( video_uri: pxt.String, prompt_text: pxt.String, model: pxt.String, ratio: pxt.String, *, seed: pxt.Int | None = None, model_kwargs: pxt.Json | None = None ) -> pxt.Json ``` Transform videos with text guidance. For additional details, see: [Video to video](https://docs.dev.runwayml.com/api/#tag/Start-generating/paths/~1v1~1video_to_video/post) **Requirements:** * `pip install runwayml` **Parameters:** * **`video_uri`** (`pxt.String`): HTTPS URL to the input video. * **`prompt_text`** (`pxt.String`): Text description of the transformation. * **`model`** (`pxt.String`): The model to use. * **`ratio`** (`pxt.String`): Aspect ratio of the output video. * **`seed`** (`pxt.Int | None`): Seed for reproducibility. * **`model_kwargs`** (`pxt.Json | None`): Additional API parameters. **Returns:** * `pxt.Json`: A dictionary containing the response and metadata. **Examples:** Add a computed column that transforms videos: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} tbl.add_computed_column( response=video_to_video( tbl.video_url, 'Anime style', model='aleph2', ratio='16:9' ) ) tbl.add_computed_column(video=tbl.response['output'].astype(pxt.Video)) ``` # SceneInfo Source: https://docs.pixeltable.com/sdk/latest/sceneinfo View Source on GitHub # class  pixeltable.functions.video.SceneInfo One scene detected within a video: where it starts and how long it lasts. ## attr  duration ``` duration: float ``` Scene duration, in seconds. ## attr  start\_pts ``` start_pts: int ``` Scene start, as a presentation timestamp (in the video stream's time base). ## attr  start\_time ``` start_time: float ``` Scene start, in seconds from the start of the video. # Secret Source: https://docs.pixeltable.com/sdk/latest/secret ## ⚠️ Error Loading Class Failed to load class `Secret`: ``` Class Secret not found in pixeltable ``` # serving Source: https://docs.pixeltable.com/sdk/latest/serving View Source on GitHub # module  pixeltable.serving Adapters for web serving frameworks. # SqlExport Source: https://docs.pixeltable.com/sdk/latest/sqlexport View Source on GitHub # class  pixeltable.serving.SqlExport Specification of an external RDBMS target for SQL export. **Parameters:** * **`db_connect`** (`Any`): SQLAlchemy connection string for the target database (e.g. `'postgresql+psycopg://user:pw@host/db'`, `'sqlite:///path/to.db'`). * **`table`** (`Any`): Name of the target table. It must already exist; resolution fails if the table is missing. * **`db_schema`** (`Any`): Optional database schema qualifier (e.g. `'analytics'`); leave `None` to use the connection's default schema. * **`method`** (`Any`): How to write each row into the target table. * `'insert'`: append the row via `INSERT ... VALUES`. * `'update'`: update the row by primary-key match (`UPDATE ... SET ... WHERE pk=...`). Requires that the target table has a primary key whose metadata is exposed by the dialect. The exported columns must include all primary-key columns of the target plus at least one non-PK column to set. This is a strict update, **not** an upsert: if the WHERE clause matches zero rows, the export fails. Useful when the source is append-only but the target is a deduplicated current-state view. * `'merge'`: upsert via the target table's primary key. **Currently not supported.** # StreamMetadata Source: https://docs.pixeltable.com/sdk/latest/streammetadata View Source on GitHub # class  pixeltable.functions.StreamMetadata Metadata for a stream within a media container. ## attr  average\_rate ``` average_rate: float | None ``` Average frame rate in FPS (frames per second). Present only for video streams. ## attr  base\_rate ``` base_rate: float | None ``` Base (constant) frame rate in FPS. Present only for video streams. ## attr  codec\_context ``` codec_context: CodecContextMetadata ``` Codec information for this stream. ## attr  duration ``` duration: int | None ``` Stream duration in `time_base` units, or `None` if unknown. ## attr  duration\_seconds ``` duration_seconds: float | None ``` Stream duration in seconds, computed from `duration` and `time_base`. ## attr  frames ``` frames: int ``` Number of frames in the stream (may be 0 if unknown). ## attr  guessed\_rate ``` guessed_rate: float | None ``` Guessed frame rate in FPS. Present only for video streams. ## attr  height ``` height: int ``` Frame height in pixels. Present only for video streams. ## attr  metadata ``` metadata: dict[str, str] ``` Additional stream-specific metadata tags (e.g. language, title). ## attr  time\_base ``` time_base: float | None ``` Time base of the stream as a float (seconds per tick), or `None` if unknown. ## attr  type ``` type: str ``` Stream type: typically `'audio'` or `'video'`. Other stream types (e.g. subtitles) will have a `StreamMetadata` entry, but with no metadata other than `type`. ## attr  width ``` width: int ``` Frame width in pixels. Present only for video streams. # string Source: https://docs.pixeltable.com/sdk/latest/string View Source on GitHub # module  pixeltable.functions.string Pixeltable UDFs for `StringType`. It closely follows the Pandas `pandas.Series.str` API. Example: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} import pixeltable as pxt t = pxt.get_table(...) t.select(t.str_col.capitalize()).collect() ``` ## iterator  string\_splitter() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.iterator string_splitter( text: pxt.String, separators: pxt.String, *, spacy_model: pxt.String = 'en_core_web_sm' ) ``` Iterator over chunks of a string. The string is chunked according to the specified `separators`. **Outputs**: One row per chunk, with the following columns: * `text` (`pxt.String`): The text of the chunk. **Parameters:** * **`separators`** (`pxt.String`): Separators to use to chunk the document. Currently the only supported option is `'sentence'`. * **`spacy_model`** (`pxt.String`): Name of the spaCy model to use for sentence segmentation. **Examples:** This example assumes an existing table `tbl` with a column `text` of type `pxt.String`. Create a view that splits all strings on sentence boundaries: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} pxt.create_view( 'sentence_chunks', tbl, iterator=string_splitter(tbl.text, separators='sentence'), ) ``` ## udf  capitalize() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf capitalize(self: pxt.String) -> pxt.String ``` Return string with its first character capitalized and the rest lowercased. Equivalent to [`str.capitalize()`](https://docs.python.org/3/library/stdtypes.html#str.capitalize). ## udf  casefold() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf casefold(self: pxt.String) -> pxt.String ``` Return a casefolded copy of string. Equivalent to [`str.casefold()`](https://docs.python.org/3/library/stdtypes.html#str.casefold). ## udf  center() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf center( self: pxt.String, width: pxt.Int, fillchar: pxt.String = ' ' ) -> pxt.String ``` Return a centered string of length `width`. Equivalent to [`str.center()`](https://docs.python.org/3/library/stdtypes.html#str.center). **Parameters:** * **`width`** (`pxt.Int`): Total width of the resulting string. * **`fillchar`** (`pxt.String`): Character used for padding. ## udf  contains() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf contains( self: pxt.String, substr: pxt.String, case: pxt.Bool = True ) -> pxt.Bool ``` Test if string contains a substring. **Parameters:** * **`substr`** (`pxt.String`): string literal or regular expression * **`case`** (`pxt.Bool`): if False, ignore case ## udf  contains\_re() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf contains_re( self: pxt.String, pattern: pxt.String, flags: pxt.Int = 0 ) -> pxt.Bool ``` Test if string contains a regular expression pattern. **Parameters:** * **`pattern`** (`pxt.String`): regular expression pattern * **`flags`** (`pxt.Int`): [flags](https://docs.python.org/3/library/re.html#flags) for the `re` module ## udf  count() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf count( self: pxt.String, pattern: pxt.String, flags: pxt.Int = 0 ) -> pxt.Int ``` Count occurrences of pattern or regex. **Parameters:** * **`pattern`** (`pxt.String`): string literal or regular expression * **`flags`** (`pxt.Int`): [flags](https://docs.python.org/3/library/re.html#flags) for the `re` module ## udf  endswith() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf endswith(self: pxt.String, substr: pxt.String) -> pxt.Bool ``` Return `True` if the string ends with the specified suffix, otherwise return `False`. Equivalent to [`str.endswith()`](https://docs.python.org/3/library/stdtypes.html#str.endswith). **Parameters:** * **`substr`** (`pxt.String`): string literal ## udf  fill() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf fill(self: pxt.String, width: pxt.Int, **kwargs) -> pxt.String ``` Wraps the single paragraph in string, and returns a single string containing the wrapped paragraph. Equivalent to [`textwrap.fill()`](https://docs.python.org/3/library/textwrap.html#textwrap.fill). **Parameters:** * **`width`** (`pxt.Int`): Maximum line width. * **`kwargs`** (`Any`): Additional keyword arguments to pass to `textwrap.fill()`. ## udf  find() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf find( self: pxt.String, substr: pxt.String, start: pxt.Int = 0, end: pxt.Int | None = None ) -> pxt.Int ``` Return the lowest index in string where `substr` is found within the slice `s[start:end]`. Equivalent to [`str.find()`](https://docs.python.org/3/library/stdtypes.html#str.find). **Parameters:** * **`substr`** (`pxt.String`): substring to search for * **`start`** (`pxt.Int`): slice start * **`end`** (`pxt.Int | None`): slice end ## udf  findall() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf findall( self: pxt.String, pattern: pxt.String, flags: pxt.Int = 0 ) -> pxt.Json[(Json, ...)] ``` Find all occurrences of a regular expression pattern in string. Equivalent to [`re.findall()`](https://docs.python.org/3/library/re.html#re.findall). **Parameters:** * **`pattern`** (`pxt.String`): regular expression pattern * **`flags`** (`pxt.Int`): [flags](https://docs.python.org/3/library/re.html#flags) for the `re` module ## udf  format() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf format(self: pxt.String, *args, **kwargs) -> pxt.String ``` Perform string formatting. Equivalent to [`str.format()`](https://docs.python.org/3/library/stdtypes.html#str.format). ## udf  fullmatch() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf fullmatch( self: pxt.String, pattern: pxt.String, case: pxt.Bool = True, flags: pxt.Int = 0 ) -> pxt.Bool ``` Determine if string fully matches a regular expression. Equivalent to [`re.fullmatch()`](https://docs.python.org/3/library/re.html#re.fullmatch). **Parameters:** * **`pattern`** (`pxt.String`): regular expression pattern * **`case`** (`pxt.Bool`): if False, ignore case * **`flags`** (`pxt.Int`): [flags](https://docs.python.org/3/library/re.html#flags) for the `re` module ## udf  index() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf index( self: pxt.String, substr: pxt.String, start: pxt.Int = 0, end: pxt.Int | None = None ) -> pxt.Int ``` Return the lowest index in string where `substr` is found within the slice `[start:end]`. Raises ValueError if `substr` is not found. Equivalent to [`str.index()`](https://docs.python.org/3/library/stdtypes.html#str.index). **Parameters:** * **`substr`** (`pxt.String`): substring to search for * **`start`** (`pxt.Int`): slice start * **`end`** (`pxt.Int | None`): slice end ## udf  isalnum() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf isalnum(self: pxt.String) -> pxt.Bool ``` Return `True` if all characters in the string are alphanumeric and there is at least one character, `False` otherwise. Equivalent to \[`str.isalnum()`]\([https://docs.python.org/3/library/stdtypes.html#str.isalnum](https://docs.python.org/3/library/stdtypes.html#str.isalnum) ## udf  isalpha() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf isalpha(self: pxt.String) -> pxt.Bool ``` Return `True` if all characters in the string are alphabetic and there is at least one character, `False` otherwise. Equivalent to [`str.isalpha()`](https://docs.python.org/3/library/stdtypes.html#str.isalpha). ## udf  isascii() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf isascii(self: pxt.String) -> pxt.Bool ``` Return `True` if the string is empty or all characters in the string are ASCII, `False` otherwise. Equivalent to [`str.isascii()`](https://docs.python.org/3/library/stdtypes.html#str.isascii). ## udf  isdecimal() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf isdecimal(self: pxt.String) -> pxt.Bool ``` Return `True` if all characters in the string are decimal characters and there is at least one character, `False` otherwise. Equivalent to [`str.isdecimal()`](https://docs.python.org/3/library/stdtypes.html#str.isdecimal). ## udf  isdigit() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf isdigit(self: pxt.String) -> pxt.Bool ``` Return `True` if all characters in the string are digits and there is at least one character, `False` otherwise. Equivalent to [`str.isdigit()`](https://docs.python.org/3/library/stdtypes.html#str.isdigit). ## udf  isidentifier() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf isidentifier(self: pxt.String) -> pxt.Bool ``` Return `True` if the string is a valid identifier according to the language definition, `False` otherwise. Equivalent to [`str.isidentifier()`](https://docs.python.org/3/library/stdtypes.html#str.isidentifier) ## udf  islower() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf islower(self: pxt.String) -> pxt.Bool ``` Return `True` if all cased characters in the string are lowercase and there is at least one cased character, `False` otherwise. Equivalent to [`str.islower()`](https://docs.python.org/3/library/stdtypes.html#str.islower) ## udf  isnumeric() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf isnumeric(self: pxt.String) -> pxt.Bool ``` Return `True` if all characters in the string are numeric characters, `False` otherwise. Equivalent to [`str.isnumeric()`](https://docs.python.org/3/library/stdtypes.html#str.isnumeric) ## udf  isspace() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf isspace(self: pxt.String) -> pxt.Bool ``` Return `True` if there are only whitespace characters in the string and there is at least one character, `False` otherwise. Equivalent to [`str.isspace()`](https://docs.python.org/3/library/stdtypes.html#str.isspace) ## udf  istitle() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf istitle(self: pxt.String) -> pxt.Bool ``` Return `True` if the string is a titlecased string and there is at least one character, `False` otherwise. Equivalent to [`str.istitle()`](https://docs.python.org/3/library/stdtypes.html#str.istitle) ## udf  isupper() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf isupper(self: pxt.String) -> pxt.Bool ``` Return `True` if all cased characters in the string are uppercase and there is at least one cased character, `False` otherwise. Equivalent to [`str.isupper()`](https://docs.python.org/3/library/stdtypes.html#str.isupper) ## udf  join() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf join( sep: pxt.String, elements: pxt.Json[(Json, ...)] ) -> pxt.String ``` Return a string which is the concatenation of the strings in `elements`. Equivalent to [`str.join()`](https://docs.python.org/3/library/stdtypes.html#str.join) ## udf  len() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf len(self: pxt.String) -> pxt.Int ``` Return the number of characters in the string. Equivalent to [`len(str)`](https://docs.python.org/3/library/functions.html#len) ## udf  ljust() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf ljust( self: pxt.String, width: pxt.Int, fillchar: pxt.String = ' ' ) -> pxt.String ``` Return the string left-justified in a string of length `width`. Equivalent to [`str.ljust()`](https://docs.python.org/3/library/stdtypes.html#str.ljust) **Parameters:** * **`width`** (`pxt.Int`): Minimum width of resulting string; additional characters will be filled with character defined in `fillchar`. * **`fillchar`** (`pxt.String`): Additional character for filling. ## udf  lower() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf lower(self: pxt.String) -> pxt.String ``` Return a copy of the string with all the cased characters converted to lowercase. Equivalent to [`str.lower()`](https://docs.python.org/3/library/stdtypes.html#str.lower) ## udf  lstrip() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf lstrip( self: pxt.String, chars: pxt.String | None = None ) -> pxt.String ``` Return a copy of the string with leading characters removed. The `chars` argument is a string specifying the set of characters to be removed. If omitted or `None`, whitespace characters are removed. Equivalent to [`str.lstrip()`](https://docs.python.org/3/library/stdtypes.html#str.lstrip) **Parameters:** * **`chars`** (`pxt.String | None`): The set of characters to be removed. ## udf  match() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf match( self: pxt.String, pattern: pxt.String, case: pxt.Bool = True, flags: pxt.Int = 0 ) -> pxt.Bool ``` Determine if string starts with a match of a regular expression **Parameters:** * **`pattern`** (`pxt.String`): regular expression pattern * **`case`** (`pxt.Bool`): if False, ignore case * **`flags`** (`pxt.Int`): [flags](https://docs.python.org/3/library/re.html#flags) for the `re` module ## udf  normalize() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf normalize(self: pxt.String, form: pxt.String) -> pxt.String ``` Return the Unicode normal form. Equivalent to [`unicodedata.normalize()`](https://docs.python.org/3/library/unicodedata.html#unicodedata.normalize) **Parameters:** * **`form`** (`pxt.String`): Unicode normal form (`'NFC'`, `'NFKC'`, `'NFD'`, `'NFKD'`) ## udf  pad() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf pad( self: pxt.String, width: pxt.Int, side: pxt.String = 'left', fillchar: pxt.String = ' ' ) -> pxt.String ``` Pad string up to width **Parameters:** * **`width`** (`pxt.Int`): Minimum width of resulting string; additional characters will be filled with character defined in `fillchar`. * **`side`** (`pxt.String`): Side from which to fill resulting string (`'left'`, `'right'`, `'both'`) * **`fillchar`** (`pxt.String`): Additional character for filling ## udf  partition() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf partition( self: pxt.String, sep: pxt.String = ' ' ) -> pxt.Json[(String, String, String)] ``` Splits string at the first occurrence of `sep`, and returns 3 elements containing the part before the separator, the separator itself, and the part after the separator. If the separator is not found, return 3 elements containing string itself, followed by two empty strings. ## udf  removeprefix() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf removeprefix(self: pxt.String, prefix: pxt.String) -> pxt.String ``` Remove prefix. If the prefix is not present, returns string. ## udf  removesuffix() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf removesuffix(self: pxt.String, suffix: pxt.String) -> pxt.String ``` Remove suffix. If the suffix is not present, returns string. ## udf  repeat() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf repeat(self: pxt.String, n: pxt.Int) -> pxt.String ``` Repeat string `n` times. ## udf  replace() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf replace( self: pxt.String, substr: pxt.String, repl: pxt.String, n: pxt.Int | None = None ) -> pxt.String ``` Replace occurrences of `substr` with `repl`. Equivalent to [`str.replace()`](https://docs.python.org/3/library/stdtypes.html#str.replace). **Parameters:** * **`substr`** (`pxt.String`): string literal * **`repl`** (`pxt.String`): replacement string * **`n`** (`pxt.Int | None`): number of replacements to make (if `None`, replace all occurrences) ## udf  replace\_re() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf replace_re( self: pxt.String, pattern: pxt.String, repl: pxt.String, n: pxt.Int | None = None, flags: pxt.Int = 0 ) -> pxt.String ``` Replace occurrences of a regular expression pattern with `repl`. Equivalent to [`re.sub()`](https://docs.python.org/3/library/re.html#re.sub). **Parameters:** * **`pattern`** (`pxt.String`): regular expression pattern * **`repl`** (`pxt.String`): replacement string * **`n`** (`pxt.Int | None`): number of replacements to make (if `None`, replace all occurrences) * **`flags`** (`pxt.Int`): [flags](https://docs.python.org/3/library/re.html#flags) for the `re` module ## udf  reverse() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf reverse(self: pxt.String) -> pxt.String ``` Return a reversed copy of the string. Equivalent to `str[::-1]`. ## udf  rfind() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf rfind( self: pxt.String, substr: pxt.String, start: pxt.Int | None = 0, end: pxt.Int | None = None ) -> pxt.Int ``` Return the highest index where `substr` is found, such that `substr` is contained within `[start:end]`. Equivalent to [`str.rfind()`](https://docs.python.org/3/library/stdtypes.html#str.rfind). **Parameters:** * **`substr`** (`pxt.String`): substring to search for * **`start`** (`pxt.Int | None`): slice start * **`end`** (`pxt.Int | None`): slice end ## udf  rindex() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf rindex( self: pxt.String, substr: pxt.String, start: pxt.Int | None = 0, end: pxt.Int | None = None ) -> pxt.Int ``` Return the highest index where `substr` is found, such that `substr` is contained within `[start:end]`. Raises ValueError if `substr` is not found. Equivalent to [`str.rindex()`](https://docs.python.org/3/library/stdtypes.html#str.rindex). ## udf  rjust() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf rjust( self: pxt.String, width: pxt.Int, fillchar: pxt.String = ' ' ) -> pxt.String ``` Return the string right-justified in a string of length `width`. Equivalent to [`str.rjust()`](https://docs.python.org/3/library/stdtypes.html#str.rjust). **Parameters:** * **`width`** (`pxt.Int`): Minimum width of resulting string. * **`fillchar`** (`pxt.String`): Additional character for filling. ## udf  rpartition() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf rpartition( self: pxt.String, sep: pxt.String = ' ' ) -> pxt.Json[(String, String, String)] ``` This method splits string at the last occurrence of `sep`, and returns a list containing the part before the separator, the separator itself, and the part after the separator. ## udf  rstrip() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf rstrip( self: pxt.String, chars: pxt.String | None = None ) -> pxt.String ``` Return a copy of string with trailing characters removed. Equivalent to [`str.rstrip()`](https://docs.python.org/3/library/stdtypes.html#str.rstrip). **Parameters:** * **`chars`** (`pxt.String | None`): The set of characters to be removed. If omitted or `None`, whitespace characters are removed. ## udf  slice() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf slice( self: pxt.String, start: pxt.Int | None = None, stop: pxt.Int | None = None, step: pxt.Int | None = None ) -> pxt.String ``` Return a slice. **Parameters:** * **`start`** (`pxt.Int | None`): slice start * **`stop`** (`pxt.Int | None`): slice end * **`step`** (`pxt.Int | None`): slice step ## udf  slice\_replace() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf slice_replace( self: pxt.String, start: pxt.Int | None = None, stop: pxt.Int | None = None, repl: pxt.String | None = None ) -> pxt.String ``` Replace a positional slice with another value. **Parameters:** * **`start`** (`pxt.Int | None`): slice start * **`stop`** (`pxt.Int | None`): slice end * **`repl`** (`pxt.String | None`): replacement value ## udf  startswith() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf startswith(self: pxt.String, substr: pxt.String) -> pxt.Bool ``` Return `True` if string starts with `substr`, otherwise return `False`. Equivalent to [`str.startswith()`](https://docs.python.org/3/library/stdtypes.html#str.startswith). **Parameters:** * **`substr`** (`pxt.String`): string literal ## udf  strip() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf strip( self: pxt.String, chars: pxt.String | None = None ) -> pxt.String ``` Return a copy of string with leading and trailing characters removed. Equivalent to [`str.strip()`](https://docs.python.org/3/library/stdtypes.html#str.strip). **Parameters:** * **`chars`** (`pxt.String | None`): The set of characters to be removed. If omitted or `None`, whitespace characters are removed. ## udf  swapcase() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf swapcase(self: pxt.String) -> pxt.String ``` Return a copy of string with uppercase characters converted to lowercase and vice versa. Equivalent to [`str.swapcase()`](https://docs.python.org/3/library/stdtypes.html#str.swapcase). ## udf  title() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf title(self: pxt.String) -> pxt.String ``` Return a titlecased version of string, i.e. words start with uppercase characters, all remaining cased characters are lowercase. Equivalent to [`str.title()`](https://docs.python.org/3/library/stdtypes.html#str.title). ## udf  upper() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf upper(self: pxt.String) -> pxt.String ``` Return a copy of string converted to uppercase. Equivalent to [`str.upper()`](https://docs.python.org/3/library/stdtypes.html#str.upper). ## udf  wrap() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf wrap( self: pxt.String, width: pxt.Int, **kwargs ) -> pxt.Json[(String, ...)] ``` Wraps the single paragraph in string so every line is at most `width` characters long. Returns a list of output lines, without final newlines. Equivalent to [`textwrap.fill()`](https://docs.python.org/3/library/textwrap.html#textwrap.fill). **Parameters:** * **`width`** (`pxt.Int`): Maximum line width. * **`kwargs`** (`Any`): Additional keyword arguments to pass to `textwrap.fill()`. ## udf  zfill() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf zfill(self: pxt.String, width: pxt.Int) -> pxt.String ``` Pad a numeric string with ASCII `0` on the left to a total length of `width`. Equivalent to [`str.zfill()`](https://docs.python.org/3/library/stdtypes.html#str.zfill). **Parameters:** * **`width`** (`pxt.Int`): Minimum width of resulting string. # Table Source: https://docs.pixeltable.com/sdk/latest/table View Source on GitHub # class  pixeltable.Table A handle to a table, view, or snapshot. This class is the primary interface through which table operations (queries, insertions, updates, etc.) are performed in Pixeltable. Thread-safe. ## method  add\_btree\_index() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} add_btree_index( column: str | ColumnRef, *, idx_name: str | None = None, if_exists: Literal['error', 'ignore'] = 'error' ) -> None ``` Add a B-tree index to the table. Once the index is created, it will be automatically kept up-to-date as new rows are inserted into and existing rows are updated in the table. A B-tree index accelerates equality and range comparisons (used in `where` clauses) and `order_by` on the indexed column. Any non-boolean scalar column (`String`, `Int`, `Float`, `Timestamp`, `Date`) or non-computed media column (`Image`, `Video`, `Audio`, `Document`) is supported; the index on a media column is over the file URL. The column must be stored and, if a media column, must not be produced by a view iterator. Only tables created with `has_default_idxs=False` (the default) accept explicit B-tree indexes. A table created with `has_default_idxs=True` indexes its eligible columns automatically, and its B-tree indexes cannot be managed separately from their respective columns. **Parameters:** * **`column`** (`str | ColumnRef`): The name of, or reference to, the column to be indexed; must be an indexable scalar or media column (see above). * **`idx_name`** (`str | None`): An optional name for the index. If not specified, a unique name will be generated automatically. If specified, the name must be unique for this table and a valid pixeltable column name. * **`if_exists`** (`Literal['error', 'ignore']`, default: `'error'`): Directive for handling an existing B-tree index on the same column, or an existing index with the same name. Must be one of `'error'`, `'ignore'`. **Examples:** Add an index to the `name` column of the table `my_table`: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} tbl = pxt.get_table('my_table') tbl.add_btree_index('name') ``` The column may also be specified by reference, and the index may be given an explicit name: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} tbl.add_btree_index(tbl.name, idx_name='name_idx') ``` ## method  add\_column() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} add_column( *, if_exists: Literal['error', 'ignore', 'replace', 'replace_force'] = 'error', **kwargs: TypeForm | ColumnSpec ) -> UpdateStatus ``` Adds an ordinary (non-computed) column to the table. **Parameters:** * **`kwargs`** (`TypeForm | ColumnSpec`): Exactly one keyword argument of the form `col_name=type` or `col_name=col_spec_dict`, where `col_spec_dict` is a [`ColumnSpec`](./columnspec) dict. * **`if_exists`** (`Literal['error', 'ignore', 'replace', 'replace_force']`, default: `'error'`): Determines the behavior if the column already exists. Must be one of the following: * `'error'`: an exception will be raised. * `'ignore'`: do nothing and return. * `'replace'` or `'replace_force'`: drop the existing column and add the new column, if it has no dependents. **Returns:** * `UpdateStatus`: Information about the execution status of the operation. **Examples:** Add an int column: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} tbl.add_column(new_col=pxt.Int | None) ``` Add a column with column metadata using a dict: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} tbl.add_column( img_col={ 'type': pxt.Image | None, 'stored': True, 'media_validation': 'on_write', } ) ``` Alternatively, adding a column can also be expressed using `add_columns`: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} tbl.add_columns({'new_col': pxt.Int | None}) ``` As well as with column metadata: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} tbl.add_columns( { 'img_col': { 'type': pxt.Image | None, 'stored': True, 'media_validation': 'on_write', } } ) ``` ## method  add\_columns() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} add_columns( schema: Mapping[str, TypeForm | ColumnSpec], if_exists: Literal['error', 'ignore', 'replace', 'replace_force'] = 'error' ) -> UpdateStatus ``` Adds multiple columns to the table. The columns must be concrete (non-computed) columns; to add computed columns, use [`add_computed_column()`](./table#method-add_computed_column) instead. The format of the `schema` argument is a dict mapping column names to their types. A bare type such as `pxt.Int` defines a non-nullable column; use `pxt.Int | None` to allow nulls. Note that a column added to a table that already has rows must be nullable. **Parameters:** * **`schema`** (`Mapping[str, TypeForm | ColumnSpec]`): A dictionary mapping column names to a `type` or a [`ColumnSpec`](./columnspec) dict. * **`if_exists`** (`Literal['error', 'ignore', 'replace', 'replace_force']`, default: `'error'`): Determines the behavior if a column already exists. Must be one of the following: * `'error'`: an exception will be raised. * `'ignore'`: do nothing and return. * `'replace' or 'replace_force'`: drop the existing column and add the new column, if it has no dependents. Note that the `if_exists` parameter is applied to all columns in the schema. To apply different behaviors to different columns, please use [`add_column()`](./table#method-add_column) for each column. **Returns:** * `UpdateStatus`: Information about the execution status of the operation. **Examples:** Add multiple columns to the table `my_table`: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} tbl = pxt.get_table('my_table') schema = {'new_col_1': pxt.Int | None, 'new_col_2': pxt.String | None} tbl.add_columns(schema) ``` It is also possible to specify column metadata using a dict: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} tbl = pxt.get_table('my_table') schema = { 'new_col_1': { 'type': pxt.Image | None, 'stored': True, 'media_validation': 'on_write', }, 'new_col_2': pxt.String | None, } tbl.add_columns(schema) ``` ## method  add\_computed\_column() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} add_computed_column( *, stored: bool | None = None, destination: str | Path | None = None, custom_metadata: Any = None, comment: str = '', print_stats: bool = False, on_error: Literal['abort', 'ignore'] = 'abort', if_exists: Literal['error', 'ignore', 'replace'] = 'error', **kwargs: exprs.Expr ) -> UpdateStatus ``` Adds a computed column to the table. **Parameters:** * **`kwargs`** (`exprs.Expr`): Exactly one keyword argument of the form `col_name=expression`. * **`stored`** (`bool | None`): Whether the column is materialized and stored or computed on demand. * **`destination`** (`str | Path | None`): An object store reference for persisting computed files. * **`custom_metadata`** (`Any`): Optional user-defined metadata to associate with the column. Must be a valid JSON-serializable object. * **`comment`** (`str`, default: `''`): An optional comment; its meaning is user-defined. * **`print_stats`** (`bool`, default: `False`): If `True`, print execution metrics during evaluation. * **`on_error`** (`Literal['abort', 'ignore']`, default: `'abort'`): Determines the behavior if an error occurs while evaluating the column expression for at least one row. * `'abort'`: an exception will be raised and the column will not be added. * `'ignore'`: execution will continue and the column will be added. Any rows with errors will have a `None` value for the column, with information about the error stored in the corresponding `tbl.col_name.errormsg` and `tbl.col_name.errortype` fields. * **`if_exists`** (`Literal['error', 'ignore', 'replace']`, default: `'error'`): Determines the behavior if the column already exists. Must be one of the following: * `'error'`: an exception will be raised. * `'ignore'`: do nothing and return. * `'replace' or 'replace_force'`: drop the existing column and add the new column, iff it has no dependents. **Returns:** * `UpdateStatus`: Information about the execution status of the operation. **Examples:** For a table with an image column `frame`, add an image column `rotated` that rotates the image by 90 degrees: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} tbl.add_computed_column(rotated=tbl.frame.rotate(90)) ``` Do the same, but now the column is unstored: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} tbl.add_computed_column(rotated=tbl.frame.rotate(90), stored=False) ``` ## method  add\_embedding\_index() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} add_embedding_index( column: str | ColumnRef, *, idx_name: str | None = None, embedding: Function | None = None, string_embed: Function | None = None, image_embed: Function | None = None, audio_embed: Function | None = None, video_embed: Function | None = None, document_embed: Function | None = None, metric: Literal['cosine', 'ip', 'l2'] = 'cosine', precision: Literal['fp16', 'fp32'] = 'fp16', if_exists: Literal['error', 'ignore', 'replace', 'replace_force'] = 'error' ) -> None ``` Add an embedding index to the table. Once the index is created, it will be automatically kept up-to-date as new rows are inserted into the table. To add an embedding index, specify the column to be indexed and, if the column is not an `Array` column, an embedding UDF. `String`, `Image`, `Audio`, `Video`, `Document`, and `Array` columns are currently supported. Multimodal embeddings can be specified in one of two ways: via a single `embedding` argument with a multi-signature UDF (one signature per modality), or via separate modality-specific arguments (`string_embed`, `image_embed`, etc.). If both are provided, the modality-specific arguments will supersede the corresponding signatures of the `embedding` UDF. For `Array` columns, which are assumed to contain precomputed embeddings, an embedding function is optional; if provided, it will be used to convert query values into embeddings for similarity search. **Parameters:** * **`column`** (`str | ColumnRef`): The name of, or reference to, the column to be indexed; must be a `String`, `Image`, `Audio`, `Video`, `Document`, or `Array` column. * **`idx_name`** (`str | None`): An optional name for the index. If not specified, a unique name will be generated automatically. If specified, the name must be unique for this table and a valid pixeltable column name. When `idx_name` is omitted, duplicates are detected by the index definition (the embedding function(s), `metric`, and `precision`) on the column: re-adding an index with an identical definition is governed by `if_exists`. * **`embedding`** (`Function | None`): The UDF to use for the embedding. Must be a UDF that accepts a single argument of type `String` or `Image` (as appropriate for the column being indexed) and returns a fixed-size 1-dimensional array of floats. If omitted, then at least one of the modality-specific `*_embed` arguments must be supplied. * **`string_embed`** (`Function | None`): An optional UDF to use for the string embedding component of this index. * **`image_embed`** (`Function | None`): An optional UDF to use for the image embedding component of this index. * **`audio_embed`** (`Function | None`): An optional UDF to use for the audio embedding component of this index. * **`video_embed`** (`Function | None`): An optional UDF to use for the video embedding component of this index. * **`document_embed`** (`Function | None`): An optional UDF to use for the document embedding component of this index. * **`metric`** (`Literal['cosine', 'ip', 'l2']`, default: `'cosine'`): Distance metric to use for the index; one of `'cosine'`, `'ip'`, or `'l2'`. The default is `'cosine'`. * **`precision`** (`Literal['fp16', 'fp32']`, default: `'fp16'`): level of precision for the embeddings; one of `'fp16'` or `'fp32'`. * **`if_exists`** (`Literal['error', 'ignore', 'replace', 'replace_force']`, default: `'error'`): Directive for handling an existing index. The existing index is the one with the same name (if `idx_name` is given), or one with an identical definition on the same column (if `idx_name` is omitted). Must be one of the following: * `'error'`: raise an error if such an index already exists. * `'ignore'`: do nothing if such an index already exists. * `'replace'` or `'replace_force'`: replace the existing index with the new one. **Examples:** Add an index to the `img` column of the table `my_table`: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} from pixeltable.functions.huggingface import clip tbl = pxt.get_table('my_table') embedding_fn = clip.using(model_id='openai/clip-vit-base-patch32') tbl.add_embedding_index(tbl.img, embedding=embedding_fn) ``` Alternatively, the `img` column may be specified by name: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} tbl.add_embedding_index('img', embedding=embedding_fn) ``` Once the index is created, similarity lookups can be performed using the `similarity` pseudo-function: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} sim = tbl.img.similarity( image='/path/to/my-image.jpg' # can also be a URL or a PIL image ) tbl.select(tbl.img, sim).order_by(sim, asc=False).limit(5) ``` If the embedding UDF is a multimodal embedding (supporting more than one data type), then lookups may be performed using any of its supported modalities. In our example, CLIP supports both text and images, so we can also search for images using a text description: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} sim = tbl.img.similarity(string='a picture of a train') tbl.select(tbl.img, sim).order_by(sim, asc=False).limit(5) ``` Audio and video lookups would look like this: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} sim = tbl.img.similarity(audio='/path/to/audio.flac') sim = tbl.img.similarity(video='/path/to/video.mp4') ``` Multiple indexes can be defined on each column. Add a second index to the `img` column, using the inner product as the distance metric, and with a specific name: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} tbl.add_embedding_index( tbl.img, idx_name='ip_idx', embedding=embedding_fn, metric='ip' ) ``` Add an index using separately specified string and image embeddings: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} tbl.add_embedding_index( tbl.img, string_embed=string_embedding_fn, image_embed=image_embedding_fn, ) ``` ## method  alter\_computed\_column() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} alter_computed_column( *, recompute: bool = True, cascade: bool = True, **kwargs: exprs.Expr ) -> UpdateStatus ``` Change the value expression of a computed column. The new expression must have the same column type as the current one. It can reference any column of this table or of one of its ancestors, as long as there are no dependency cycles. Args: kwargs: Exactly one keyword argument of the form `col_name=expression`. recompute: If `True`, the stored values of the column are recomputed. If `False`, they remain those produced by the previous expression, and are stale until the column is recomputed with [`recompute_columns()`](./table#method-recompute_columns). cascade: If `True`, computed columns and views that transitively depend on this column are recomputed as well; if `False`, they retain their current values. Only applies if `recompute` is `True`. Returns: An [`UpdateStatus`](./updatestatus) describing the recompute; rows that raise during evaluation are recorded as cell errors and counted in `num_excs`. Raises: Error: If the column does not exist, is not a computed column, belongs to a base table, or if the new expression has a different column type or creates a dependency cycle. Examples: Change a computed column to use a different scaling factor: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} tbl = pxt.create_table('my_table', {'n': pxt.Int}) tbl.add_computed_column(scaled=tbl.n * 2) tbl.alter_computed_column(scaled=tbl.n * 3) ``` Update the definition without recomputing the existing rows: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} tbl.alter_computed_column(scaled=tbl.n * 4, recompute=False) ``` ## method  batch\_update() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} batch_update( rows: Iterable[dict[str, Any]], cascade: bool = True, if_not_exists: Literal['error', 'ignore', 'insert'] = 'error', return_rows: bool = False ) -> UpdateStatus ``` Update rows in this table. **Parameters:** * **`rows`** (`Iterable[dict[str, Any]]`): an Iterable of dictionaries containing values for the updated columns plus values for the primary key columns. * **`cascade`** (`bool`, default: `True`): if True, also update all computed columns that transitively depend on the updated columns. * **`if_not_exists`** (`Literal['error', 'ignore', 'insert']`, default: `'error'`): Specifies the behavior if a row to update does not exist: * `'error'`: Raise an error. * `'ignore'`: Skip the row silently. * `'insert'`: Insert the row. * **`return_rows`** (`bool`, default: `False`): If `True`, populate `UpdateStatus.rows` with one dict per affected row, mapping column names to their new stored values. Rows newly inserted via `if_not_exists='insert'` are included. If `False` (default), `UpdateStatus.rows` is `None`. **Examples:** Update the `name` and `age` columns for the rows with ids 1 and 2 (assuming `id` is the primary key). If either row does not exist, this raises an error: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} tbl.batch_update( [ {'id': 1, 'name': 'Alice', 'age': 30}, {'id': 2, 'name': 'Bob', 'age': 40}, ] ) ``` Update the `name` and `age` columns for the row with `id` 1 (assuming `id` is the primary key) and insert the row with new `id` 3 (assuming this key does not exist): ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} tbl.batch_update( [ {'id': 1, 'name': 'Alice', 'age': 30}, {'id': 3, 'name': 'Bob', 'age': 40}, ], if_not_exists='insert', ) ``` ## method  collect() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} collect() -> ResultSet ``` Return rows from this table. ## method  columns() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} columns() -> list[str] ``` Return the names of the columns in this table. ## method  compute() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} compute( source: Sequence[dict[str, Any]] | Sequence[pydantic.BaseModel], /, *, outputs: Sequence[str | ColumnRef] | None = None, on_error: Literal['abort', 'ignore'] = 'abort' ) -> RowBatch ``` Materialize the computed columns of this table for the given input rows and return the resulting rows without persisting them. If `outputs` is specified, will compute only those columns and the dependent columns that they use. An input row then needs to supply only the stored required columns used by `outputs`. If this table is a view, the input rows are applied to the view's insertable base table (i.e., the root of the view hierarchy) and the output rows are the resulting rows of the view, as if the input had been inserted into the base: * rows that don't satisfy the view's filter are dropped * an iterator view can produce multiple output rows per input row **Parameters:** * **`source`** (`Sequence[dict[str, Any]] | Sequence[pydantic.BaseModel]`): Rows to compute, as a sequence of dictionaries or Pydantic model instances. Rows contain values for the base table's columns (for a view) or this table's columns; each row must supply values for every required (non-nullable, non-computed) column used by `outputs`; otherwise the same rules as [`insert()`](./table#method-insert) apply. * **`outputs`** (`Sequence[str | ColumnRef] | None`): The columns to compute and return, as names or column references. Defaults to all columns of the table. * **`on_error`** (`Literal['abort', 'ignore']`, default: `'abort'`): Determines the behavior if an error occurs while evaluating a computed column or detecting an invalid media file (such as a corrupt image). * If `on_error='abort'`, an exception will be raised. * If `on_error='ignore'`, execution will continue and the (possibly partially) completed rows will be returned. Any cells with errors will have a `None` value for that cell, with information about the error recorded in the row's [`errors`](./row). A row whose view filter fails to evaluate is dropped. **Returns:** * `RowBatch`: A [`RowBatch`](./rowbatch) of output rows, in input row order (with an iterator's output rows in iteration order). Each [`Row`](./row) contains a value for every column in `outputs`. [`Row.errors`](./row) holds `{'errortype': ..., 'errormsg': ...}` for each cell that raised, keyed by column or index name (only with `on_error='ignore'`). **Examples:** Compute output rows for a table with int columns `a`, `b` and a computed column `c = a + b`: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} tbl = pxt.get_table('my_table') rows = tbl.compute([{'a': 1, 'b': 1}, {'a': 2, 'b': 2}]) # rows == [{'a': 1, 'b': 1, 'c': 2}, {'a': 2, 'b': 2, 'c': 4}] ``` Compute and return only `c`: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} rows = tbl.compute([{'a': 1, 'b': 1}], outputs=['c']) # rows == [{'c': 2}] ``` Same with Pydantic model inputs: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} class MyModel(pydantic.BaseModel): a: int b: int rows = tbl.compute([MyModel(a=1, b=2), MyModel(a=3, b=4)]) ``` Continue past per-row failures and inspect the per-cell error info: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} rows = tbl.compute( [{'a': 0, 'b': 1}, {'a': 2, 'b': 2}], on_error='ignore' ) # If `c` raised on row 0, rows[0]['c'] is None and rows[0].errors['c'] # contains {'errortype': ..., 'errormsg': ...}. ``` Compute view rows from base table input, for a view `my_view` defined over `my_table` with a filter `a > 0`: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} v = pxt.get_table('my_view') rows = v.compute([{'a': 0, 'b': 1}, {'a': 2, 'b': 2}]) # only the second input row satisfies the filter; rows contains its view row ``` ## method  count() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} count() -> int ``` Return the number of rows in this table. ## method  cursor() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} cursor() -> ResultCursor ``` Return a [`ResultCursor`](./resultcursor) that iterates over this table's rows. See [`ResultCursor`](./resultcursor) for usage examples and lifecycle details. ## method  delete() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} delete(where: exprs.Expr | None = None) -> UpdateStatus ``` Delete rows in this table. **Parameters:** * **`where`** (`'exprs.Expr' | None`): a predicate to filter rows to delete. **Examples:** Delete all rows in a table: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} tbl.delete() ``` Delete all rows in a table where column `a` is greater than 5: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} tbl.delete(tbl.a > 5) ``` ## method  describe() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} describe() -> None ``` Print the table schema. ## method  distinct() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} distinct() -> Query ``` Remove duplicate rows from table. ## method  drop\_column() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} drop_column( column: str | ColumnRef, if_not_exists: Literal['error', 'ignore'] = 'error' ) -> None ``` Drop a column from the table. **Parameters:** * **`column`** (`str | ColumnRef`): The name or reference of the column to drop. * **`if_not_exists`** (`Literal['error', 'ignore']`, default: `'error'`): Directive for handling a non-existent column. Must be one of the following: * `'error'`: raise an error if the column does not exist. * `'ignore'`: do nothing if the column does not exist. **Examples:** Drop the column `col` from the table `my_table` by column name: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} tbl = pxt.get_table('my_table') tbl.drop_column('col') ``` Drop the column `col` from the table `my_table` by column reference: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} tbl = pxt.get_table('my_table') tbl.drop_column(tbl.col) ``` Drop the column `col` from the table `my_table` if it exists, otherwise do nothing: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} tbl = pxt.get_table('my_table') tbl.drop_col(tbl.col, if_not_exists='ignore') ``` ## method  drop\_embedding\_index() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} drop_embedding_index( *, column: str | ColumnRef | None = None, idx_name: str | None = None, if_not_exists: Literal['error', 'ignore'] = 'error' ) -> None ``` Drop an embedding index from the table. Either a column name or an index name (but not both) must be specified. If a column name or reference is specified, it must be a column containing exactly one embedding index; otherwise the specific index name must be provided instead. **Parameters:** * **`column`** (`str | ColumnRef | None`): The name of, or reference to, the column from which to drop the index. The column must have only one embedding index. * **`idx_name`** (`str | None`): The name of the index to drop. * **`if_not_exists`** (`Literal['error', 'ignore']`, default: `'error'`): Directive for handling a non-existent index. Must be one of the following: * `'error'`: raise an error if the index does not exist. * `'ignore'`: do nothing if the index does not exist. Note that `if_not_exists` parameter is only applicable when an `idx_name` is specified and it does not exist, or when `column` is specified and it has no index. `if_not_exists` does not apply to non-exisitng column. **Examples:** Drop the embedding index on the `img` column of the table `my_table` by column name: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} tbl = pxt.get_table('my_table') tbl.drop_embedding_index(column='img') ``` Drop the embedding index on the `img` column of the table `my_table` by column reference: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} tbl = pxt.get_table('my_table') tbl.drop_embedding_index(column=tbl.img) ``` Drop the embedding index `idx1` of the table `my_table` by index name: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} tbl = pxt.get_table('my_table') tbl.drop_embedding_index(idx_name='idx1') ``` Drop the embedding index `idx1` of the table `my_table` by index name, if it exists, otherwise do nothing: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} tbl = pxt.get_table('my_table') tbl.drop_embedding_index(idx_name='idx1', if_not_exists='ignore') ``` ## method  drop\_index() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} drop_index( *, column: str | ColumnRef | None = None, idx_name: str | None = None, if_not_exists: Literal['error', 'ignore'] = 'error' ) -> None ``` Drop an index from the table. Either a column name or an index name (but not both) must be specified. If a column name or reference is specified, it must be a column containing exactly one index; otherwise the specific index name must be provided instead. The B-tree indexes of a table created with `has_default_idxs=True` cannot be dropped: they are managed automatically, together with the columns they index. **Parameters:** * **`column`** (`str | ColumnRef | None`): The name of, or reference to, the column from which to drop the index. The column must have only one embedding index. * **`idx_name`** (`str | None`): The name of the index to drop. * **`if_not_exists`** (`Literal['error', 'ignore']`, default: `'error'`): Directive for handling a non-existent index. Must be one of the following: * `'error'`: raise an error if the index does not exist. * `'ignore'`: do nothing if the index does not exist. Note that `if_not_exists` parameter is only applicable when an `idx_name` is specified and it does not exist, or when `column` is specified and it has no index. `if_not_exists` does not apply to non-exisitng column. **Examples:** Drop the index on the `img` column of the table `my_table` by column name: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} tbl = pxt.get_table('my_table') tbl.drop_index(column_name='img') ``` Drop the index on the `img` column of the table `my_table` by column reference: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} tbl = pxt.get_table('my_table') tbl.drop_index(tbl.img) ``` Drop the index `idx1` of the table `my_table` by index name: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} tbl = pxt.get_table('my_table') tbl.drop_index(idx_name='idx1') ``` Drop the index `idx1` of the table `my_table` by index name, if it exists, otherwise do nothing: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} tbl = pxt.get_table('my_table') tbl.drop_index(idx_name='idx1', if_not_exists='ignore') ``` ## method  get\_metadata() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} get_metadata() -> TableMetadata ``` Retrieves metadata associated with this table. **Returns:** * `'TableMetadata'`: A [TableMetadata](./tablemetadata) instance containing this table's metadata. ## method  get\_versions() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} get_versions(n: int | None = None) -> list[VersionMetadata] ``` Returns information about versions of this table, most recent first. `get_versions()` is intended for programmatic access to version metadata; for human-readable output, use [`history()`](./table#method-history) instead. **Parameters:** * **`n`** (`int | None`): if specified, will return at most `n` versions **Returns:** * `list[VersionMetadata]`: A list of [VersionMetadata](./versionmetadata) dictionaries, one per version retrieved, most recent first. **Examples:** Retrieve metadata about all versions of the table `tbl`: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} tbl.get_versions() ``` Retrieve metadata about the most recent 5 versions of the table `tbl`: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} tbl.get_versions(n=5) ``` ## method  group\_by() ```python Signatures theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Signature 1: group_by(grouping_tbl: Table, /) -> Query # Signature 2: group_by(*grouping_items: exprs.Expr) -> Query ``` Group the rows of this table based on the expression. See [`Query.group_by`](./query#method-group_by) for more details. ## method  head() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} head(n: int = 10) -> ResultSet ``` Return the first n rows inserted into this table. ## method  history() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} history(n: int | None = None) -> pd.DataFrame ``` Returns a human-readable report about versions of this table. `history()` is intended for human-readable output of version metadata; for programmatic access, use [`get_versions()`](./table#method-get_versions) instead. **Parameters:** * **`n`** (`int | None`): if specified, will return at most `n` versions **Returns:** * `pd.DataFrame`: A report with information about each version, one per row, most recent first. **Examples:** Report all versions of the table: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} tbl.history() ``` Report only the most recent 5 changes to the table: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} tbl.history(n=5) ``` ## method  insert() ```python Signatures theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Signature 1: insert( source: TableDataSource, /, *, source_format: Literal['csv', 'excel', 'parquet', 'json'] | None = None, schema_overrides: dict[str, ts.ColumnType] | None = None, on_error: Literal['abort', 'ignore'] = 'abort', print_stats: bool = False, return_rows: bool = False, **kwargs: Any ) -> UpdateStatus # Signature 2: insert( *, on_error: Literal['abort', 'ignore'] = 'abort', print_stats: bool = False, return_rows: bool = False, **kwargs: Any ) -> UpdateStatus ``` Inserts rows into this table. You can insert rows directly by providing a list of dictionaries as the `source`. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} tbl.insert([{'col1': 1, 'col2': 'egg'}, {'col1': 2, 'col2': 'fish'}]) ``` You can also insert data from any recognized data source by providing a file path or URL. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} tbl.insert('path/to/file.csv') tbl.insert('https://example.com/data.xlsx') tbl.insert('s3://my-bucket/data.parquet') ``` Pixeltable will attempt to infer the format of the source data, unless the optional `source_format` parameter is specified. Pixeltable will also attempt to infer the schema of the source data; you can override the inferred schema by providing a `schema_overrides` dictionary (which may include all columns or just a subset of columns). The `source` can also be another table or a [`Query`](./query): ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} tbl.insert( other_tbl.select( col1=other_tbl.other_col, col2=other_tbl.yet_another_col ) ) ``` For inserting just a single row, there is a convenient shorthand key/value syntax: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} tbl.insert(col1=1, col2='egg') ``` **Parameters:** * **`source`** (`TableDataSource | None`): A data source from which data can be imported. Can be any of the following: * A list of dictionaries * A list of Pydantic model instances * A file path or URI of a recognized data source * A Pandas `DataFrame` * Another Pixeltable table or a `Query` * A Hugging Face dataset * **`kwargs`** (`Any`): (if inserting a single row) Keyword-argument pairs representing column names and values. (if inserting multiple rows) Additional keyword arguments are passed to the data source. * **`source_format`** (`Literal['csv', 'excel', 'parquet', 'json'] | None`): A hint about the format of the source data. If not specified, Pixeltable will attempt to infer the format. * **`schema_overrides`** (`dict[str, ts.ColumnType] | None`): If specified, then columns in `schema_overrides` will be given the specified types. Any columns not included in `schema_overrides` will have their types inferred as usual. * **`on_error`** (`Literal['abort', 'ignore']`, default: `'abort'`): Determines the behavior if an error occurs while evaluating a computed column or detecting an invalid media file (such as a corrupt image) for one of the inserted rows. * If `on_error='abort'`, then an exception will be raised and the rows will not be inserted. * If `on_error='ignore'`, then execution will continue and the rows will be inserted. Any cells with errors will have a `None` value for that cell, with information about the error stored in the corresponding `tbl.col_name.errortype` and `tbl.col_name.errormsg` fields. * **`print_stats`** (`bool`, default: `False`): If `True`, print statistics about the cost of computed columns. * **`return_rows`** (`bool`, default: `False`): If `True`, populate `UpdateStatus.rows` with one dict per inserted row, mapping column names to their stored or computed values. If `False` (default), `UpdateStatus.rows` is `None`. **Returns:** * `UpdateStatus`: An [`UpdateStatus`](./updatestatus) object containing information about the update. **Examples:** Insert two rows into the table `my_table` with three int columns `a`, `b`, and `c`. Column `c` is nullable: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} tbl = pxt.get_table('my_table') tbl.insert([{'a': 1, 'b': 1, 'c': 1}, {'a': 2, 'b': 2}]) ``` Insert a single row using the alternative syntax: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} tbl.insert(a=3, b=3, c=3) ``` Insert rows from a CSV file: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} tbl.insert('path/to/file.csv') ``` Insert Pydantic model instances into a table with two `pxt.Int` columns `a` and `b`: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} class MyModel(pydantic.BaseModel): a: int b: int models = [MyModel(a=1, b=2), MyModel(a=3, b=4)] tbl.insert(models) ``` ## method  join() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} join( other: Table, *, on: exprs.Expr | None = None, how: JoinType.LiteralType = 'inner' ) -> Query ``` Join this table with another table. ## method  limit() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} limit(n: int, offset: int | None = None) -> Query ``` Select a limited number of rows from the Table, optionally skipping rows for pagination. **Parameters:** * **`n`** (`int`): Number of rows to select. * **`offset`** (`int | None`): Number of rows to skip before returning results. Default is None (no offset). **Returns:** * `'Query'`: A Query with the specified limited rows. **Examples:** Get the first 10 rows: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} t.limit(10).collect() ``` Get rows 21-30 (skip first 20, return next 10): ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} t.limit(10, offset=20).collect() ``` ## method  list\_views() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} list_views(*, recursive: bool = True) -> list[str] ``` Returns a list of all views and snapshots of this `Table`. **Parameters:** * **`recursive`** (`bool`, default: `True`): If `False`, returns only the immediate successor views of this `Table`. If `True`, returns all sub-views (including views of views, etc.) **Returns:** * `list[str]`: A list of view paths. ## method  order\_by() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} order_by(*items: exprs.Expr, asc: bool = True) -> Query ``` Order the rows of this table based on the expression. See [`Query.order_by`](./query#method-order_by) for more details. ## method  recompute\_columns() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} recompute_columns( *columns: str | ColumnRef, where: exprs.Expr | None = None, errors_only: bool = False, cascade: bool = True ) -> UpdateStatus ``` Recompute the values in one or more computed columns of this table. **Parameters:** * **`columns`** (`str | ColumnRef`): The names or references of the computed columns to recompute. * **`where`** (`'exprs.Expr' | None`): A predicate to filter rows to recompute. * **`errors_only`** (`bool`, default: `False`): If True, only run the recomputation for rows that have errors in the column (ie, the column's `errortype` property indicates that an error occurred). Only allowed for recomputing a single column. * **`cascade`** (`bool`, default: `True`): if True, also update all computed columns that transitively depend on the recomputed columns. **Examples:** Recompute computed columns `c1` and `c2` for all rows in this table, and everything that transitively depends on them: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} tbl.recompute_columns('c1', 'c2') ``` Recompute computed column `c1` for all rows in this table, but don't recompute other columns that depend on it: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} tbl.recompute_columns(tbl.c1, tbl.c2, cascade=False) ``` Recompute column `c1` and its dependents, but only for rows with `c2` == 0: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} tbl.recompute_columns('c1', where=tbl.c2 == 0) ``` Recompute column `c1` and its dependents, but only for rows that have errors in it: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} tbl.recompute_columns('c1', errors_only=True) ``` ## method  rename\_column() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} rename_column(old_name: str, new_name: str) -> None ``` Rename a column. **Parameters:** * **`old_name`** (`str`): The current name of the column. * **`new_name`** (`str`): The new name of the column. **Examples:** Rename the column `col1` to `col2` of the table `my_table`: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} tbl = pxt.get_table('my_table') tbl.rename_column('col1', 'col2') ``` ## method  revert() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} revert() -> None ``` Reverts the table to the previous version. .. warning:: This operation is irreversible. ## method  sample() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} sample( n: int | None = None, n_per_stratum: int | None = None, fraction: float | None = None, seed: int | None = None, stratify_by: Any = None ) -> Query ``` Choose a shuffled sample of rows See [`Query.sample`](./query#method-sample) for more details. ## method  select() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} select(*items: Any, **named_items: Any) -> Query ``` Select columns or expressions from this table. See [`Query.select`](./query#method-select) for more details. ## method  show() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} show(n: int = 20) -> ResultSet ``` Return the first n rows from this table. ## method  tail() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} tail(n: int = 10) -> ResultSet ``` Return the last n rows inserted into this table. ## method  update() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} update( value_spec: dict[str, Any], where: exprs.Expr | None = None, cascade: bool = True, return_rows: bool = False ) -> UpdateStatus ``` Update rows in this table. **Parameters:** * **`value_spec`** (`dict[str, Any]`): a dictionary mapping column names to literal values or Pixeltable expressions. * **`where`** (`'exprs.Expr' | None`): a predicate to filter rows to update. * **`cascade`** (`bool`, default: `True`): if True, also update all computed columns that transitively depend on the updated columns. * **`return_rows`** (`bool`, default: `False`): If `True`, populate `UpdateStatus.rows` with one dict per updated row, mapping column names to their new stored values. If `False` (default), `UpdateStatus.rows` is `None`. **Returns:** * `UpdateStatus`: An [`UpdateStatus`](./updatestatus) object containing information about the update. **Examples:** Set column `int_col` to 1 for all rows: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} tbl.update({'int_col': 1}) ``` Set column `int_col` to 1 for all rows where `int_col` is 0: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} tbl.update({'int_col': 1}, where=tbl.int_col == 0) ``` Set `int_col` to the value of `other_int_col` + 1: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} tbl.update({'int_col': tbl.other_int_col + 1}) ``` Increment `int_col` by 1 for all rows where `int_col` is 0: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} tbl.update({'int_col': tbl.int_col + 1}, where=tbl.int_col == 0) ``` ## method  where() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} where(pred: exprs.Expr) -> Query ``` Filter rows from this table based on the expression. See [`Query.where`](./query#method-where) for more details. # TableMetadata Source: https://docs.pixeltable.com/sdk/latest/tablemetadata View Source on GitHub # class  pixeltable.TableMetadata Metadata for a Pixeltable table. ## attr  base ``` base: str | None ``` If this table is a view or snapshot, the full path of its base table; otherwise `None`. ## attr  columns ``` columns: dict[str, ColumnMetadata] ``` Column metadata for all of the visible columns of the table. ## attr  comment ``` comment: str | None ``` User-provided table comment, if one exists. ## attr  custom\_metadata ``` custom_metadata: Any ``` User-defined JSON metadata for this table, if any. ## attr  has\_default\_idxs ``` has_default_idxs: bool ``` `True` if eligible columns of this table get a default B-tree index. ## attr  id ``` id: uuid.UUID ``` The stable UUID of the table. Useful for detecting drop-and-recreate across time. ## attr  indexes ``` indexes: dict[str, IndexMetadata] ``` Index metadata for all of the indexes of the table. ## attr  is\_data\_versioned ``` is_data_versioned: bool ``` `True` if this is a data-versioned table. ## attr  is\_snapshot ``` is_snapshot: bool ``` `True` if this table is a snapshot. ## attr  is\_view ``` is_view: bool ``` `True` if this table is a view. ## attr  iterator\_call ``` iterator_call: str | None ``` The iterator call for views that use an iterator; otherwise `None`. ## attr  kind ``` kind: Literal['table', 'view', 'snapshot'] ``` The kind of table: `'table'`, `'view'`, or `'snapshot'`. ## attr  media\_validation ``` media_validation: Literal['on_read', 'on_write'] ``` The media validation policy for this table. ## attr  name ``` name: str ``` The name of the table (ex: `'my_table'`). ## attr  path ``` path: str ``` The full path of the table (ex: `'my_dir.my_subdir.my_table'`). ## attr  primary\_key ``` primary_key: list[str] | None ``` List of primary key column names, or `None` if this table has no primary key. ## attr  schema\_version ``` schema_version: int ``` The current schema version of the table. ## attr  version ``` version: int ``` The current version of the table. ## attr  version\_created ``` version_created: datetime.datetime ``` The timestamp when this table version was created. ## attr  view\_filter ``` view_filter: str | None ``` A display string of a view's filter (`where`) predicate; `None` if this is not a view or has no filter. ## attr  view\_sample ``` view_sample: str | None ``` A display string of a view's `sample` clause; `None` if this is not a view or has no sample clause. # TableNode Source: https://docs.pixeltable.com/sdk/latest/tablenode View Source on GitHub # class  pixeltable.TableNode A table/view/snapshot entry in a [`TreeNode`](./treenode) tree. ## attr  base ``` base: str | None ``` Path of the immediate base table for views/snapshots; None for plain tables. ## attr  error\_count ``` error_count: int ``` Cumulative error count as recorded in table's history. ## attr  kind ``` kind: TableKind ``` ⚠️ **No documentation** ## attr  name ``` name: str ``` ⚠️ **No documentation** ## attr  path ``` path: str ``` ⚠️ **No documentation** ## attr  version ``` version: int | None ``` ⚠️ **No documentation** # telemetry Source: https://docs.pixeltable.com/sdk/latest/telemetry View Source on GitHub # module  pixeltable.telemetry Dependency-free instrumentation hooks. Core pixeltable reports spans (timed, nested units of work) and discrete events through this module; subscribers (e.g. the bridge in `opentelemetry-instrumentation-pixeltable`) translate them into a telemetry backend. With no subscribers registered every call is a near-free no-op, but hot loops should still guard with `if telemetry.active():` before building attribute dicts (or pass `attrs` as a callable). Spans should cover contiguous units of real computation (a UDF call, a DB insert batch, model loading) or serve as structural containers (operation and exec-node spans). A CPU work span must not contain a `yield`/`await`, which would let it cover unrelated interleaved work; awaiting an external call inside a span is fine (the request really is in flight). ## func  emit() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} emit(name: str, attrs: HookAttrs = None) -> None ``` Report a discrete event (e.g. a counter increment) to all subscribers. ## func  span() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} span( name: str, *, level: int = 20, parent: SpanHandle | None = None, set_current: bool = False, **attrs: Any ) -> Iterator[SpanHandle | None] ``` Context-manager sugar for a lexical-block span. Keyword attrs get a 'pxt.' prefix; None values are skipped. ## func  span\_end() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} span_end( handle: SpanHandle | None, *, exc: BaseException | None = None, attrs: HookAttrs = None ) -> None ``` End a span started with span\_start(); a no-op for None handles. ## func  span\_start() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} span_start( name: str, *, level: int = 20, parent: SpanHandle | None = None, set_current: bool = False, attrs: HookAttrs = None ) -> SpanHandle | None ``` Start a span and return its handle; None if no subscribers are registered or the span is suppressed. Spans below the level threshold are suppressed, as are non-set\_current spans with no ambient ancestor; descendants of a suppressed span parent to the ambient span. set\_current=True makes the span the ambient parent (a ContextVar, so it propagates into asyncio tasks) and requires that span\_end() runs on the same thread/context; use capture\_context()/restore\_context()/exit\_context() for explicit thread handoffs. # timestamp Source: https://docs.pixeltable.com/sdk/latest/timestamp View Source on GitHub # module  pixeltable.functions.timestamp Pixeltable UDFs for `TimestampType`. Usage example: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} import pixeltable as pxt t = pxt.get_table(...) t.select(t.timestamp_col.year, t.timestamp_col.weekday()).collect() ``` ## udf  astimezone() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf astimezone(self: pxt.Timestamp, tz: pxt.String) -> pxt.Timestamp ``` Convert the datetime to the given time zone. **Parameters:** * **`tz`** (`pxt.String`): The time zone to convert to. Must be a valid time zone name from the [IANA Time Zone Database](https://en.wikipedia.org/wiki/List_of_tz_database_time_zones). ## udf  day() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf day(self: pxt.Timestamp) -> pxt.Int ``` Between 1 and the number of days in the given month of the given year. Equivalent to [`datetime.day`](https://docs.python.org/3/library/datetime.html#datetime.datetime.day). ## udf  hour() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf hour(self: pxt.Timestamp) -> pxt.Int ``` Between 0 and 23 inclusive. Equivalent to [`datetime.hour`](https://docs.python.org/3/library/datetime.html#datetime.datetime.hour). ## udf  isocalendar() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf isocalendar(self: pxt.Timestamp) -> IsoCalendar ``` Return a dictionary with three entries: `'year'`, `'week'`, and `'weekday'`. Equivalent to [`datetime.isocalendar()`](https://docs.python.org/3/library/datetime.html#datetime.datetime.isocalendar). ## udf  isoformat() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf isoformat( self: pxt.Timestamp, sep: pxt.String = 'T', timespec: pxt.String = 'auto' ) -> pxt.String ``` Return a string representing the date and time in ISO 8601 format. Equivalent to [`datetime.isoformat()`](https://docs.python.org/3/library/datetime.html#datetime.datetime.isoformat). **Parameters:** * **`sep`** (`pxt.String`): Separator between date and time. * **`timespec`** (`pxt.String`): The number of additional terms in the output. See the [`datetime.isoformat()`](https://docs.python.org/3/library/datetime.html#datetime.datetime.isoformat) documentation for more details. ## udf  isoweekday() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf isoweekday(self: pxt.Timestamp) -> pxt.Int ``` Return the day of the week as an integer, where Monday is 1 and Sunday is 7. Equivalent to [`datetime.isoweekday()`](https://docs.python.org/3/library/datetime.html#datetime.datetime.isoweekday). ## udf  make\_timestamp() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf make_timestamp( year: pxt.Int, month: pxt.Int, day: pxt.Int, hour: pxt.Int = 0, minute: pxt.Int = 0, second: pxt.Int = 0, microsecond: pxt.Int = 0 ) -> pxt.Timestamp ``` Create a timestamp. Equivalent to [`datetime()`](https://docs.python.org/3/library/datetime.html#datetime.datetime). ## udf  microsecond() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf microsecond(self: pxt.Timestamp) -> pxt.Int ``` Between 0 and 999999 inclusive. Equivalent to [`datetime.microsecond`](https://docs.python.org/3/library/datetime.html#datetime.datetime.microsecond). ## udf  minute() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf minute(self: pxt.Timestamp) -> pxt.Int ``` Between 0 and 59 inclusive. Equivalent to [`datetime.minute`](https://docs.python.org/3/library/datetime.html#datetime.datetime.minute). ## udf  month() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf month(self: pxt.Timestamp) -> pxt.Int ``` Between 1 and 12 inclusive. Equivalent to [`datetime.month`](https://docs.python.org/3/library/datetime.html#datetime.datetime.month). ## udf  posix\_timestamp() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf posix_timestamp(self: pxt.Timestamp) -> pxt.Float ``` Return POSIX timestamp corresponding to the datetime instance. Equivalent to [`datetime.timestamp()`](https://docs.python.org/3/library/datetime.html#datetime.datetime.timestamp). ## udf  replace() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf replace( self: pxt.Timestamp, year: pxt.Int | None = None, month: pxt.Int | None = None, day: pxt.Int | None = None, hour: pxt.Int | None = None, minute: pxt.Int | None = None, second: pxt.Int | None = None, microsecond: pxt.Int | None = None ) -> pxt.Timestamp ``` Return a datetime with the same attributes, except for those attributes given new values by whichever keyword arguments are specified. Equivalent to [`datetime.replace()`](https://docs.python.org/3/library/datetime.html#datetime.datetime.replace). ## udf  second() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf second(self: pxt.Timestamp) -> pxt.Int ``` Between 0 and 59 inclusive. Equivalent to [`datetime.second`](https://docs.python.org/3/library/datetime.html#datetime.datetime.second). ## udf  strftime() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf strftime(self: pxt.Timestamp, format: pxt.String) -> pxt.String ``` Return a string representing the date and time, controlled by an explicit format string. Equivalent to [`datetime.strftime()`](https://docs.python.org/3/library/datetime.html#datetime.datetime.strftime). **Parameters:** * **`format`** (`pxt.String`): The format string to control the output. For a complete list of formatting directives, see [`strftime()` and `strptime()` Behavior](https://docs.python.org/3/library/datetime.html#strftime-and-strptime-behavior). ## udf  strip\_timezone() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf strip_timezone(self: pxt.Timestamp) -> pxt.Timestamp ``` Return a naive datetime with the same wall-clock as `self` and no `tzinfo`. Equivalent to [`datetime.replace(tzinfo=None)`](https://docs.python.org/3/library/datetime.html#datetime.datetime.replace). ## udf  toordinal() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf toordinal(self: pxt.Timestamp) -> pxt.Int ``` Return the proleptic Gregorian ordinal of the date, where January 1 of year 1 has ordinal 1. Equivalent to [`datetime.toordinal()`](https://docs.python.org/3/library/datetime.html#datetime.datetime.toordinal). ## udf  weekday() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf weekday(self: pxt.Timestamp) -> pxt.Int ``` Between 0 (Monday) and 6 (Sunday) inclusive. Equivalent to [`datetime.weekday()`](https://docs.python.org/3/library/datetime.html#datetime.datetime.weekday). ## udf  year() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf year(self: pxt.Timestamp) -> pxt.Int ``` Between [`MINYEAR`](https://docs.python.org/3/library/datetime.html#datetime.MINYEAR) and [`MAXYEAR`](https://docs.python.org/3/library/datetime.html#datetime.MAXYEAR) inclusive. Equivalent to [`datetime.year`](https://docs.python.org/3/library/datetime.html#datetime.datetime.year). # together Source: https://docs.pixeltable.com/sdk/latest/together View Source on GitHub # module  pixeltable.functions.together Pixeltable UDFs that wrap various endpoints from the Together AI API. In order to use them, you must first `pip install together` and configure your Together AI credentials, as described in the [Working with Together AI](https://docs.pixeltable.com/howto/providers/working-with-together) tutorial. ## udf  chat\_completions() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf chat_completions( messages: pxt.Json[(Json, ...)], *, model: pxt.String, model_kwargs: pxt.Json | None = None ) -> pxt.Json ``` Generate chat completions based on a given prompt using a specified model. Equivalent to the Together AI `chat/completions` API endpoint. For additional details, see: [https://docs.together.ai/reference/chat-completions-1](https://docs.together.ai/reference/chat-completions-1) Request throttling: Applies the rate limit set in the config (section `together.rate_limits`, key `chat`). If no rate limit is configured, uses a default of 600 RPM. **Requirements:** * `pip install together` **Parameters:** * **`messages`** (`pxt.Json[(Json`): A list of messages comprising the conversation so far. * **`model`** (`Any`): The name of the model to query. * **`model_kwargs`** (`Any`): Additional keyword arguments for the Together `chat/completions` API. For details on the available parameters, see: [https://docs.together.ai/reference/chat-completions-1](https://docs.together.ai/reference/chat-completions-1) **Returns:** * `pxt.Json`: A dictionary containing the response and other metadata. **Examples:** Add a computed column that applies the model `openai/gpt-oss-20b` to an existing Pixeltable column `tbl.prompt` of the table `tbl`: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} messages = [{'role': 'user', 'content': tbl.prompt}] tbl.add_computed_column( response=chat_completions(messages, model='openai/gpt-oss-20b') ) ``` ## udf  completions() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf completions( prompt: pxt.String, *, model: pxt.String, model_kwargs: pxt.Json | None = None ) -> pxt.Json ``` Generate completions based on a given prompt using a specified model. Equivalent to the Together AI `completions` API endpoint. For additional details, see: [https://docs.together.ai/reference/completions-1](https://docs.together.ai/reference/completions-1) Request throttling: Applies the rate limit set in the config (section `together.rate_limits`, key `chat`). If no rate limit is configured, uses a default of 600 RPM. **Requirements:** * `pip install together` **Parameters:** * **`prompt`** (`pxt.String`): A string providing context for the model to complete. * **`model`** (`pxt.String`): The name of the model to query. * **`model_kwargs`** (`pxt.Json | None`): Additional keyword arguments for the Together `completions` API. For details on the available parameters, see: [https://docs.together.ai/reference/completions-1](https://docs.together.ai/reference/completions-1) **Returns:** * `pxt.Json`: A dictionary containing the response and other metadata. **Examples:** Add a computed column that applies the model `Qwen/Qwen3.5-9B` to an existing Pixeltable column `tbl.prompt` of the table `tbl`: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} tbl.add_computed_column( response=completions(tbl.prompt, model='Qwen/Qwen3.5-9B') ) ``` ## udf  embeddings() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf embeddings( input: pxt.String, *, model: pxt.String ) -> pxt.Array[(None,), float32] ``` Query an embedding model for a given string of text. Equivalent to the Together AI `embeddings` API endpoint. For additional details, see: [https://docs.together.ai/reference/embeddings-2](https://docs.together.ai/reference/embeddings-2) Request throttling: Applies the rate limit set in the config (section `together.rate_limits`, key `embeddings`). If no rate limit is configured, uses a default of 600 RPM. **Requirements:** * `pip install together` **Parameters:** * **`input`** (`pxt.String`): A string providing the text for the model to embed. * **`model`** (`pxt.String`): The name of the embedding model to use. **Returns:** * `pxt.Array[(None,), float32]`: An array representing the application of the given embedding to `input`. **Examples:** Add a computed column that applies the model `intfloat/multilingual-e5-large-instruct` to an existing Pixeltable column `tbl.text` of the table `tbl`: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} tbl.add_computed_column( response=embeddings( tbl.text, model='intfloat/multilingual-e5-large-instruct' ) ) ``` ## udf  image\_generations() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf image_generations( prompt: pxt.String, *, model: pxt.String, model_kwargs: pxt.Json | None = None ) -> pxt.Image ``` Generate images based on a given prompt using a specified model. Equivalent to the Together AI `images/generations` API endpoint. For additional details, see: [https://docs.together.ai/reference/post\_images-generations](https://docs.together.ai/reference/post_images-generations) Request throttling: Applies the rate limit set in the config (section `together.rate_limits`, key `images`). If no rate limit is configured, uses a default of 600 RPM. **Requirements:** * `pip install together` **Parameters:** * **`prompt`** (`pxt.String`): A description of the desired images. * **`model`** (`pxt.String`): The model to use for image generation. * **`model_kwargs`** (`pxt.Json | None`): Additional keyword args for the Together `images/generations` API. For details on the available parameters, see: [https://docs.together.ai/reference/post\_images-generations](https://docs.together.ai/reference/post_images-generations) **Returns:** * `pxt.Image`: The generated image. **Examples:** Add a computed column that applies the model `Rundiffusion/Juggernaut-Lightning-Flux` to an existing Pixeltable column `tbl.prompt` of the table `tbl`: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} tbl.add_computed_column( response=image_generations( tbl.prompt, model='Rundiffusion/Juggernaut-Lightning-Flux' ) ) ``` # TreeNode Source: https://docs.pixeltable.com/sdk/latest/treenode ## ⚠️ Error Loading Class Failed to load class `TreeNode`: ``` TreeNode is not a class ``` # twelvelabs Source: https://docs.pixeltable.com/sdk/latest/twelvelabs View Source on GitHub # module  pixeltable.functions.twelvelabs Pixeltable UDFs that wrap various endpoints from the TwelveLabs API. In order to use them, you must first `pip install twelvelabs` and configure your TwelveLabs credentials, as described in the [Working with TwelveLabs](https://docs.pixeltable.com/howto/providers/working-with-twelvelabs) tutorial. ## udf  embed() ```python Signatures theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Signature 1: @pxt.udf embed( text: pxt.String, image: pxt.Image | None, *, model_name: pxt.String ) -> pxt.Array[float32] | None # Signature 2: @pxt.udf embed( image: pxt.Image, *, model_name: pxt.String ) -> pxt.Array[float32] | None # Signature 3: @pxt.udf embed( audio: pxt.Audio, *, model_name: pxt.String, start_sec: pxt.Float | None, end_sec: pxt.Float | None, embedding_option: pxt.Json[(String, ...)] | None ) -> pxt.Array[float32] | None # Signature 4: @pxt.udf embed( video: pxt.Video, *, model_name: pxt.String, start_sec: pxt.Float | None, end_sec: pxt.Float | None, embedding_option: pxt.Json[(String, ...)] | None ) -> pxt.Array[float32] | None ``` Creates an embedding vector for the given text, audio, image, or video input. Each UDF signature corresponds to one of the four supported input types. If text is specified, it is possible to specify an image as well, corresponding to the `text_image` embedding type in the TwelveLabs API. This is (currently) the only way to include more than one input type at a time. Equivalent to the TwelveLabs Embed API: [https://docs.twelvelabs.io/v1.3/docs/guides/create-embeddings](https://docs.twelvelabs.io/v1.3/docs/guides/create-embeddings) Request throttling: Applies the rate limit set in the config (section `twelvelabs`, key `rate_limit`). If no rate limit is configured, uses a default of 600 RPM. **Requirements:** * `pip install twelvelabs` **Parameters:** * **`model_name`** (`String`): The name of the model to use. Check [the TwelveLabs documentation](https://docs.twelvelabs.io/v1.3/sdk-reference/python/create-embeddings-v-1/create-text-image-and-audio-embeddings) for available models. * **`text`** (`String`): The text to embed. * **`image`** (`Image | None`, default: `Literal(None)`): If specified, the embedding will be created from both the text and the image. **Returns:** * `pxt.Array[float32] | None`: The embedding. **Examples:** Add a computed column `embed` for an embedding of a string column `input`: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} tbl.add_computed_column( embed=embed(model_name='marengo3.0', text=tbl.input) ) ``` # UpdateStatus Source: https://docs.pixeltable.com/sdk/latest/updatestatus View Source on GitHub # class  pixeltable.UpdateStatus Information about changes to table data or table schema ## attr  num\_computed\_values ``` num_computed_values: int ``` Total number of computed values affected (including cascaded changes). ## attr  num\_excs ``` num_excs: int ``` Total number of exceptions encountered (including cascaded changes). ## attr  num\_rows ``` num_rows: int ``` Total number of rows affected (including cascaded changes). ## attr  pxt\_rows\_updated ``` pxt_rows_updated: int ``` Returns the number of Pixeltable rows that were updated as a result of the operation. # URI Source: https://docs.pixeltable.com/sdk/latest/uri View Source on GitHub # class  pixeltable.URI A storage destination: an object-store URI, or a local filesystem path. Validates at construction. # uuid Source: https://docs.pixeltable.com/sdk/latest/uuid View Source on GitHub # module  pixeltable.functions.uuid Pixeltable UDFs for `UUID`. ## udf  to\_string() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf to_string(u: pxt.UUID) -> pxt.String ``` Convert a UUID to its string representation. **Parameters:** * **`u`** (`pxt.UUID`): The UUID to convert. **Returns:** * `pxt.String`: The string representation of the UUID, in the form `xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx`. **Examples:** Convert the UUID column `id` in an existing table `tbl` to a string: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} tbl.add_computed_column(id_string=to_string(tbl.id)) ``` ## udf  uuid4() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf uuid4() -> pxt.UUID ``` Generate a random UUID (version 4). Equivalent to [`uuid.uuid4()`](https://docs.python.org/3/library/uuid.html#uuid.uuid4). ## udf  uuid7() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf uuid7() -> pxt.UUID ``` Generate a time-based UUID. Equivalent to [`uuid.uuid7()`](https://docs.python.org/3/library/uuid.html#uuid.uuid7). # VersionMetadata Source: https://docs.pixeltable.com/sdk/latest/versionmetadata View Source on GitHub # class  pixeltable.VersionMetadata Metadata for a specific version of a Pixeltable table. ## attr  change\_type ``` change_type: Literal['data', 'schema'] ``` The type of table transformation that this version represents (`'data'` or `'schema'`). ## attr  created\_at ``` created_at: datetime.datetime ``` The timestamp when this version was created. ## attr  deletes ``` deletes: int ``` The number of rows deleted in this version. ## attr  errors ``` errors: int ``` The number of errors encountered during this version. ## attr  inserts ``` inserts: int ``` The number of rows inserted in this version. ## attr  schema\_change ``` schema_change: str | None ``` A description of the schema change that occurred in this version, if any. ## attr  updates ``` updates: int ``` The number of rows updated in this version. ## attr  user ``` user: str | None ``` The user who created this version, if defined. ## attr  version ``` version: int ``` The version number. # video Source: https://docs.pixeltable.com/sdk/latest/video View Source on GitHub # module  pixeltable.functions.video Pixeltable UDFs for VideoType. ## iterator  frame\_iterator() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.iterator frame_iterator( video: pxt.Video, *, fps: pxt.Float | None = None, num_frames: pxt.Int | None = None, keyframes_only: pxt.Bool = False ) ``` Iterator over frames of a video. At most one of `fps`, `num_frames` or `keyframes_only` may be specified. If `fps` is specified, then frames will be extracted at the specified rate (frames per second). If `num_frames` is specified, then the exact number of frames will be extracted. If neither is specified, then all frames will be extracted. **Outputs:** One row per extracted frame, with the following columns: * `frame` (`pxt.Image`): The extracted video frame * `frame_attrs` (`pxt.Json`): A dictionary containing the following attributes (for more information, see `pyav`'s documentation on [VideoFrame](https://pyav.org/docs/develop/api/video.html#module-av.video.frame) and [Frame](https://pyav.org/docs/develop/api/frame.html)): * `index` (`int`): The index of the frame in the video stream * `pts` (`int | None`): The presentation timestamp of the frame * `dts` (`int | None`): The decoding timestamp of the frame * `time` (`float | None`): The timestamp of the frame in seconds * `is_corrupt` (`bool`): `True` if the frame is corrupt * `key_frame` (`bool`): `True` if the frame is a keyframe * `pict_type` (`int`): The picture type of the frame * `interlaced_frame` (`bool`): `True` if the frame is interlaced **Parameters:** * **`fps`** (`pxt.Float | None`): Number of frames to extract per second of video. This may be a fractional value, such as 0.5. If omitted, or if greater than the native framerate of the video, then the framerate of the video will be used (all frames will be extracted). * **`num_frames`** (`pxt.Int | None`): Exact number of frames to extract. The frames will be spaced as evenly as possible. If `num_frames` is greater than the number of frames in the video, all frames will be extracted. * **`keyframes_only`** (`pxt.Bool`): If True, only extract keyframes. **Examples:** All these examples assume an existing table `tbl` with a column `video` of type `pxt.Video`. Create a view that extracts all frames from all videos: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} pxt.create_view('all_frames', tbl, iterator=frame_iterator(tbl.video)) ``` Create a view that extracts only keyframes from all videos: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} pxt.create_view( 'keyframes', tbl, iterator=frame_iterator(tbl.video, keyframes_only=True), ) ``` Create a view that extracts frames from all videos at a rate of 1 frame per second: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} pxt.create_view( 'one_fps_frames', tbl, iterator=frame_iterator(tbl.video, fps=1.0) ) ``` Create a view that extracts exactly 10 frames from each video: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} pxt.create_view( 'ten_frames', tbl, iterator=frame_iterator(tbl.video, num_frames=10) ) ``` ## iterator  video\_splitter() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.iterator video_splitter( video: pxt.Video, *, duration: pxt.Float | None = None, overlap: pxt.Float | None = None, min_segment_duration: pxt.Float | None = None, segment_times: pxt.Json[(Float, ...)] | None = None, mode: pxt.String = 'accurate', video_encoder: pxt.String | None = None, video_encoder_args: pxt.Json | None = None ) ``` Iterator over segments of a video file, which is split into segments. The segments are specified either via a fixed duration or a list of split points. **Parameters:** * **`duration`** (`pxt.Float | None`): Video segment duration in seconds * **`overlap`** (`pxt.Float | None`): Overlap between consecutive segments in seconds. Only available for `mode='fast'`. * **`min_segment_duration`** (`pxt.Float | None`): Drop the last segment if it is smaller than min\_segment\_duration. * **`segment_times`** (`pxt.Json[(Float`): List of timestamps (in seconds) in video where segments should be split. Note that these are not segment durations. If all segment times are less than the duration of the video, produces exactly `len(segment_times) + 1` segments. An argument of `[]` will produce a single segment containing the entire video. * **`mode`** (`Any`): Segmentation mode: * `'fast'`: Quick segmentation using stream copy (splits only at keyframes, approximate durations) * `'accurate'`: Precise segmentation with re-encoding (exact durations, slower) * **`video_encoder`** (`Any`): Video encoder to use. If not specified, uses the default encoder for the current platform. Only available for `mode='accurate'`. * **`video_encoder_args`** (`Any`): Additional arguments to pass to the video encoder. Only available for `mode='accurate'`. **Examples:** All these examples assume an existing table `tbl` with a column `video` of type `pxt.Video`. Create a view that splits each video into 10-second segments: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} pxt.create_view( 'ten_second_segments', tbl, iterator=video_splitter(tbl.video, duration=10.0), ) ``` Create a view that splits each video into segments at specified fixed times: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} split_times = [5.0, 15.0, 30.0] pxt.create_view( 'custom_segments', tbl, iterator=video_splitter(tbl.video, segment_times=split_times), ) ``` Create a view that splits each video into segments at times specified by a column `split_times` of type `pxt.Json`, containing a list of timestamps in seconds: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} pxt.create_view( 'custom_segments', tbl, iterator=video_splitter(tbl.video, segment_times=tbl.split_times), ) ``` ## uda  concat\_videos\_agg() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.uda concat_videos_agg(*args, **kwargs) -> pxt.Video | None ``` Aggregate function that merges videos into a single video. **Requirements:** * `ffmpeg` needs to be installed and in PATH * All videos must have the same resolution **Parameters:** * **`video_encoder`** (`pxt.String | None`): Video encoder to use. If not specified, uses the default encoder for the current platform. * **`video_encoder_args`** (`pxt.Json | None`): Additional arguments to pass to the video encoder. **Returns:** * `pxt.Video | None`: A new video containing all input videos concatenated in order, or None if all inputs are None. **Examples:** Concatenate all videos in a table, ordered by timestamp: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} tbl.select(concat_videos_agg(tbl.timestamp, tbl.video)).collect() ``` ## uda  make\_video() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.uda make_video(*args, **kwargs) -> pxt.Video ``` Aggregate function that creates a video from a sequence of images, using the default video encoder and yuv420p pixel format. Call as `make_video(order_key, frame, *, fps=25)`. `order_key` is a frame index or timestamp. `order_by=` is invalid. **Parameters:** * **`fps`** (`pxt.Int`): Frames per second for the output video. **Returns:** * `pxt.Video`: The video obtained by combining the input frames at the specified `fps`. **Examples:** Combine the frames in `tbl.frame`, ordered by `tbl.pos`: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} tbl.select(make_video(tbl.pos, tbl.frame, fps=30)).collect() ``` Combine a sequence of rotated frames: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} tbl.select(make_video(tbl.pos, tbl.frame.rotate(45), fps=30)).collect() ``` For a more extensive example, see the [Object Detection in Videos](https://docs.pixeltable.com/howto/use-cases/object-detection-in-videos) cookbook. ## udf  clip() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf clip( video: pxt.Video, *, start_time: pxt.Float, end_time: pxt.Float | None = None, duration: pxt.Float | None = None, mode: pxt.String = 'accurate', video_encoder: pxt.String | None = None, video_encoder_args: pxt.Json | None = None ) -> pxt.Video | None ``` Extract a clip from a video, specified by `start_time` and either `end_time` or `duration` (in seconds). If `start_time` is beyond the end of the video, returns None. Can only specify one of `end_time` and `duration`. If both `end_time` and `duration` are None, the clip goes to the end of the video. **Requirements:** * `ffmpeg` needs to be installed and in PATH **Parameters:** * **`video`** (`pxt.Video`): Input video file * **`start_time`** (`pxt.Float`): Start time in seconds * **`end_time`** (`pxt.Float | None`): End time in seconds * **`duration`** (`pxt.Float | None`): Duration of the clip in seconds * **`mode`** (`pxt.String`): Clip mode: * `'fast'`: avoids re-encoding but starts the clip at the nearest keyframes and as a result, the clip duration will be slightly longer than requested * `'accurate'`: extracts a frame-accurate clip, but requires re-encoding * **`video_encoder`** (`pxt.String | None`): Video encoder to use. If not specified, uses the default encoder for the current platform. Only available for `mode='accurate'`. * **`video_encoder_args`** (`pxt.Json | None`): Additional arguments to pass to the video encoder. Only available for `mode='accurate'`. **Returns:** * `pxt.Video | None`: New video containing only the specified time range or None if start\_time is beyond the end of the video. ## udf  concat\_videos() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf concat_videos( videos: pxt.Json[(Video, ...)], *, video_encoder: pxt.String | None = None, video_encoder_args: pxt.Json | None = None ) -> pxt.Video | None ``` Merge multiple videos into a single video. **Requirements:** * `ffmpeg` needs to be installed and in PATH **Parameters:** * **`videos`** (`pxt.Json[(Video`): List of videos to merge. * **`video_encoder`** (`Any`): Video encoder to use. If not specified, uses the default encoder for the current platform. * **`video_encoder_args`** (`Any`): Additional arguments to pass to the video encoder. **Returns:** * `pxt.Video | None`: A new video containing the merged videos, or None if the input list is empty. ## udf  crop() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf crop( video: pxt.Video, bbox: pxt.Json[(Int, ...)], *, bbox_format: pxt.String = 'xywh', video_encoder: pxt.String | None = None, video_encoder_args: pxt.Json | None = None ) -> pxt.Video ``` Crop a rectangular region from a video using ffmpeg's crop filter. **Requirements:** * `ffmpeg` needs to be installed and in PATH **Parameters:** * **`video`** (`pxt.Video`): Input video. * **`bbox`** (`pxt.Json[(Int`): Crop region as a list of 4 integers. * **`bbox_format`** (`Any`): Format of the `bbox` coordinates: * `'xyxy'`: `[x1, y1, x2, y2]` where (x1, y1) is top-left and (x2, y2) is bottom-right * `'xywh'`: `[x, y, width, height]` where (x, y) is top-left corner * `'cxcywh'`: `[cx, cy, width, height]` where (cx, cy) is the center * **`video_encoder`** (`Any`): Video encoder to use. If not specified, uses the default encoder. * **`video_encoder_args`** (`Any`): Additional arguments to pass to the video encoder. **Returns:** * `pxt.Video`: Video containing the cropped region. **Examples:** Crop using default xywh format: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} tbl.select(tbl.video.crop([100, 50, 320, 240])).collect() ``` Crop using xyxy format (common in object detection): ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} tbl.select( tbl.video.crop([100, 50, 420, 290], bbox_format='xyxy') ).collect() ``` Crop using center format: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} tbl.select( tbl.video.crop([260, 170, 320, 240], bbox_format='cxcywh') ).collect() ``` Use with yolox object detection output: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} tbl.add_computed_column( cropped=tbl.video.crop(tbl.detections.bboxes[0], bbox_format='xyxy') ) ``` ## udf  extract\_audio() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf extract_audio( video_path: pxt.Video, stream_idx: pxt.Int = 0, format: pxt.String = 'wav', codec: pxt.String | None = None ) -> pxt.Audio | None ``` Extract an audio stream from a video. **Parameters:** * **`stream_idx`** (`pxt.Int`): Index of the audio stream to extract. * **`format`** (`pxt.String`): The target audio format. (`'wav'`, `'mp3'`, `'flac'`). * **`codec`** (`pxt.String | None`): The codec to use for the audio stream. If not provided, a default codec will be used. **Returns:** * `pxt.Audio | None`: The extracted audio, or None if the video has no audio stream at `stream_idx`. **Examples:** Add a computed column to a table `tbl` that extracts audio from an existing column `video_col`: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} tbl.add_computed_column( extracted_audio=tbl.video_col.extract_audio(format='flac') ) ``` ## udf  extract\_frame() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf extract_frame( video: pxt.Video, *, timestamp: pxt.Float ) -> pxt.Image | None ``` Extract a single frame from a video at a specific timestamp. **Parameters:** * **`video`** (`pxt.Video`): The video from which to extract the frame. * **`timestamp`** (`pxt.Float`): Extract frame at this timestamp (in seconds). **Returns:** * `pxt.Image | None`: The extracted frame as a PIL Image, or None if the timestamp is beyond the video duration. **Examples:** Extract the first frame from each video in the `video` column of the table `tbl`: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} tbl.select(tbl.video.extract_frame(0.0)).collect() ``` Extract a frame close to the end of each video in the `video` column of the table `tbl`: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} tbl.select( tbl.video.extract_frame( tbl.video.get_metadata().streams[0].duration_seconds - 0.1 ) ).collect() ``` ## udf  fade\_in() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf fade_in( video: pxt.Video, *, duration: pxt.Float = 1.0, color: pxt.String = 'black', video_encoder: pxt.String | None = None, video_encoder_args: pxt.Json | None = None ) -> pxt.Video ``` Apply a fade-in effect from a solid color at the start of a video using ffmpeg's fade filter. The video transitions from a solid `color` to the full video content over `duration` seconds. **Requirements:** * `ffmpeg` needs to be installed and in PATH **Parameters:** * **`video`** (`pxt.Video`): Input video. * **`duration`** (`pxt.Float`): Duration of the fade-in effect in seconds. * **`color`** (`pxt.String`): Color to fade from (e.g., `'black'`, `'white'`, `'#FF0000'`). * **`video_encoder`** (`pxt.String | None`): Video encoder to use. If not specified, uses the default encoder for the current platform. * **`video_encoder_args`** (`pxt.Json | None`): Additional arguments to pass to the video encoder. **Returns:** * `pxt.Video`: A new video with the fade-in effect applied. **Examples:** Apply a 1-second fade from black (default): ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} tbl.select(tbl.video.fade_in()).collect() ``` Apply a 2-second fade from white: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} tbl.select(tbl.video.fade_in(duration=2.0, color='white')).collect() ``` ## udf  fade\_out() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf fade_out( video: pxt.Video, *, duration: pxt.Float = 1.0, color: pxt.String = 'black', video_encoder: pxt.String | None = None, video_encoder_args: pxt.Json | None = None ) -> pxt.Video ``` Apply a fade-out effect to a solid color at the end of a video using ffmpeg's fade filter. The video transitions from the full video content to a solid `color` over the final `duration` seconds. **Requirements:** * `ffmpeg` needs to be installed and in PATH **Parameters:** * **`video`** (`pxt.Video`): Input video. * **`duration`** (`pxt.Float`): Duration of the fade-out effect in seconds. * **`color`** (`pxt.String`): Color to fade to (e.g., `'black'`, `'white'`, `'#FF0000'`). * **`video_encoder`** (`pxt.String | None`): Video encoder to use. If not specified, uses the default encoder for the current platform. * **`video_encoder_args`** (`pxt.Json | None`): Additional arguments to pass to the video encoder. **Returns:** * `pxt.Video`: A new video with the fade-out effect applied. **Examples:** Apply a 1-second fade to black (default): ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} tbl.select(tbl.video.fade_out()).collect() ``` Apply a 3-second fade to white: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} tbl.select(tbl.video.fade_out(duration=3.0, color='white')).collect() ``` ## udf  get\_duration() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf get_duration(video: pxt.Video) -> pxt.Float | None ``` Get video duration in seconds. **Parameters:** * **`video`** (`pxt.Video`): The video for which to get the duration. **Returns:** * `pxt.Float | None`: The duration in seconds, or None if the duration cannot be determined. ## udf  get\_metadata() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf get_metadata(video: pxt.Video) -> ContainerMetadata ``` Gets various metadata associated with a video file and returns it as a [`ContainerMetadata`](./containermetadata) dictionary. **Parameters:** * **`video`** (`pxt.Video`): The video for which to get metadata. **Returns:** * `ContainerMetadata`: A [`ContainerMetadata`](./containermetadata) with typical structure: ```json theme={"theme":{"light":"light-plus","dark":"dark-plus"}} { 'bit_exact': False, 'bit_rate': 967260, 'size': 2234371, 'metadata': { 'encoder': 'Lavf60.16.100', 'major_brand': 'isom', 'minor_version': '512', 'compatible_brands': 'isomiso2avc1mp41', }, 'streams': [ { 'type': 'video', 'width': 640, 'height': 360, 'frames': 462, 'time_base': 1.0 / 12800, 'duration': 236544, 'duration_seconds': 236544.0 / 12800, 'average_rate': 25.0, 'base_rate': 25.0, 'guessed_rate': 25.0, 'metadata': { 'language': 'und', 'handler_name': 'L-SMASH Video Handler', 'vendor_id': '[0][0][0][0]', 'encoder': 'Lavc60.31.102 libx264', }, 'codec_context': {'name': 'h264', 'codec_tag': 'avc1', 'profile': 'High', 'pix_fmt': 'yuv420p'}, } ], } ``` **Examples:** Extract metadata for files in the `video_col` column of the table `tbl`: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} tbl.select(tbl.video_col.get_metadata()).collect() ``` ## udf  grayscale() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf grayscale( video: pxt.Video, *, video_encoder: pxt.String | None = None, video_encoder_args: pxt.Json | None = None ) -> pxt.Video ``` Convert a video to grayscale **Requirements:** * `ffmpeg` needs to be installed and in PATH **Parameters:** * **`video`** (`pxt.Video`): Input video. * **`video_encoder`** (`pxt.String | None`): Video encoder to use. If not specified, uses the default encoder for the current platform. * **`video_encoder_args`** (`pxt.Json | None`): Additional arguments to pass to the video encoder. **Returns:** * `pxt.Video`: A grayscale version of the video. **Examples:** ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} tbl.select(tbl.video.grayscale()).collect() ``` ## udf  mirror\_x() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf mirror_x( video: pxt.Video, *, video_encoder: pxt.String | None = None, video_encoder_args: pxt.Json | None = None ) -> pxt.Video ``` Flip a video horizontally using ffmpeg's hflip filter. **Requirements:** * `ffmpeg` needs to be installed and in PATH **Parameters:** * **`video`** (`pxt.Video`): Input video. * **`video_encoder`** (`pxt.String | None`): Video encoder to use. If not specified, uses the default encoder for the current platform. * **`video_encoder_args`** (`pxt.Json | None`): Additional arguments to pass to the video encoder. **Returns:** * `pxt.Video`: A horizontally flipped video. **Examples:** ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} tbl.select(tbl.video.mirror_x()).collect() ``` ## udf  mirror\_y() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf mirror_y( video: pxt.Video, *, video_encoder: pxt.String | None = None, video_encoder_args: pxt.Json | None = None ) -> pxt.Video ``` Flip a video vertically using ffmpeg's vflip filter. **Requirements:** * `ffmpeg` needs to be installed and in PATH **Parameters:** * **`video`** (`pxt.Video`): Input video. * **`video_encoder`** (`pxt.String | None`): Video encoder to use. If not specified, uses the default encoder for the current platform. * **`video_encoder_args`** (`pxt.Json | None`): Additional arguments to pass to the video encoder. **Returns:** * `pxt.Video`: A vertically flipped video. **Examples:** ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} tbl.select(tbl.video.mirror_y()).collect() ``` ## udf  mix\_audio() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf mix_audio( video: pxt.Video, audio: pxt.Audio, *, audio_volume: pxt.Float = 1.0, original_volume: pxt.Float = 1.0, audio_start_time: pxt.Float = 0.0, mix_duration: pxt.String = 'longest', normalize: pxt.Bool = False, dropout_transition: pxt.Float = 2.0, align_to_video: pxt.String = 'trim', video_encoder: pxt.String | None = None, video_encoder_args: pxt.Json | None = None ) -> pxt.Video ``` Mix an audio track into a video's existing audio, blending both tracks together. Volume levels for each track can be controlled independently. **Requirements:** * `ffmpeg` needs to be installed and in PATH **Parameters:** * **`video`** (`pxt.Video`): Input video (must have an existing audio stream). * **`audio`** (`pxt.Audio`): Audio track to mix in. * **`audio_volume`** (`pxt.Float`): Volume multiplier for the added audio track. 1.0 is original volume. * **`original_volume`** (`pxt.Float`): Volume multiplier for the video's existing audio track. 1.0 is original volume. * **`audio_start_time`** (`pxt.Float`): Time in seconds at which the added audio begins playing in the output. * **`mix_duration`** (`pxt.String`): Controls which input determines the length of the mixed audio stream. * `"longest"`: the mix runs until the longer of the two audio inputs ends. Use this when the added audio (e.g. a music bed) is longer than the video's original audio. * `"first"`: the mix ends when the video's original audio track ends. Useful when the original audio and video streams are the same length. * `"shortest"`: the mix ends when the shorter input ends, truncating whichever track is longer. * **`normalize`** (`pxt.Bool`): If `True`, ffmpeg scales the mixed output to prevent clipping by dividing each track's contribution by the number of inputs. Defaults to `False` so that `audio_volume` and `original_volume` mean what they say; flip on if you are not setting volumes explicitly and want automatic clip protection. * **`dropout_transition`** (`pxt.Float`): Duration in seconds over which a track's contribution fades to zero after it ends, preventing audible clicks at hard boundaries. Defaults to 2.0 seconds, matching ffmpeg's own default. Set to 0.0 to disable. Relevant whenever one input ends before the mixed output ends, regardless of which `mix_duration` mode is selected. * **`align_to_video`** (`pxt.String`): Post-mix adjustment to align the output audio stream with the video stream duration. Applied after `amix`, so it is independent of `mix_duration`. * `"trim"`: if the mixed audio is longer than the video stream, truncate it to match. Pairs naturally with `mix_duration="longest"` for music-bed workflows. * `"none"`: no adjustment; output audio duration is whatever `amix` produces. * `"pad"`: if the mixed audio is shorter than the video stream, extend it with silence. Also trims to the video duration if the mix runs long. * **`video_encoder`** (`pxt.String | None`): Video encoder to use. If not specified, uses the default encoder. * **`video_encoder_args`** (`pxt.Json | None`): Additional arguments to pass to the video encoder. **Returns:** * `pxt.Video`: A new video with both audio tracks mixed together. **Examples:** Add background music at 30% volume. With the defaults, this lays the music as a bed under the video: `mix_duration="longest"` keeps the music playing past the end of the original audio, `align_to_video="trim"` caps the result at the video stream duration, and `normalize=False` means the `audio_volume=0.3` setting is taken at face value rather than halved by `amix`: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} tbl.select(tbl.video.mix_audio(tbl.music, audio_volume=0.3)).collect() ``` Mix audio starting at second 5, with the original audio reduced: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} tbl.select( tbl.video.mix_audio( tbl.music, audio_volume=0.5, original_volume=0.7, audio_start_time=5.0, ) ).collect() ``` Pad a short ambient track with silence so the audio stream matches the full video length: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} tbl.select( tbl.video.mix_audio( tbl.ambient, audio_volume=0.6, align_to_video='pad' ) ).collect() ``` ## udf  overlay\_image() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf overlay_image( video: pxt.Video, image: pxt.Image, *, horizontal_align: pxt.String = 'center', horizontal_margin: pxt.Int = 0, vertical_align: pxt.String = 'center', vertical_margin: pxt.Int = 0, scale: pxt.Float | None = None, opacity: pxt.Float = 1.0, start_time: pxt.Float | None = None, end_time: pxt.Float | None = None, video_encoder: pxt.String | None = None, video_encoder_args: pxt.Json | None = None ) -> pxt.Video ``` Overlay an image on a video with customizable positioning, scaling, opacity, and timing. **Requirements:** * `ffmpeg` needs to be installed and in PATH **Parameters:** * **`video`** (`pxt.Video`): Input video to overlay the image on. * **`image`** (`pxt.Image`): Image to overlay. * **`horizontal_align`** (`pxt.String`): Horizontal alignment of the overlay (`'left'`, `'center'`, `'right'`). * **`horizontal_margin`** (`pxt.Int`): Horizontal margin in pixels from the alignment edge. * **`vertical_align`** (`pxt.String`): Vertical alignment of the overlay (`'top'`, `'center'`, `'bottom'`). * **`vertical_margin`** (`pxt.Int`): Vertical margin in pixels from the alignment edge. * **`scale`** (`pxt.Float | None`): Scale factor for the overlay image relative to the video height. For example, 0.1 scales the image to 10% of the video height while preserving aspect ratio. If None, uses the original size. * **`opacity`** (`pxt.Float`): Overlay opacity from 0.0 (transparent) to 1.0 (opaque). * **`start_time`** (`pxt.Float | None`): Time in seconds when the overlay appears. If None, the overlay is visible from the start. * **`end_time`** (`pxt.Float | None`): Time in seconds when the overlay disappears. If None, the overlay is visible until the end. * **`video_encoder`** (`pxt.String | None`): Video encoder to use. If not specified, uses the default encoder. * **`video_encoder_args`** (`pxt.Json | None`): Additional arguments to pass to the video encoder. **Returns:** * `pxt.Video`: A new video with the image overlay applied. **Examples:** Add a logo to the top-right corner: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} tbl.select( tbl.video.overlay_image( tbl.logo_img, horizontal_align='right', vertical_align='top' ) ).collect() ``` Add a watermark at 50% opacity, scaled to 10% of video height: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} tbl.select( tbl.video.overlay_image(tbl.watermark_img, scale=0.1, opacity=0.5) ).collect() ``` Show an image only between seconds 2 and 8: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} tbl.select( tbl.video.overlay_image( tbl.img, start_time=2.0, end_time=8.0, horizontal_align='right' ) ).collect() ``` ## udf  overlay\_text() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf overlay_text( video: pxt.Video, text: pxt.String, *, font: pxt.String | None = None, font_size: pxt.Int = 24, line_spacing: pxt.Int = 0, color: pxt.String = 'white', opacity: pxt.Float = 1.0, horizontal_align: pxt.String = 'center', horizontal_margin: pxt.Int = 0, vertical_align: pxt.String = 'center', vertical_margin: pxt.Int = 0, box: pxt.Bool = False, box_color: pxt.String = 'black', box_opacity: pxt.Float = 1.0, box_border: pxt.Json[(Int, ...)] | None = None, start_time: pxt.Float | None = None, end_time: pxt.Float | None = None, video_encoder: pxt.String | None = None, video_encoder_args: pxt.Json | None = None ) -> pxt.Video ``` Overlay text on a video with customizable positioning and styling. **Requirements:** * `ffmpeg` needs to be installed and in PATH Args: video: Input video to overlay text on. text: The text string to overlay on the video. font: Font family or path to font file. If None, uses the system default. font\_size: Size of the text in points. line\_spacing: Pixels of vertical space added between lines of multi-line text (text containing ` `). Defaults to 0. Negative values pull lines closer together for tighter packing. Ignored for single-line text. color: Text color (e.g., `'white'`, `'red'`, `'#FF0000'`). opacity: Text opacity from 0.0 (transparent) to 1.0 (opaque). horizontal\_align: Horizontal text alignment (`'left'`, `'center'`, `'right'`). horizontal\_margin: Horizontal margin in pixels from the alignment edge. vertical\_align: Vertical text alignment (`'top'`, `'center'`, `'bottom'`). vertical\_margin: Vertical margin in pixels from the alignment edge. box: Whether to draw a background box behind the text. box\_color: Background box color as a string. box\_opacity: Background box opacity from 0.0 to 1.0. box\_border: Padding around text in the box in pixels. * `[10]`: 10 pixels on all sides * `[10, 20]`: 10 pixels on top/bottom, 20 on left/right * `[10, 20, 30]`: 10 pixels on top, 20 on left/right, 30 on bottom * `[10, 20, 30, 40]`: 10 pixels on top, 20 on right, 30 on bottom, 40 on left start\_time: Time in seconds when the text appears. If None, the text is visible from the start. end\_time: Time in seconds when the text disappears. If None, the text is visible until the end. video\_encoder: Video encoder to use. If not specified, uses the default encoder. video\_encoder\_args: Additional arguments to pass to the video encoder. Returns: A new video with the text overlay applied. Examples: Add a simple text overlay to videos in a table: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} tbl.select(tbl.video.overlay_text('Sample Text')).collect() ``` Add a YouTube-style caption: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} tbl.select( tbl.video.overlay_text( 'Caption text', font_size=32, color='white', opacity=1.0, box=True, box_color='black', box_opacity=0.8, box_border=[6, 14], horizontal_margin=10, vertical_align='bottom', vertical_margin=70, ) ).collect() ``` Add text with a semi-transparent background box: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} tbl.select( tbl.video.overlay_text( 'Important Message', font_size=32, color='yellow', box=True, box_color='black', box_opacity=0.6, box_border=[20, 10], ) ).collect() ``` ## udf  pan() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf pan(*args, **kwargs) -> pxt.Video | None ``` A parameterized expression from which an executable Expr is created with a function call. ## udf  resize() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf resize( video: pxt.Video, *, width: pxt.Int | None = None, height: pxt.Int | None = None, scale: pxt.Float | None = None, video_encoder: pxt.String | None = None, video_encoder_args: pxt.Json | None = None ) -> pxt.Video ``` Resize a video using ffmpeg's scale filter. **Requirements:** * `ffmpeg` needs to be installed and in PATH **Parameters:** * **`video`** (`pxt.Video`): Input video. * **`width`** (`pxt.Int | None`): Width of the output video. Maintains the existing aspect ratio if no `height` is provided. * **`height`** (`pxt.Int | None`): Height of the output video. Maintains the existing aspect ratio if no `width` is provided. * **`scale`** (`pxt.Float | None`): Scale factor. Mutually exclusive with `width` and `height`. * **`video_encoder`** (`pxt.String | None`): Video encoder to use. If not specified, uses the default encoder for the current platform. * **`video_encoder_args`** (`pxt.Json | None`): Additional arguments to pass to the video encoder. **Returns:** * `pxt.Video`: The resized video. **Examples:** Resize to a specific width, preserving aspect ratio: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} tbl.select(tbl.video.resize(width=640)).collect() ``` Resize to exact dimensions: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} tbl.select(tbl.video.resize(width=1280, height=720)).collect() ``` Scale down by half: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} tbl.select(tbl.video.resize(scale=0.5)).collect() ``` ## udf  reverse() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf reverse( video: pxt.Video, audio: pxt.String = 'drop', *, video_encoder: pxt.String | None = None, video_encoder_args: pxt.Json | None = None ) -> pxt.Video ``` Reverse a video using ffmpeg's reverse filter. **Requirements:** * `ffmpeg` needs to be installed and in PATH **Parameters:** * **`video`** (`pxt.Video`): Input video. * **`audio`** (`pxt.String`): Specifies what to do with audio streams * `'drop'`: drop the audio streams * `'reverse'`: also reverse the audio streams * `'keep'`: keep the audio streams * **`video_encoder`** (`pxt.String | None`): Video encoder to use. If not specified, uses the default encoder for the current platform. * **`video_encoder_args`** (`pxt.Json | None`): Additional arguments to pass to the video encoder. **Returns:** * `pxt.Video`: The reversed video. **Examples:** Reverse a video, dropping audio: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} tbl.select(tbl.video.reverse()).collect() ``` Reverse a video along with its audio: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} tbl.select(tbl.video.reverse(audio='reverse')).collect() ``` ## udf  rotate() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf rotate( video: pxt.Video, *, angle: pxt.Float, unit: pxt.String = 'deg', expand: pxt.Bool = False, video_encoder: pxt.String | None = None, video_encoder_args: pxt.Json | None = None ) -> pxt.Video ``` Rotate a video by a fixed angle using ffmpeg's rotate filter. **Requirements:** * `ffmpeg` needs to be installed and in PATH **Parameters:** * **`video`** (`pxt.Video`): Input video. * **`angle`** (`pxt.Float`): Rotation angle. Positive values rotate counter-clockwise. * **`unit`** (`pxt.String`): Unit of the angle: `'deg'` for degrees or `'rad'` for radians. * **`expand`** (`pxt.Bool`): If True, the output frame is enlarged to contain the entire rotated frame (no cropping). If False (default), the output frame keeps the original dimensions, cropping corners. * **`video_encoder`** (`pxt.String | None`): Video encoder to use. If not specified, uses the default encoder for the current platform. * **`video_encoder_args`** (`pxt.Json | None`): Additional arguments to pass to the video encoder. **Returns:** * `pxt.Video`: A new video rotated by the specified angle. **Examples:** Rotate 90 degrees counter-clockwise: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} tbl.select(tbl.video.rotate(angle=90)).collect() ``` Rotate 45 degrees with frame expansion to avoid cropping: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} tbl.select(tbl.video.rotate(angle=45, expand=True)).collect() ``` Rotate by pi/2 radians: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} tbl.select(tbl.video.rotate(angle=1.5708, unit='rad')).collect() ``` ## udf  scene\_detect\_adaptive() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf scene_detect_adaptive( video: pxt.Video, *, fps: pxt.Float | None = None, adaptive_threshold: pxt.Float = 3.0, min_scene_len: pxt.Int = 15, window_width: pxt.Int = 2, min_content_val: pxt.Float = 15.0, delta_hue: pxt.Float = 1.0, delta_sat: pxt.Float = 1.0, delta_lum: pxt.Float = 1.0, delta_edges: pxt.Float = 0.0, luma_only: pxt.Bool = False, kernel_size: pxt.Int | None = None ) -> pxt.Json[(Json[{'start_time': Float, 'start_pts': Int, 'duration': Float}], ...)] ``` Detect scene cuts in a video using PySceneDetect's [AdaptiveDetector](https://www.scenedetect.com/docs/latest/api/detectors.html#scenedetect.detectors.adaptive_detector.AdaptiveDetector). **Requirements:** * `pip install scenedetect` **Parameters:** * **`video`** (`pxt.Video`): The video to analyze for scene cuts. * **`fps`** (`pxt.Float | None`): Number of frames to extract per second for analysis. If None or 0, analyzes all frames. Lower values process faster but may miss exact scene cuts. * **`adaptive_threshold`** (`pxt.Float`): Threshold that the score ratio must exceed to trigger a new scene cut. Lower values will detect more scenes (more sensitive), higher values will detect fewer scenes. * **`min_scene_len`** (`pxt.Int`): Once a cut is detected, this many frames must pass before a new one can be added to the scene list. * **`window_width`** (`pxt.Int`): Size of window (number of frames) before and after each frame to average together in order to detect deviations from the mean. Must be at least 1. * **`min_content_val`** (`pxt.Float`): Minimum threshold (float) that the content\_val must exceed in order to register as a new scene. This is calculated the same way that `scene_detect_content()` calculates frame score based on weights/luma\_only/kernel\_size. * **`delta_hue`** (`pxt.Float`): Weight for hue component changes. Higher values make hue changes more important. * **`delta_sat`** (`pxt.Float`): Weight for saturation component changes. Higher values make saturation changes more important. * **`delta_lum`** (`pxt.Float`): Weight for luminance component changes. Higher values make brightness changes more important. * **`delta_edges`** (`pxt.Float`): Weight for edge detection changes. Higher values make edge changes more important. Edge detection can help detect cuts in scenes with similar colors but different content. * **`luma_only`** (`pxt.Bool`): If True, only analyzes changes in the luminance (brightness) channel of the video, ignoring color information. This can be faster and may work better for grayscale content. * **`kernel_size`** (`pxt.Int | None`): Size of kernel to use for post edge detection filtering. If None, automatically set based on video resolution. **Returns:** * `pxt.Json[(Json[{'start_time': Float, 'start_pts': Int, 'duration': Float}], ...)]`: A list of dictionaries, one for each detected scene, with the following keys: * `start_time` (float): The start time of the scene in seconds. * `start_pts` (int): The pts of the start of the scene. * `duration` (float): The duration of the scene in seconds. The list is ordered chronologically. Returns the full duration of the video if no scenes are detected. **Examples:** Detect scene cuts with default parameters: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} tbl.select(tbl.video.scene_detect_adaptive()).collect() ``` Detect more scenes by lowering the threshold: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} tbl.select( tbl.video.scene_detect_adaptive(adaptive_threshold=1.5) ).collect() ``` Use luminance-only detection with a longer minimum scene length: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} tbl.select( tbl.video.scene_detect_adaptive(luma_only=True, min_scene_len=30) ).collect() ``` Add scene cuts as a computed column: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} tbl.add_computed_column( scene_cuts=tbl.video.scene_detect_adaptive(adaptive_threshold=2.0) ) ``` Analyze at a lower frame rate for faster processing: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} tbl.select(tbl.video.scene_detect_adaptive(fps=2.0)).collect() ``` ## udf  scene\_detect\_content() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf scene_detect_content( video: pxt.Video, *, fps: pxt.Float | None = None, threshold: pxt.Float = 27.0, min_scene_len: pxt.Int = 15, delta_hue: pxt.Float = 1.0, delta_sat: pxt.Float = 1.0, delta_lum: pxt.Float = 1.0, delta_edges: pxt.Float = 0.0, luma_only: pxt.Bool = False, kernel_size: pxt.Int | None = None, filter_mode: pxt.String = 'merge' ) -> pxt.Json[(Json[{'start_time': Float, 'start_pts': Int, 'duration': Float}], ...)] ``` Detect scene cuts in a video using PySceneDetect's [ContentDetector](https://www.scenedetect.com/docs/latest/api/detectors.html#scenedetect.detectors.content_detector.ContentDetector). **Requirements:** * `pip install scenedetect` **Parameters:** * **`video`** (`pxt.Video`): The video to analyze for scene cuts. * **`fps`** (`pxt.Float | None`): Number of frames to extract per second for analysis. If None, analyzes all frames. Lower values process faster but may miss exact scene cuts. * **`threshold`** (`pxt.Float`): Threshold that the weighted sum of component changes must exceed to trigger a scene cut. Lower values detect more scenes (more sensitive), higher values detect fewer scenes. * **`min_scene_len`** (`pxt.Int`): Once a cut is detected, this many frames must pass before a new one can be added to the scene list. * **`delta_hue`** (`pxt.Float`): Weight for hue component changes. Higher values make hue changes more important. * **`delta_sat`** (`pxt.Float`): Weight for saturation component changes. Higher values make saturation changes more important. * **`delta_lum`** (`pxt.Float`): Weight for luminance component changes. Higher values make brightness changes more important. * **`delta_edges`** (`pxt.Float`): Weight for edge detection changes. Higher values make edge changes more important. Edge detection can help detect cuts in scenes with similar colors but different content. * **`luma_only`** (`pxt.Bool`): If True, only analyzes changes in the luminance (brightness) channel, ignoring color information. This can be faster and may work better for grayscale content. * **`kernel_size`** (`pxt.Int | None`): Size of kernel for expanding detected edges. Must be odd integer greater than or equal to 3. If None, automatically set using video resolution. * **`filter_mode`** (`pxt.String`): How to handle fast cuts/flashes. 'merge' combines quick cuts, 'suppress' filters them out. **Returns:** * `pxt.Json[(Json[{'start_time': Float, 'start_pts': Int, 'duration': Float}], ...)]`: A list of dictionaries, one for each detected scene, with the following keys: * `start_time` (float): The start time of the scene in seconds. * `start_pts` (int): The pts of the start of the scene. * `duration` (float): The duration of the scene in seconds. The list is ordered chronologically. Returns the full duration of the video if no scenes are detected. **Examples:** Detect scene cuts with default parameters: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} tbl.select(tbl.video.scene_detect_content()).collect() ``` Detect more scenes by lowering the threshold: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} tbl.select(tbl.video.scene_detect_content(threshold=15.0)).collect() ``` Use luminance-only detection: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} tbl.select(tbl.video.scene_detect_content(luma_only=True)).collect() ``` Emphasize edge detection for scenes with similar colors: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} tbl.select( tbl.video.scene_detect_content( delta_edges=1.0, delta_hue=0.5, delta_sat=0.5 ) ).collect() ``` Add scene cuts as a computed column: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} tbl.add_computed_column( scene_cuts=tbl.video.scene_detect_content(threshold=20.0) ) ``` ## udf  scene\_detect\_hash() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf scene_detect_hash( video: pxt.Video, *, fps: pxt.Float | None = None, threshold: pxt.Float = 0.395, size: pxt.Int = 16, lowpass: pxt.Int = 2, min_scene_len: pxt.Int = 15 ) -> pxt.Json[(Json[{'start_time': Float, 'start_pts': Int, 'duration': Float}], ...)] ``` Detect scene cuts in a video using PySceneDetect's [HashDetector](https://www.scenedetect.com/docs/latest/api/detectors.html#scenedetect.detectors.hash_detector.HashDetector). HashDetector uses perceptual hashing for very fast scene detection. It computes a hash of each frame at reduced resolution and compares hash distances. **Requirements:** * `pip install scenedetect` **Parameters:** * **`video`** (`pxt.Video`): The video to analyze for scene cuts. * **`fps`** (`pxt.Float | None`): Number of frames to extract per second for analysis. If None, analyzes all frames. Lower values process faster but may miss exact scene cuts. * **`threshold`** (`pxt.Float`): Value from 0.0 and 1.0 representing the relative hamming distance between the perceptual hashes of adjacent frames. A distance of 0 means the image is the same, and 1 means no correlation. Smaller threshold values thus require more correlation, making the detector more sensitive. The Hamming distance is divided by size x size before comparing to threshold for normalization. Lower values detect more scenes (more sensitive), higher values detect fewer scenes. * **`size`** (`pxt.Int`): Size of square of low frequency data to use for the DCT. Larger values are more precise but slower. Common values are 8, 16, or 32. * **`lowpass`** (`pxt.Int`): How much high frequency information to filter from the DCT. A value of 2 means keep lower 1/2 of the frequency data, 4 means only keep 1/4, etc. Larger values make the detector less sensitive to high-frequency details and noise. * **`min_scene_len`** (`pxt.Int`): Once a cut is detected, this many frames must pass before a new one can be added to the scene list. **Returns:** * `pxt.Json[(Json[{'start_time': Float, 'start_pts': Int, 'duration': Float}], ...)]`: A list of dictionaries, one for each detected scene, with the following keys: * `start_time` (float): The start time of the scene in seconds. * `start_pts` (int): The pts of the start of the scene. * `duration` (float): The duration of the scene in seconds. The list is ordered chronologically. Returns the full duration of the video if no scenes are detected. **Examples:** Detect scene cuts with default parameters: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} tbl.select(tbl.video.scene_detect_hash()).collect() ``` Detect more scenes by lowering the threshold: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} tbl.select(tbl.video.scene_detect_hash(threshold=0.3)).collect() ``` Use larger hash size for more precision: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} tbl.select(tbl.video.scene_detect_hash(size=32)).collect() ``` Use for fast processing with lower frame rate: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} tbl.select(tbl.video.scene_detect_hash(fps=1.0, threshold=0.4)).collect() ``` Add scene cuts as a computed column: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} tbl.add_computed_column(scene_cuts=tbl.video.scene_detect_hash()) ``` ## udf  scene\_detect\_histogram() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf scene_detect_histogram( video: pxt.Video, *, fps: pxt.Float | None = None, threshold: pxt.Float = 0.05, bins: pxt.Int = 256, min_scene_len: pxt.Int = 15 ) -> pxt.Json[(Json[{'start_time': Float, 'start_pts': Int, 'duration': Float}], ...)] ``` Detect scene cuts in a video using PySceneDetect's [HistogramDetector](https://www.scenedetect.com/docs/latest/api/detectors.html#scenedetect.detectors.histogram_detector.HistogramDetector). HistogramDetector compares frame histograms on the Y (luminance) channel after YUV conversion. It detects scenes based on relative histogram differences and is more robust to gradual lighting changes than content-based detection. **Requirements:** * `pip install scenedetect` **Parameters:** * **`video`** (`pxt.Video`): The video to analyze for scene cuts. * **`fps`** (`pxt.Float | None`): Number of frames to extract per second for analysis. If None or 0, analyzes all frames. Lower values process faster but may miss exact scene cuts. * **`threshold`** (`pxt.Float`): Maximum relative difference between 0.0 and 1.0 that the histograms can differ. Histograms are calculated on the Y channel after converting the frame to YUV, and normalized based on the number of bins. Higher differences imply greater change in content, so larger threshold values are less sensitive to cuts. Lower values detect more scenes (more sensitive), higher values detect fewer scenes. * **`bins`** (`pxt.Int`): Number of bins to use for histogram calculation (typically 16-256). More bins provide finer granularity but may be more sensitive to noise. * **`min_scene_len`** (`pxt.Int`): Once a cut is detected, this many frames must pass before a new one can be added to the scene list. **Returns:** * `pxt.Json[(Json[{'start_time': Float, 'start_pts': Int, 'duration': Float}], ...)]`: A list of dictionaries, one for each detected scene, with the following keys: * `start_time` (float): The start time of the scene in seconds. * `start_pts` (int): The pts of the start of the scene. * `duration` (float): The duration of the scene in seconds. The list is ordered chronologically. Returns the full duration of the video if no scenes are detected. **Examples:** Detect scene cuts with default parameters: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} tbl.select(tbl.video.scene_detect_histogram()).collect() ``` Detect more scenes by lowering the threshold: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} tbl.select(tbl.video.scene_detect_histogram(threshold=0.03)).collect() ``` Use fewer bins for faster processing: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} tbl.select(tbl.video.scene_detect_histogram(bins=64)).collect() ``` Use with a longer minimum scene length: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} tbl.select(tbl.video.scene_detect_histogram(min_scene_len=30)).collect() ``` Add scene cuts as a computed column: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} tbl.add_computed_column( scene_cuts=tbl.video.scene_detect_histogram(threshold=0.04) ) ``` ## udf  scene\_detect\_threshold() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf scene_detect_threshold( video: pxt.Video, *, fps: pxt.Float | None = None, threshold: pxt.Float = 12.0, min_scene_len: pxt.Int = 15, fade_bias: pxt.Float = 0.0, add_final_scene: pxt.Bool = False, method: pxt.String = 'floor' ) -> pxt.Json[(Json[{'start_time': Float, 'start_pts': Int, 'duration': Float}], ...)] ``` Detect fade-in and fade-out transitions in a video using PySceneDetect's [ThresholdDetector](https://www.scenedetect.com/docs/latest/api/detectors.html#scenedetect.detectors.threshold_detector.ThresholdDetector). ThresholdDetector identifies scenes by detecting when pixel brightness falls below or rises above a threshold value, suitable for detecting fade-to-black, fade-to-white, and similar transitions. **Requirements:** * `pip install scenedetect` **Parameters:** * **`video`** (`pxt.Video`): The video to analyze for fade transitions. * **`fps`** (`pxt.Float | None`): Number of frames to extract per second for analysis. If None or 0, analyzes all frames. Lower values process faster but may miss exact transition points. * **`threshold`** (`pxt.Float`): 8-bit intensity value that each pixel value (R, G, and B) must be less than or equal to in order to trigger a fade in/out. * **`min_scene_len`** (`pxt.Int`): Once a cut is detected, this many frames must pass before a new one can be added to the scene list. * **`fade_bias`** (`pxt.Float`): Float between -1.0 and +1.0 representing the percentage of timecode skew for the start of a scene (-1.0 causing a cut at the fade-to-black, 0.0 in the middle, and +1.0 causing the cut to be right at the position where the threshold is passed). * **`add_final_scene`** (`pxt.Bool`): Boolean indicating if the video ends on a fade-out to generate an additional scene at this timecode. * **`method`** (`pxt.String`): How to treat threshold when detecting fade events * 'ceiling': Fade out happens when frame brightness rises above threshold. * 'floor': Fade out happens when frame brightness falls below threshold. **Returns:** * `pxt.Json[(Json[{'start_time': Float, 'start_pts': Int, 'duration': Float}], ...)]`: A list of dictionaries, one for each detected scene, with the following keys: * `start_time` (float): The start time of the scene in seconds. * `start_pts` (int): The pts of the start of the scene. * `duration` (float): The duration of the scene in seconds. The list is ordered chronologically. Returns the full duration of the video if no scenes are detected. **Examples:** Detect fade-to-black transitions with default parameters: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} tbl.select(tbl.video.scene_detect_threshold()).collect() ``` Use a lower threshold to detect darker fades: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} tbl.select(tbl.video.scene_detect_threshold(threshold=8.0)).collect() ``` Detect fade-to-white transitions using the 'ceiling' method: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} tbl.select(tbl.video.scene_detect_threshold(method='ceiling')).collect() ``` Add final scene boundary: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} tbl.select( tbl.video.scene_detect_threshold(add_final_scene=True) ).collect() ``` Add fade transitions as a computed column: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} tbl.add_computed_column( fade_cuts=tbl.video.scene_detect_threshold(threshold=15.0) ) ``` ## udf  scroll() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf scroll( video: pxt.Video, *, w: pxt.Int | None = None, h: pxt.Int | None = None, x_speed: pxt.Float = 0, y_speed: pxt.Float = 0, x_start: pxt.Int = 0, y_start: pxt.Int = 0, video_encoder: pxt.String | None = None, video_encoder_args: pxt.Json | None = None ) -> pxt.Video ``` Apply a scrolling viewport effect to a video using ffmpeg's crop filter. Extracts a viewport of size `w` x `h` from each frame, starting at position (`x_start`, `y_start`) and moving at (`x_speed`, `y_speed`) pixels per second. The viewport clamps at the frame edges: once it reaches a boundary, it stops moving and the remaining frames show a static crop. At least one of `w` or `h` must be smaller than the input dimensions for the effect to be visible. The clip duration is unchanged. To pan across the full available range, set `x_speed = (input_width - w) / duration`. **Requirements:** * `ffmpeg` needs to be installed and in PATH **Parameters:** * **`video`** (`pxt.Video`): Input video. * **`w`** (`pxt.Int | None`): Width of the output viewport in pixels. If None, uses the input width. * **`h`** (`pxt.Int | None`): Height of the output viewport in pixels. If None, uses the input height. * **`x_speed`** (`pxt.Float`): Horizontal scroll speed in pixels per second. Positive values scroll rightward (the viewport moves right, revealing content to the right). Negative values scroll leftward. * **`y_speed`** (`pxt.Float`): Vertical scroll speed in pixels per second. Positive values scroll downward. Negative values scroll upward. * **`x_start`** (`pxt.Int`): Initial horizontal offset of the viewport in pixels. * **`y_start`** (`pxt.Int`): Initial vertical offset of the viewport in pixels. * **`video_encoder`** (`pxt.String | None`): Video encoder to use. If not specified, uses the default encoder for the current platform. * **`video_encoder_args`** (`pxt.Json | None`): Additional arguments to pass to the video encoder. **Returns:** * `pxt.Video`: A new video with the scrolling effect applied. Output dimensions are `w` x `h`. **Examples:** Pan rightward across a 1920x1080 video using a 1280-pixel-wide viewport, scrolling at 50 px/s: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} tbl.select(tbl.video.scroll(w=1280, x_speed=50)).collect() ``` Pan rightward across the full range of a 1920x1080 video in exactly its duration. The viewport is 1280 px wide, so the pan range is 1920 - 1280 = 640 px. For a 10-second video, set `x_speed = 640 / 10 = 64`: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} tbl.select(tbl.video.scroll(w=1280, x_speed=64)).collect() ``` Pan leftward across a 1920x1080 video, starting from the right edge: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} tbl.select(tbl.video.scroll(w=1280, x_start=640, x_speed=-64)).collect() ``` ## udf  segment\_video() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf segment_video( video: pxt.Video, *, duration: pxt.Float | None = None, segment_times: pxt.Json[(Float, ...)] | None = None, mode: pxt.String = 'accurate', video_encoder: pxt.String | None = None, video_encoder_args: pxt.Json | None = None ) -> pxt.Json[(Json[{'segment_start': Float | None, 'segment_start_pts': Int | None, 'segment_end': Float | None, 'segment_end_pts': Int | None, 'video_segment': Video}], ...)] ``` Split a video into segments. **Requirements:** * `ffmpeg` needs to be installed and in PATH **Parameters:** * **`video`** (`pxt.Video`): Input video file to segment * **`duration`** (`pxt.Float | None`): Duration of each segment (in seconds). For `mode='fast'`, this is approximate; for `mode='accurate'`, segments will have exact durations. Cannot be specified together with `segment_times`. * **`segment_times`** (`pxt.Json[(Float`): List of timestamps (in seconds) in video where segments should be split. Note that these are not segment durations. If all segment times are less than the duration of the video, produces exactly `len(segment_times) + 1` segments. Cannot be empty or be specified together with `duration`. * **`mode`** (`Any`): Segmentation mode: * `'fast'`: Quick segmentation using stream copy (splits only at keyframes, approximate durations) * `'accurate'`: Precise segmentation with re-encoding (exact durations, slower) * **`video_encoder`** (`Any`): Video encoder to use. If not specified, uses the default encoder for the current platform. Only available for `mode='accurate'`. * **`video_encoder_args`** (`Any`): Additional arguments to pass to the video encoder. Only available for `mode='accurate'`. **Returns:** * `pxt.Json[(Json[{'segment_start': Float | None, 'segment_start_pts': Int | None, 'segment_end': Float | None, 'segment_end_pts': Int | None, 'video_segment': Video}], ...)]`: A list of [`VideoSegment`](./videosegment) records, one per generated segment. **Examples:** Split a video at 1 minute intervals using fast mode: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} tbl.select( segments=tbl.video.segment_video(duration=60, mode='fast') ).collect() ``` Split video into exact 10-second segments with default accurate mode, using the libx264 encoder with a CRF of 23 and slow preset (for smaller output files): ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} tbl.select( segments=tbl.video.segment_video( duration=10, video_encoder='libx264', video_encoder_args={'crf': 23, 'preset': 'slow'}, ) ).collect() ``` Split video into two parts at the midpoint: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} duration = tbl.video.get_duration() tbl.select( segments=tbl.video.segment_video(segment_times=[duration / 2]) ).collect() ``` ## udf  speed() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf speed( video: pxt.Video, *, factor: pxt.Float, video_encoder: pxt.String | None = None, video_encoder_args: pxt.Json | None = None ) -> pxt.Video ``` Change the playback speed of a video using ffmpeg's setpts filter. A factor of 2.0 doubles the speed (halves the duration); a factor of 0.5 halves the speed (doubles the duration). Audio pitch is preserved using ffmpeg's `atempo` filter. **Requirements:** * `ffmpeg` needs to be installed and in PATH **Parameters:** * **`video`** (`pxt.Video`): Input video. * **`factor`** (`pxt.Float`): Speed multiplier. Must be positive. Values > 1.0 speed up, values \< 1.0 slow down. * **`video_encoder`** (`pxt.String | None`): Video encoder to use. If not specified, uses the default encoder for the current platform. * **`video_encoder_args`** (`pxt.Json | None`): Additional arguments to pass to the video encoder. **Returns:** * `pxt.Video`: A new video with the adjusted playback speed. **Examples:** Double the speed: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} tbl.select(tbl.video.speed(factor=2.0)).collect() ``` Half speed (slow motion): ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} tbl.select(tbl.video.speed(factor=0.5)).collect() ``` ## udf  with\_audio() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf with_audio( video: pxt.Video, audio: pxt.Audio, *, video_start_time: pxt.Float = 0.0, video_duration: pxt.Float | None = None, audio_start_time: pxt.Float = 0.0, audio_duration: pxt.Float | None = None ) -> pxt.Video ``` Creates a new video that combines the video stream from `video` and the audio stream from `audio`. The `start_time` and `duration` parameters can be used to select a specific time range from each input. If the audio input (or selected time range) is longer than the video, the audio will be truncated. **Requirements:** * `ffmpeg` needs to be installed and in PATH **Parameters:** * **`video`** (`pxt.Video`): Input video. * **`audio`** (`pxt.Audio`): Input audio. * **`video_start_time`** (`pxt.Float`): Start time in the video input (in seconds). * **`video_duration`** (`pxt.Float | None`): Duration of video segment (in seconds). If None, uses the remainder of the video after `video_start_time`. `video_duration` determines the duration of the output video. * **`audio_start_time`** (`pxt.Float`): Start time in the audio input (in seconds). * **`audio_duration`** (`pxt.Float | None`): Duration of audio segment (in seconds). If None, uses the remainder of the audio after `audio_start_time`. If the audio is longer than the output video, it will be truncated. **Returns:** * `pxt.Video`: A new video file with the audio track added. **Examples:** Add background music to a video: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} tbl.select(tbl.video.with_audio(tbl.music_track)).collect() ``` Add audio starting 5 seconds into both files: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} tbl.select( tbl.video.with_audio( tbl.music_track, video_start_time=5.0, audio_start_time=5.0 ) ).collect() ``` Use a 10-second clip from the middle of both files: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} tbl.select( tbl.video.with_audio( tbl.music_track, video_start_time=30.0, video_duration=10.0, audio_start_time=15.0, audio_duration=10.0, ) ).collect() ``` ## udf  zoom() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf zoom( video: pxt.Video, *, start_scale: pxt.Float = 1.0, end_scale: pxt.Float = 1.3, center: pxt.Json[(Float, ...)] | None = None, video_encoder: pxt.String | None = None, video_encoder_args: pxt.Json | None = None ) -> pxt.Video ``` Apply a smooth zoom effect over the duration of a video using ffmpeg's zoompan filter. The zoom factor interpolates linearly from `start_scale` to `end_scale`. The effect works by computing a crop region at each frame (centered on `center`) and scaling it back to the original resolution. Output dimensions match the input. * `start_scale < end_scale`: zoom in (frame progressively tightens) * `start_scale > end_scale`: zoom out (frame progressively widens) * `start_scale == end_scale`: static zoom (constant crop, no animation) **Requirements:** * `ffmpeg` needs to be installed and in PATH **Parameters:** * **`video`** (`pxt.Video`): Input video. * **`start_scale`** (`pxt.Float`): Zoom factor at the start of the video. Must be >= 1.0. * **`end_scale`** (`pxt.Float`): Zoom factor at the end of the video. Must be >= 1.0. * **`center`** (`pxt.Json[(Float`): Zoom center as `[x, y]` in normalized coordinates (0.0 to 1.0), where `[0.5, 0.5]` is the frame center. If None, defaults to `[0.5, 0.5]`. * **`video_encoder`** (`Any`): Video encoder to use. If not specified, uses the default encoder for the current platform. * **`video_encoder_args`** (`Any`): Additional arguments to pass to the video encoder. **Returns:** * `pxt.Video`: A new video with the zoom effect applied. Output resolution matches the input. **Examples:** Zoom in (default, 1.0x to 1.3x centered): ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} tbl.select(tbl.video.zoom()).collect() ``` Zoom out from 2x to 1x: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} tbl.select(tbl.video.zoom(start_scale=2.0, end_scale=1.0)).collect() ``` Zoom in toward the upper-left quadrant: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} tbl.select(tbl.video.zoom(end_scale=1.5, center=[0.25, 0.25])).collect() ``` Static 1.5x zoom (no animation): ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} tbl.select(tbl.video.zoom(start_scale=1.5, end_scale=1.5)).collect() ``` # VideoSegment Source: https://docs.pixeltable.com/sdk/latest/videosegment View Source on GitHub # class  pixeltable.functions.video.VideoSegment One segment of a video: the segment's video plus where it falls in the source video. ## attr  segment\_end ``` segment_end: float | None ``` Segment end, in seconds from the start of the source video; None if the source stream does not report it. ## attr  segment\_end\_pts ``` segment_end_pts: int | None ``` Segment end, as a presentation timestamp (in the source stream's time base); None if unavailable. ## attr  segment\_start ``` segment_start: float | None ``` Segment start, in seconds from the start of the source video; None if the source stream does not report it. ## attr  segment\_start\_pts ``` segment_start_pts: int | None ``` Segment start, as a presentation timestamp (in the source stream's time base); None if unavailable. ## attr  video\_segment ``` video_segment: pxt.Video ``` The segment's video. # vision Source: https://docs.pixeltable.com/sdk/latest/vision View Source on GitHub # module  pixeltable.functions.vision Pixeltable UDFs for Computer Vision. Example: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} import pixeltable as pxt from pixeltable.functions import vision as pxtv t = pxt.get_table(...) t.select( pxtv.bboxes_draw(t.img, boxes=t.boxes, labels=t.labels) ).collect() ``` ## udf  bboxes\_clip\_to\_canvas() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf bboxes_clip_to_canvas( bboxes: pxt.Json[(Json, ...)], format: pxt.String, *, width: pxt.Int | None = None, height: pxt.Int | None = None, min_visibility: pxt.Float = 0.0, min_area: pxt.Float = 0.0 ) -> pxt.Json[(Json, ...)] ``` Clip a list of bounding boxes to a canvas of specified size. **Parameters:** * **`bboxes`** (`pxt.Json[(Json`): List of bounding boxes, specified with absolute pixel coordinates if `width`/`height` are given, with relative coordinates otherwise. All-integer boxes are always treated as absolute and require `width`/`height`. * **`format`** (`Any`): Format of the bounding box coordinates, one of 'xyxy', 'xywh', 'cxcywh'. * **`width`** (`Any`): Canvas width in absolute pixels. Required for absolute coordinates, omit for relative. * **`height`** (`Any`): Canvas height in absolute pixels. Required for absolute coordinates, omit for relative. * **`min_visibility`** (`Any`): Minimum fraction of the bounding box that must be visible after clipping. If the visibility is less than this value, returns None. * **`min_area`** (`Any`): Minimum area of the bounding box after clipping. If the area is less than this value, returns None. **Returns:** * `pxt.Json[(Json, ...)]`: List of clipped bounding boxes in the same format as the input. Boxes that don't meet the min\_visibility or min\_area thresholds are replaced with None. ## udf  bboxes\_convert() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf bboxes_convert( bboxes: pxt.Json[(Json, ...)], *, src_format: pxt.String, dst_format: pxt.String ) -> pxt.Json[(Json, ...)] ``` Convert a list of bounding boxes from src\_format to dst\_format. **Parameters:** * **`bboxes`** (`pxt.Json[(Json`): List of bounding boxes, each with absolute pixel coordinates or relative coordinates in \[0, 1]. Coordinates may be int or float; relative vs absolute is inferred (relative only when every coordinate is within \[0, 1]), so absolute float coordinates such as 10.5 are supported. * **`src_format`** (`Any`): Source format, one of 'xyxy', 'xywh', 'cxcywh'. * **`dst_format`** (`Any`): Destination format, one of 'xyxy', 'xywh', 'cxcywh'. **Returns:** * `pxt.Json[(Json, ...)]`: List of bounding boxes in dst\_format. ## udf  bboxes\_crop\_canvas() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf bboxes_crop_canvas( bboxes: pxt.Json[(Json, ...)], format: pxt.String, *, canvas_region: pxt.Json[(Json, ...)], canvas_region_format: pxt.String, canvas_width: pxt.Int | None = None, canvas_height: pxt.Int | None = None ) -> pxt.Json[(Json, ...)] ``` Adjust a list of bounding boxes to account for a canvas crop. **Parameters:** * **`bboxes`** (`pxt.Json[(Json`): List of bounding boxes, specified with absolute pixel coordinates if `canvas_width`/`canvas_height` are given, with relative coordinates otherwise. All-integer boxes are always treated as absolute and require `canvas_width`/`canvas_height`. * **`format`** (`Any`): Format of the bounding box coordinates, one of 'xyxy', 'xywh', 'cxcywh'. * **`canvas_width`** (`Any`): Canvas width in absolute pixels. Required for absolute coordinates, omit for relative. * **`canvas_height`** (`Any`): Canvas height in absolute pixels. Required for absolute coordinates, omit for relative. * **`canvas_region`** (`Any`): Canvas region that was cropped, either specified with absolute pixel coordinates or relative coordinates, in the format specified by `canvas_region_format`. * **`canvas_region_format`** (`Any`): Format of the `canvas_region` coordinates, one of 'xyxy', 'xywh', 'cxcywh'. **Returns:** * `pxt.Json[(Json, ...)]`: List of adjusted bounding boxes in the same format as the input. They can extend beyond the canvas boundaries. ## udf  bboxes\_draw() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf bboxes_draw( img: pxt.Image, boxes: pxt.Json[(Json[(Int, ...)], ...)], *, labels: pxt.Json[(Json, ...)] | None = None, color: pxt.String | None = None, box_colors: pxt.Json[(String, ...)] | None = None, alpha: pxt.Float | None = None, fill: pxt.Bool = False, fill_alpha: pxt.Float | None = None, width: pxt.Int = 1, font: pxt.String | None = None, font_size: pxt.Int | None = None ) -> pxt.Image ``` Draws bounding boxes on the given image. Labels can be any type that supports `str()` and is hashable (e.g., strings, ints, etc.). Colors can be specified as common HTML color names (e.g., 'red') supported by PIL's [`ImageColor`](https://pillow.readthedocs.io/en/stable/reference/ImageColor.html#imagecolor-module) module or as RGB/RGBA hex codes (e.g., '#FF0000', '#FF0000FF'). If opacity isn't specified in the color string and `alpha`/`fill_alpha` is `None`, defaults to 1.0 for box borders and 0.5 for filled boxes. If no colors are specified, this function randomly assigns each label a specific color based on a hash of the label. **Parameters:** * **`img`** (`pxt.Image`): The image on which to draw the bounding boxes. * **`boxes`** (`pxt.Json[(Json[(Int`): List of bounding boxes, each represented as \[xmin, ymin, xmax, ymax]. * **`labels`** (`Any`): List of labels for each bounding box. * **`color`** (`Any`): Single color to be used for all bounding boxes and labels. * **`box_colors`** (`Any`): List of colors, one per bounding box. * **`alpha`** (`Any`): Opacity (0-1) of the bounding box borders and labels. If non-`None`, overrides any alpha in `color`/`box_colors`. * **`fill`** (`Any`): Whether to fill the bounding boxes with color. * **`fill_alpha`** (`Any`): Opacity (0-1) of the bounding box fill. If non-`None`, overrides any alpha in `color`/`box_colors`. * **`width`** (`Any`): Width of the bounding box borders. * **`font`** (`Any`): Name of a system font or path to a TrueType font file, as required by [`PIL.ImageFont.truetype()`](https://pillow.readthedocs.io/en/stable/reference/ImageFont.html#PIL.ImageFont.truetype). If `None`, uses the default provided by [`PIL.ImageFont.load_default()`](https://pillow.readthedocs.io/en/stable/reference/ImageFont.html#PIL.ImageFont.load_default). * **`font_size`** (`Any`): Size of the font used for labels in points. Only used in conjunction with non-`None` `font` argument. **Returns:** * `pxt.Image`: The image with bounding boxes drawn on it. ## udf  bboxes\_pad() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf bboxes_pad( bboxes: pxt.Json[(Json, ...)], format: pxt.String, *, top: pxt.Int | None = None, bottom: pxt.Int | None = None, left: pxt.Int | None = None, right: pxt.Int | None = None, x: pxt.Int | None = None, y: pxt.Int | None = None ) -> pxt.Json[(Json, ...)] ``` Pad a list of bounding boxes. **Parameters:** * **`bboxes`** (`pxt.Json[(Json`): List of bounding boxes in absolute pixel coordinates. * **`format`** (`Any`): Format of the bounding box coordinates, one of 'xyxy', 'xywh', 'cxcywh'. * **`top`** (`Any`): Amount to pad at the top, in absolute pixels. * **`bottom`** (`Any`): Amount to pad at the bottom, in absolute pixels. * **`left`** (`Any`): Amount to pad at the left, in absolute pixels. * **`right`** (`Any`): Amount to pad at the right, in absolute pixels. * **`x`** (`Any`): Amount to pad at the left and right, in absolute pixels. * **`y`** (`Any`): Amount to pad at the top and bottom, in absolute pixels. **Returns:** * `pxt.Json[(Json, ...)]`: List of padded bounding boxes in the same format as the input. ## udf  bboxes\_resize() ```python Signatures theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Signature 1: @pxt.udf bboxes_resize( bboxes: pxt.Json[(Json, ...)], format: pxt.String, *, width: pxt.Int | None, height: pxt.Int | None, aspect: pxt.String | None, aspect_mode: pxt.String | None ) -> pxt.Json[(Json, ...)] # Signature 2: @pxt.udf bboxes_resize( bboxes: pxt.Json[(Json, ...)], format: pxt.String, *, width: pxt.Float | None, height: pxt.Float | None, aspect: pxt.Float | None, aspect_mode: pxt.String | None ) -> pxt.Json[(Json, ...)] ``` Resize a list of bounding boxes (center-anchored): * to a specified width or height (the other dimension is scaled to maintain the aspect ratio) * to a specified aspect ratio Only one of `width`, `height`, or `aspect` can be specified. **Parameters:** * **`bboxes`** (`Json[(Json, ...)]`): List of bounding boxes, each with absolute pixel coordinates or relative coordinates in \[0, 1]. Coordinates may be int or float; relative vs absolute is inferred (relative only when every coordinate is within \[0, 1]), so absolute float coordinates such as 10.5 are supported. * **`format`** (`String`): Format of the bounding box coordinates, one of 'xyxy', 'xywh', 'cxcywh'. * **`width`** (`Int | None`, default: `Literal(None)`): Target width. Pass an `int` for absolute pixels or a `float` for relative coordinates. * **`height`** (`Int | None`, default: `Literal(None)`): Target height. Pass an `int` for absolute pixels or a `float` for relative coordinates. * **`aspect`** (`String | None`, default: `Literal(None)`): Target aspect ratio. Pass a `str` like '16:9' or a `float` like 1.78. * **`aspect`** (`String | None`, default: `Literal(None)`): Target aspect ratio as a string 'W:H' (e.g., '16:9') or a `float`. Resizes either the width or height to match the specified aspect ratio, maintaining the other dimension. Requires `aspect_mode`. * **`aspect_mode`** (`String | None`, default: `Literal(None)`): Either 'crop' or 'pad'. Required when `aspect` is specified. If `crop`, reduces the oversized dimension to match the aspect ratio. If `pad`, extends the undersized dimension to match the aspect ratio. **Returns:** * `pxt.Json[(Json, ...)]`: List of resized bounding boxes in the same format as the input. ## udf  bboxes\_resize\_canvas() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf bboxes_resize_canvas( bboxes: pxt.Json[(Json, ...)], format: pxt.String, *, canvas_width: pxt.Int | None = None, canvas_height: pxt.Int | None = None, new_canvas_width: pxt.Int | None = None, new_canvas_height: pxt.Int | None = None, canvas_scale: pxt.Float | None = None, canvas_scale_x: pxt.Float | None = None, canvas_scale_y: pxt.Float | None = None ) -> pxt.Json[(Json, ...)] ``` Adjust a list of bounding boxes to account for a canvas resize. The resize operation can be expressed * as absolute pixel dimensions (requires canvas\_width, canvas\_height, new\_canvas\_width, new\_canvas\_height) * as relative dimensions (requires at least one of canvas\_scale, canvas\_scale\_x, canvas\_scale\_y) **Parameters:** * **`bboxes`** (`pxt.Json[(Json`): List of bounding boxes in absolute pixel coordinates. * **`format`** (`Any`): Format of the bounding box coordinates, one of 'xyxy', 'xywh', 'cxcywh'. * **`canvas_width`** (`Any`): Original canvas width in absolute pixels. * **`canvas_height`** (`Any`): Original canvas height in absolute pixels. * **`new_canvas_width`** (`Any`): New canvas width in absolute pixels. Requires canvas\_width/canvas\_height to be specified. * **`new_canvas_height`** (`Any`): New canvas height in absolute pixels. Requires canvas\_width/canvas\_height to be specified. * **`canvas_scale`** (`Any`): Scale factor to apply to both canvas dimensions. * **`canvas_scale_x`** (`Any`): Scale factor to apply to the canvas width. * **`canvas_scale_y`** (`Any`): Scale factor to apply to the canvas height. **Returns:** * `pxt.Json[(Json, ...)]`: List of adjusted bounding boxes in the same format as the input. ## udf  bboxes\_scale() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf bboxes_scale( bboxes: pxt.Json[(Json, ...)], format: pxt.String, *, factor: pxt.Float | None = None, x_factor: pxt.Float | None = None, y_factor: pxt.Float | None = None ) -> pxt.Json[(Json, ...)] ``` Re-scale a list of bounding boxes (center-anchored). **Parameters:** * **`bboxes`** (`pxt.Json[(Json`): List of bounding boxes, each with absolute pixel coordinates or relative coordinates in \[0, 1]. Coordinates may be int or float; relative vs absolute is inferred (relative only when every coordinate is within \[0, 1]), so absolute float coordinates such as 10.5 are supported. * **`format`** (`Any`): Format of the bounding box coordinates, one of 'xyxy', 'xywh', 'cxcywh'. * **`factor`** (`Any`): Scale factor to apply to both box dimensions. * **`x_factor`** (`Any`): Scale factor to apply to the box width. * **`y_factor`** (`Any`): Scale factor to apply to the box height. **Returns:** * `pxt.Json[(Json, ...)]`: List of scaled bounding boxes in the same format as the input. ## udf  eval\_detections() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf eval_detections( pred_bboxes: pxt.Json[(Json[(Int, ...)], ...)], pred_labels: pxt.Json[(Int, ...)], pred_scores: pxt.Json[(Float, ...)], gt_bboxes: pxt.Json[(Json[(Int, ...)], ...)], gt_labels: pxt.Json[(Int, ...)], min_iou: pxt.Float = 0.5 ) -> pxt.Json[(Json[{'min_iou': Float, 'category': Int, 'tp': Json[(Int, ...)], 'fp': Json[(Int, ...)], 'scores': Json[(Float, ...)], 'num_gts': Int}], ...)] ``` Evaluates the performance of a set of predicted bounding boxes against a set of ground truth bounding boxes. **Parameters:** * **`pred_bboxes`** (`pxt.Json[(Json[(Int`): List of predicted bounding boxes, each represented as \[xmin, ymin, xmax, ymax]. * **`pred_labels`** (`Any`): List of predicted labels. * **`pred_scores`** (`Any`): List of predicted scores. * **`gt_bboxes`** (`Any`): List of ground truth bounding boxes, each represented as \[xmin, ymin, xmax, ymax]. * **`gt_labels`** (`Any`): List of ground truth labels. * **`min_iou`** (`Any`): Minimum intersection-over-union (IoU) threshold for a predicted bounding box to be considered a true positive. **Returns:** * `pxt.Json[(Json[{'min_iou': Float, 'category': Int, 'tp': Json[(Int, ...)], 'fp': Json[(Int, ...)], 'scores': Json[(Float, ...)], 'num_gts': Int}], ...)]`: A list of dictionaries, one per label class, with the following entries: * `min_iou` (`float`): the value of `min_iou` used for the detections * `category` (`int`): the label class * `tp` (`list[int]`): 1 or 0 for each predicted bounding box of this class, indicating a true positive * `fp` (`list[int]`): 1 or 0 for each predicted bounding box of this class; `fp[n] == 1 - tp[n]` * `scores` (`list[float]`): the predicted scores for each bounding box of this class * `num_gts` (`int`): the number of ground truth bounding boxes of this class ## udf  overlay\_segmentation() ```python Signatures theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Signature 1: @pxt.udf overlay_segmentation( img: pxt.Image, segmentation: pxt.Array[(None, None), int32], *, alpha: pxt.Float, background: pxt.Int, segment_colors: pxt.Json[(String, ...)] | None, draw_contours: pxt.Bool, contour_thickness: pxt.Int ) -> pxt.Image # Signature 2: @pxt.udf overlay_segmentation( img: pxt.Image, segmentation: pxt.Array[(None, None, None), bool], *, ids: pxt.Array[(None,), int64] | None, alpha: pxt.Float, segment_colors: pxt.Json[(String, ...)] | None, draw_contours: pxt.Bool, contour_thickness: pxt.Int ) -> pxt.Image ``` Overlays a colored segmentation map on an image. Colors can be specified as common HTML color names (e.g., 'red') supported by PIL's [`ImageColor`](https://pillow.readthedocs.io/en/stable/reference/ImageColor.html#imagecolor-module) module or as RGB hex codes (e.g., '#FF0000'). If no colors are specified, this function randomly assigns each segment a specific color based on a hash of its id. Two input formats are supported: * A 2D `(H, W)` int32 id-map where each pixel value is a segment id. This matches the panoptic output of [`detr_for_segmentation()`](./huggingface#func-detr_for_segmentation). * A 3D `(num_instances, H, W)` boolean stack with one binary mask per instance. This matches the per-instance mask output of [`sam_for_segmentation()`](./huggingface#func-sam_for_segmentation). Instances are assigned ids `1..num_instances` in order; where masks overlap the highest id wins. **Parameters:** * **`img`** (`Image`): Input image. * **`segmentation`** (`Array[(None, None), int32]`): Either a 2D `(H, W)` int32 array of segment ids, or a 3D `(num_instances, H, W)` boolean array of per-instance masks. * **`alpha`** (`Float`, default: `Literal(0.5)`): Blend factor for the overlay (0.0 = only original image, 1.0 = only segmentation colors). * **`background`** (`Int`, default: `Literal(0)`): Segment id to treat as background (not overlaid with color, showing the original image through). Only meaningful for the 2D id-map form. * **`segment_colors`** (`Json[(String, ...)] | None`, default: `Literal(None)`): List of colors, one per segment id. If the list is shorter than the number of segments, the remaining segments will be assigned colors automatically. * **`draw_contours`** (`Bool`, default: `Literal(True)`): If True, draw contours around each segment with full opacity. * **`contour_thickness`** (`Int`, default: `Literal(1)`): Thickness of the contour lines in pixels. **Returns:** * `pxt.Image`: The image with the colored segmentation overlay. # vllm Source: https://docs.pixeltable.com/sdk/latest/vllm View Source on GitHub # module  pixeltable.functions.vllm Pixeltable UDFs for vLLM models. Provides integration with vLLM for high-throughput inference with large language models, supporting chat completions and text generation with HuggingFace models. ## udf  chat\_completions() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf chat_completions( messages: pxt.Json[(Json, ...)], *, model: pxt.String, engine_args: pxt.Json | None = None, sampling_params: pxt.Json | None = None ) -> VllmRequestOutput ``` Generate a chat completion from a list of messages using vLLM. For additional details, see the [vLLM documentation](https://docs.vllm.ai/en/stable/). **Requirements:** * `pip install vllm` **Parameters:** * **`messages`** (`pxt.Json[(Json`): A list of messages to generate a response for. Each message should be a dict with `role` and `content` keys, following the OpenAI chat format. * **`model`** (`Any`): The HuggingFace model identifier (e.g., `'Qwen/Qwen2.5-0.5B-Instruct'`). * **`engine_args`** (`Any`): Additional keyword args for the vLLM `LLM` constructor, such as `dtype`, `max_model_len`, `gpu_memory_utilization`, `tensor_parallel_size`. For details, see the [vLLM engine args documentation](https://docs.vllm.ai/en/stable/configuration/engine_args/). * **`sampling_params`** (`Any`): Keyword args for vLLM `SamplingParams`, such as `max_tokens`, `temperature`, `top_p`, `top_k`. For details, see the [vLLM sampling params documentation](https://docs.vllm.ai/en/stable/api/vllm/sampling_params.html). **Returns:** * `VllmRequestOutput`: A dict containing the vLLM `RequestOutput` in its native format. **Examples:** Add a computed column that generates chat completions: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} t.add_computed_column( result=chat_completions( t.messages, model='Qwen/Qwen2.5-0.5B-Instruct' ) ) ``` With custom sampling parameters: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} t.add_computed_column( result=chat_completions( t.messages, model='Qwen/Qwen2.5-0.5B-Instruct', sampling_params={'max_tokens': 256, 'temperature': 0.7}, ) ) ``` ## udf  generate() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf generate( prompt: pxt.String, *, model: pxt.String, engine_args: pxt.Json | None = None, sampling_params: pxt.Json | None = None ) -> VllmRequestOutput ``` Generate text completion for a given prompt using vLLM. Uses vLLM's high-throughput inference engine for efficient local LLM serving. Models are loaded from HuggingFace and cached for reuse across calls. For additional details, see the [vLLM documentation](https://docs.vllm.ai/en/stable/). **Requirements:** * `pip install vllm` **Parameters:** * **`prompt`** (`pxt.String`): The text prompt to generate a completion for. * **`model`** (`pxt.String`): The HuggingFace model identifier (e.g., `'Qwen/Qwen2.5-0.5B-Instruct'`). * **`engine_args`** (`pxt.Json | None`): Additional keyword args for the vLLM `LLM` constructor, such as `dtype`, `max_model_len`, `gpu_memory_utilization`, `tensor_parallel_size`. For details, see the [vLLM engine args documentation](https://docs.vllm.ai/en/stable/configuration/engine_args/). * **`sampling_params`** (`pxt.Json | None`): Keyword args for vLLM `SamplingParams`, such as `max_tokens`, `temperature`, `top_p`, `top_k`. For details, see the [vLLM sampling params documentation](https://docs.vllm.ai/en/stable/api/vllm/sampling_params.html). **Returns:** * `VllmRequestOutput`: A dict containing the vLLM `RequestOutput` in its native format. **Examples:** Add a computed column that generates text completions: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} t.add_computed_column( result=generate(t.prompt, model='Qwen/Qwen2.5-0.5B-Instruct') ) ``` # voyageai Source: https://docs.pixeltable.com/sdk/latest/voyageai View Source on GitHub # module  pixeltable.functions.voyageai Pixeltable UDFs that wrap various endpoints from the Voyage AI API. In order to use them, you must first `pip install voyageai` and configure your Voyage AI credentials, as described in the [Working with Voyage AI](https://docs.pixeltable.com/howto/providers/working-with-voyageai) tutorial. ## udf  embeddings() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf embeddings( input: pxt.String, *, model: pxt.String, input_type: pxt.String | None = None, truncation: pxt.Bool | None = None, output_dimension: pxt.Int | None = None, output_dtype: pxt.String | None = None ) -> pxt.Array[(None,), float32] ``` Creates an embedding vector representing the input text. Equivalent to the Voyage AI `embeddings` API endpoint. For additional details, see: [https://docs.voyageai.com/docs/embeddings](https://docs.voyageai.com/docs/embeddings) Request throttling: Applies the rate limit set in the config (section `voyageai`, key `rate_limit`). If no rate limit is configured, uses a default of 600 RPM. **Requirements:** * `pip install voyageai` **Parameters:** * **`input`** (`pxt.String`): The text to embed. * **`model`** (`pxt.String`): The model to use for the embedding. Recommended options: `voyage-3-large`, `voyage-3.5`, `voyage-3.5-lite`, `voyage-code-3`, `voyage-finance-2`, `voyage-law-2`. * **`input_type`** (`pxt.String | None`): Type of the input text. Options: `None`, `query`, `document`. When `input_type` is `None`, the embedding model directly converts the inputs into numerical vectors. For retrieval/search purposes, we recommend setting this to `query` or `document` as appropriate. * **`truncation`** (`pxt.Bool | None`): Whether to truncate the input texts to fit within the context length. Defaults to `True`. * **`output_dimension`** (`pxt.Int | None`): The number of dimensions for resulting output embeddings. Most models only support a single default dimension. Models `voyage-3-large`, `voyage-3.5`, `voyage-3.5-lite`, and `voyage-code-3` support: 256, 512, 1024 (default), and 2048. * **`output_dtype`** (`pxt.String | None`): The data type for the embeddings to be returned. Options: `float`, `int8`, `uint8`, `binary`, `ubinary`. Only `float` is currently supported in Pixeltable. **Returns:** * `pxt.Array[(None,), float32]`: An array representing the application of the given embedding to `input`. **Examples:** Add a computed column that applies the model `voyage-3.5` to an existing Pixeltable column `tbl.text` of the table `tbl`: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} tbl.add_computed_column( embed=embeddings(tbl.text, model='voyage-3.5', input_type='document') ) ``` Add an embedding index to an existing column `text`, using the model `voyage-3.5`: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} tbl.add_embedding_index( 'text', string_embed=embeddings.using(model='voyage-3.5') ) ``` ## udf  multimodal\_embed() ```python Signatures theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Signature 1: @pxt.udf multimodal_embed( text: pxt.String, *, model: pxt.String, input_type: pxt.String | None, truncation: pxt.Bool ) -> pxt.Array[(1024,), float32] # Signature 2: @pxt.udf multimodal_embed( image: pxt.Image, *, model: pxt.String, input_type: pxt.String | None, truncation: pxt.Bool ) -> pxt.Array[(1024,), float32] # Signature 3: @pxt.udf multimodal_embed( video: pxt.Video, *, model: pxt.String, input_type: pxt.String | None, truncation: pxt.Bool ) -> pxt.Array[(1024,), float32] ``` Creates an embedding vector for text, images, or video using Voyage AI's multimodal model. Equivalent to the Voyage AI `multimodal_embed` API endpoint. For additional details, see: [https://docs.voyageai.com/docs/multimodal-embeddings](https://docs.voyageai.com/docs/multimodal-embeddings) Request throttling: Applies the rate limit set in the config (section `voyageai`, key `rate_limit`). If no rate limit is configured, uses a default of 600 RPM. **Requirements:** * `pip install voyageai` **Parameters:** * **`text`** (`String`): The text to embed. * **`image`** (`Image`): The image to embed. * **`video`** (`Video`): The video to embed. * **`model`** (`String`): The model to use. Currently only `voyage-multimodal-3` is supported. * **`input_type`** (`String | None`, default: `Literal(None)`): Type of the input. Options: `None`, `query`, `document`. For retrieval/search, set to `query` or `document` as appropriate. * **`truncation`** (`Bool`, default: `Literal(True)`): Whether to truncate inputs to fit within context length. Defaults to `True`. **Returns:** * `pxt.Array[(1024,), float32]`: An array of 1024 floats representing the embedding. **Examples:** Embed a text column `description`: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} tbl.add_computed_column( embed=multimodal_embed(tbl.description, input_type='document') ) ``` Add an embedding index for column `description`: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} tbl.add_embedding_index( 'description', embed=multimodal_embed.using(model='voyage-multimodal-3'), ) ``` ## udf  rerank() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf rerank( query: pxt.String, documents: pxt.Json[(String, ...)], *, model: pxt.String, top_k: pxt.Int | None = None, truncation: pxt.Bool = True ) -> VoyageRerankResponse ``` Reranks documents based on their relevance to a query. Equivalent to the Voyage AI `rerank` API endpoint. For additional details, see: [https://docs.voyageai.com/docs/reranker](https://docs.voyageai.com/docs/reranker) Request throttling: Applies the rate limit set in the config (section `voyageai`, key `rate_limit`). If no rate limit is configured, uses a default of 600 RPM. **Requirements:** * `pip install voyageai` **Parameters:** * **`query`** (`pxt.String`): The query as a string. * **`documents`** (`pxt.Json[(String`): The documents to be reranked as a list of strings. * **`model`** (`Any`): The model to use for reranking. Recommended options: `rerank-2.5`, `rerank-2.5-lite`. * **`top_k`** (`Any`): The number of most relevant documents to return. If not specified, all documents will be reranked and returned. * **`truncation`** (`Any`): Whether to truncate the input to satisfy context length limits. Defaults to `True`. **Returns:** * `VoyageRerankResponse`: A dictionary containing: * `results`: List of reranking results with `index`, `document`, and `relevance_score` * `total_tokens`: The total number of tokens used **Examples:** Rerank similarity search results for better relevance. First, create a table with an embedding index, then use a query function to retrieve candidates and rerank them: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} docs = pxt.create_table('docs', {'text': pxt.String}) docs.add_computed_column(embed=embeddings(docs.text, model='voyage-3.5')) docs.add_embedding_index('text', embed=docs.embed) @pxt.query def get_candidates(query_text: str): sim = docs.text.similarity( query_text, embed=embeddings.using(model='voyage-3.5') ) return docs.order_by(sim, asc=False).limit(20).select(docs.text) queries = pxt.create_table('queries', {'query': pxt.String}) queries.add_computed_column(candidates=get_candidates(queries.query)) queries.add_computed_column( reranked=rerank( queries.query, queries.candidates.text, model='rerank-2.5', top_k=5, ) ) ``` # whisper Source: https://docs.pixeltable.com/sdk/latest/whisper View Source on GitHub # module  pixeltable.functions.whisper Pixeltable UDFs that wraps the OpenAI Whisper library. This UDF will cause Pixeltable to invoke the relevant model locally. In order to use it, you must first `pip install openai-whisper`. ## udf  transcribe() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf transcribe( audio: pxt.Audio, *, model: pxt.String, temperature: pxt.Json[(Float, ...)] | None = (0.0, 0.2, 0.4, 0.6, 0.8, 1.0), compression_ratio_threshold: pxt.Float | None = 2.4, logprob_threshold: pxt.Float | None = -1.0, no_speech_threshold: pxt.Float | None = 0.6, condition_on_previous_text: pxt.Bool = True, initial_prompt: pxt.String | None = None, word_timestamps: pxt.Bool = False, prepend_punctuations: pxt.String = '"\'“¿([{-', append_punctuations: pxt.String = '"\'.。,,!!??::”)]}、', decode_options: pxt.Json | None = None ) -> WhisperTranscription ``` Transcribe an audio file using Whisper. This UDF runs a transcription model *locally* using the Whisper library, equivalent to the Whisper `transcribe` function, as described in the [Whisper library documentation](https://github.com/openai/whisper). **Requirements:** * `pip install openai-whisper` **Parameters:** * **`audio`** (`pxt.Audio`): The audio file to transcribe. * **`model`** (`pxt.String`): The name of the model to use for transcription. **Returns:** * `WhisperTranscription`: A [`WhisperTranscription`](./whispertranscription) dictionary with the transcription's text, segments, and language. **Examples:** Add a computed column that applies the model `base.en` to an existing Pixeltable column `tbl.audio` of the table `tbl`: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} tbl.add_computed_column(result=transcribe(tbl.audio, model='base.en')) ``` Add a `String` column with the transcription's text: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} tbl.add_computed_column(text=tbl.result.text) ``` # WhisperSegment Source: https://docs.pixeltable.com/sdk/latest/whispersegment View Source on GitHub # class  pixeltable.functions.whisper.WhisperSegment One segment of a transcription, with its timing and decoding statistics. ## attr  avg\_logprob ``` avg_logprob: float ``` Average log probability of the tokens in the segment's decoding window. ## attr  compression\_ratio ``` compression_ratio: float ``` Compression ratio of the text in the segment's decoding window; a high value indicates repetitive output. ## attr  end ``` end: float ``` Segment end, in seconds from the start of the audio. ## attr  id ``` id: int ``` Index of the segment in the transcription's `segments` list. ## attr  no\_speech\_prob ``` no_speech_prob: float ``` Probability that the segment's decoding window contains no speech. ## attr  seek ``` seek: int ``` Start of the segment's decoding window, in 10 ms frames. ## attr  start ``` start: float ``` Segment start, in seconds from the start of the audio. ## attr  temperature ``` temperature: float ``` Sampling temperature of the segment's decoding window. ## attr  text ``` text: str ``` The segment's text. ## attr  tokens ``` tokens: list[int] ``` Token IDs of the segment; can include timestamp tokens. ## attr  words ``` words: NotRequired[list[WhisperWord]] ``` Timings of the segment's words; empty or absent unless `word_timestamps=True`. # WhisperTranscription Source: https://docs.pixeltable.com/sdk/latest/whispertranscription View Source on GitHub # class  pixeltable.functions.whisper.WhisperTranscription Output of [`transcribe()`](./whisper#func-transcribe). ## attr  language ``` language: str ``` Code of the transcription's language, such as `en`. ## attr  segments ``` segments: list[WhisperSegment] ``` The transcription's segments, in order. ## attr  text ``` text: str ``` The full transcription. # WhisperWord Source: https://docs.pixeltable.com/sdk/latest/whisperword View Source on GitHub # class  pixeltable.functions.whisper.WhisperWord One word of a transcription segment, with its timing. ## attr  end ``` end: float ``` Word end, in seconds from the start of the audio. ## attr  probability ``` probability: float ``` Mean probability of the word's tokens. ## attr  start ``` start: float ``` Word start, in seconds from the start of the audio. ## attr  word ``` word: str ``` The word's text, including any leading space and attached punctuation. # whisperx Source: https://docs.pixeltable.com/sdk/latest/whisperx View Source on GitHub # module  pixeltable.functions.whisperx WhisperX audio transcription and diarization functions. ## udf  transcribe() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf transcribe( audio: pxt.Audio, *, model: pxt.String, diarize: pxt.Bool = False, compute_type: pxt.String | None = None, language: pxt.String | None = None, task: pxt.String | None = None, chunk_size: pxt.Int | None = None, alignment_model_name: pxt.String | None = None, interpolate_method: pxt.String | None = None, return_char_alignments: pxt.Bool | None = None, diarization_model_name: pxt.String | None = None, num_speakers: pxt.Int | None = None, min_speakers: pxt.Int | None = None, max_speakers: pxt.Int | None = None ) -> pxt.Json ``` Transcribe an audio file using WhisperX. This UDF runs a transcription model *locally* using the WhisperX library, equivalent to the WhisperX `transcribe` function, as described in the [WhisperX library documentation](https://github.com/m-bain/whisperX). If `diarize=True`, then speaker diarization will also be performed. Several of the UDF parameters are only valid if `diarize=True`, as documented in the parameters list below. **Requirements:** * `pip install whisperx` **Parameters:** * **`audio`** (`pxt.Audio`): The audio file to transcribe. * **`model`** (`pxt.String`): The name of the model to use for transcription. * **`diarize`** (`pxt.Bool`): Whether to perform speaker diarization. * **`compute_type`** (`pxt.String | None`): The compute type to use for the model (e.g., `'int8'`, `'float16'`). If `None`, defaults to `'float16'` on CUDA devices and `'int8'` otherwise. * **`language`** (`pxt.String | None`): The language code for the transcription (e.g., `'en'` for English). * **`task`** (`pxt.String | None`): The task to perform (e.g., `'transcribe'` or `'translate'`). Defaults to `'transcribe'`. * **`chunk_size`** (`pxt.Int | None`): The size of the audio chunks to process, in seconds. Defaults to `30`. * **`alignment_model_name`** (`pxt.String | None`): The name of the alignment model to use. If `None`, uses the default model for the given language. Only valid if `diarize=True`. * **`interpolate_method`** (`pxt.String | None`): The method to use for interpolation of the alignment results. If not specified, uses the WhisperX default (`'nearest'`). Only valid if `diarize=True`. * **`return_char_alignments`** (`pxt.Bool | None`): Whether to return character-level alignments. Defaults to `False`. Only valid if `diarize=True`. * **`diarization_model_name`** (`pxt.String | None`): The name of the diarization model to use. Defaults to `pyannote/speaker-diarization-3.1`. Only valid if `diarize=True`. * **`num_speakers`** (`pxt.Int | None`): The number of speakers to expect in the audio. By default, the model with try to detect the number of speakers. Only valid if `diarize=True`. * **`min_speakers`** (`pxt.Int | None`): If specified, the minimum number of speakers to expect in the audio. Only valid if `diarize=True`. * **`max_speakers`** (`pxt.Int | None`): If specified, the maximum number of speakers to expect in the audio. Only valid if `diarize=True`. **Returns:** * `pxt.Json`: A dictionary containing the audio transcription, diarization (if enabled), and various other metadata. **Examples:** Add a computed column that applies the model `tiny.en` to an existing Pixeltable column `tbl.audio` of the table `tbl`: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} tbl.add_computed_column(result=transcribe(tbl.audio, model='tiny.en')) ``` Add a computed column that applies the model `tiny.en` to an existing Pixeltable column `tbl.audio` of the table `tbl`, with speaker diarization enabled, expecting at least 2 speakers: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} tbl.add_computed_column( result=transcribe( tbl.audio, model='tiny.en', diarize=True, min_speakers=2 ) ) ``` # yolox Source: https://docs.pixeltable.com/sdk/latest/yolox View Source on GitHub # module  pixeltable.functions.yolox YOLOX object detection functions. ## udf  yolo\_to\_coco() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf yolo_to_coco(detections: pxt.Json) -> pxt.Json[(Json[{'bbox': Json[(Int, ...)], 'category': Int}], ...)] ``` Converts the output of a YOLOX object detection model to COCO format. **Parameters:** * **`detections`** (`pxt.Json`): The output of a YOLOX object detection model, as returned by `yolox`. **Returns:** * `pxt.Json[(Json[{'bbox': Json[(Int, ...)], 'category': Int}], ...)]`: A dictionary containing the data from `detections`, converted to COCO format. **Examples:** Add a computed column that converts the output `tbl.detections` to COCO format, where `tbl.image` is the image for which detections were computed: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} tbl.add_computed_column( detections=yolox(tbl.image, model_id='yolox_m', threshold=0.8) ) tbl.add_computed_column(detections_coco=yolo_to_coco(tbl.detections)) ``` ## udf  yolox() ```python Signature theme={"theme":{"light":"light-plus","dark":"dark-plus"}} @pxt.udf yolox( images: pxt.Image, *, model_id: pxt.String, threshold: pxt.Float = 0.5 ) -> YoloxResponse ``` Computes YOLOX object detections for the specified image. `model_id` should reference one of the models defined in the [YOLOX documentation](https://github.com/Megvii-BaseDetection/YOLOX). **Requirements**: * `pip install pixeltable-yolox` **Parameters:** * **`model_id`** (`pxt.String`): one of: `yolox_nano`, `yolox_tiny`, `yolox_s`, `yolox_m`, `yolox_l`, `yolox_x` * **`threshold`** (`pxt.Float`): the threshold for object detection **Returns:** * `YoloxResponse`: A dictionary containing the output of the object detection model. **Examples:** Add a computed column that applies the model `yolox_m` to an existing Pixeltable column `tbl.image` of the table `tbl`: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} tbl.add_computed_column( detections=yolox(tbl.image, model_id='yolox_m', threshold=0.8) ) ``` # Computed Columns Source: https://docs.pixeltable.com/tutorials/computed-columns Open in Kaggle  Open in Colab  Download Notebook This documentation page is also available as an interactive notebook. You can launch the notebook in Kaggle or Colab, or download it for use with an IDE or local Jupyter installation, by clicking one of the above links. This notebook uses `pxt.create_table()` and `add_computed_column()` so you can run cells without a project. An app puts the same columns on a `TableModel` in `app.py` and creates the tables with `pxt schema update`. This guide introduces one of Pixeltable’s most essential and powerful concepts: computed columns. You’ll learn how to: * Add computed columns to a table * Use computed columns for complex operations such as image processing and model inference ## Prerequisites This guide assumes you’re familiar with: * Creating and managing tables * Inserting and querying data * Basic table operations If you’re new to Pixeltable, start with the [Tables and Data Operations](/tutorials/tables-and-data-operations) guide. First, let’s ensure the Pixeltable library is installed in your environment, along with the Huggingface `transformers` library. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} %pip install -qU pixeltable torch transformers timm ``` ### Computed Columns Let’s start with a simple example that illustrates the basic concepts behind computed columns. We’ll use a table of world population data for our example. Remember that you can import datasets into a Pixeltable table by using `pxt.create_table()` with the `source` parameter. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} import pixeltable as pxt pxt.create_dir('fundamentals', if_exists='ignore') pop_t = pxt.create_table( 'fundamentals/population', source='https://github.com/pixeltable/pixeltable/raw/release/docs/resources/world-population-data.csv', if_exists='replace', ) ```
  Connected to Pixeltable database at: postgresql+psycopg://postgres:@/pixeltable?host=/Users/asiegel/.pixeltable/pgdata
  Created directory 'fundamentals'.
  Created table 'population'.
  Inserting rows into \`population\`: 234 rows \[00:00, 6850.71 rows/s]
  Inserted 234 rows with 0 errors.
Also recall that `pop_t.head()` returns the first few rows of a table, and typing the table name `pop_t` by itself gives the schema. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} pop_t.head(5) ```
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} pop_t ```
Now let’s suppose we want to add a new column for the year-over-year population change from 2022 to 2023. You can `select()` such a quantity into a Pixeltable `Query`, giving it the name `yoy_change` (year-over-year change): ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} pop_t.select( pop_t.country, yoy_change=(pop_t.pop_2023 - pop_t.pop_2022) ).head(5) ```
A **computed column** is a way of turning such a selection into a new, permanent column of the table. Here’s how it works: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} pop_t.add_computed_column(yoy_change=(pop_t.pop_2023 - pop_t.pop_2022)) ```
  Added 234 column values with 0 errors.
  234 rows updated, 468 values computed.
As soon as the column is added, Pixeltable will (by default) automatically compute its value for all rows in the table, storing the results in the new column. If we now inspect the schema of `pop_t`, we see the new column and its definition. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} pop_t ```
The new column can be queried in the usual manner. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} pop_t.select(pop_t.country, pop_t.yoy_change).head(5) ```
The output is identical to the previous example, but now we’re retrieving the computed output from the database, instead of computing it on-the-fly. Computed columns can be “chained” with other computed columns. Here’s an example that expresses population change as a percentage: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} pop_t.add_computed_column( yoy_percent_change=(100 * pop_t.yoy_change / pop_t.pop_2022) ) ```
  Added 234 column values with 0 errors.
  234 rows updated, 468 values computed.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} pop_t ```
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} pop_t.select( pop_t.country, pop_t.yoy_change, pop_t.yoy_percent_change ).head(5) ```
Although computed columns appear superficially similar to Queries, there is a key difference. Because computed columns are a permanent part of the table, they will be automatically updated any time new data is added to the table. These updates will propagate through any other computed columns that are “downstream” of the new data, ensuring that the state of the entire data is kept up-to-date. In traditional data workflows, it is commonplace to recompute entire pipelines when the input dataset is changed or enlarged. In Pixeltable, by contrast, all updates are applied incrementally. When new data appear in a table or existing data are altered, Pixeltable will recompute only those rows that are dependent on the changed data. Let’s see how this works in practice. For purposes of illustration, we’ll add an entry for California to the table, as if it were a country. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} pop_t.insert(country='California', pop_2023=39110000, pop_2022=39030000) ```
  Inserting rows into \`population\`: 1 rows \[00:00, 228.35 rows/s]
  Inserted 1 row with 0 errors.
  1 row inserted, 5 values computed.
Observe that the computed columns `yoy_growth` and `yoy_percent_growth` have been automatically updated in response to the new data. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} pop_t.tail(5) ```
Remember that all tables in Pixeltable are persistent. This includes computed columns: when you create a computed column, its definition is stored in the database. You can think of computed columns as setting up a persistent compute workflow: if you close your notebook or restart your Python instance, computed columns (along with the relationships between them, and any data contained in them) will be preserved. ### Recomputing Columns From time to time you might need to recompute the data in an existing computed column. Perhaps the *code* for one of your UDFs has changed, and you want to recompute a column that uses that UDF in order to pick up the new logic. Or perhaps you want to re-run a nondeterministic computation such as model inference. The command to do this is `recompute_columns()`. It won’t do much in the current example, because all our computations are simple and deterministic, but for demonstration purposes here’s what it looks like: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} pop_t.recompute_columns(pop_t.yoy_change, pop_t.yoy_percent_change) ```
  Inserting rows into \`population\`: 235 rows \[00:00, 8795.92 rows/s]
  235 rows updated, 940 values computed.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} pop_t.tail(5) ```
As expected, it looks the same. If you modify the data that a computed column depends on, Pixeltable will recompute automatically; so recompute\_columns() is primarily useful when the input data remains the same, but your UDF business logic changes. ### A More Complex Example: Image Processing Pixeltable supports media data such as images alongside traditional structured data. Let’s explore an example that uses computed columns for image processing operations. In this example, we’ll create the table directly by providing a schema, rather than importing it from a CSV. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} t = pxt.create_table('fundamentals/image_ops', {'source': pxt.Image}) ```
  Created table 'image\_ops'.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} url_prefix = 'https://github.com/pixeltable/pixeltable/raw/release/docs/resources/images' images = ['000000000139.jpg', '000000000632.jpg', '000000000872.jpg'] t.insert({'source': f'{url_prefix}/{image}'} for image in images) ```
  Inserting rows into \`image\_ops\`: 3 rows \[00:00, 1133.39 rows/s]
  Inserted 3 rows with 0 errors.
  3 rows inserted, 6 values computed.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} t.collect() ```
What are some things we might want to do with these images? A fairly basic one is to extract metadata. Pixeltable provides the built-in UDF `get_metadata()`, which returns a dictionary with various metadata about the image. Let’s go ahead and make this a computed column. “UDF” is standard terminology in databases, meaning “User-Defined Function”. Technically speaking, the get\_metadata() function isn’t user-defined, it’s built in to the Pixeltable library. But we’ll consistently refer to Pixeltable functions as “UDFs” in order to clearly distinguish them from ordinary Python functions. Later in this guide, we’ll see how to turn (almost) any Python function into a Pixeltable UDF. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} t.add_computed_column(metadata=t.source.get_metadata()) t.collect() ```
  Added 3 column values with 0 errors.
Image operations, of course, can also return new images. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} t.add_computed_column(rotated=t.source.rotate(10)) ```
  Added 3 column values with 0 errors.
  3 rows updated, 3 values computed.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} t.collect() ```
Or, perhaps we want to rotate our images and fill them in with a transparent background rather than black. We can do this by chaining image operations, adding a transparency layer before doing the rotation. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} t.add_computed_column( rotated_transparent=t.source.convert('RGBA').rotate(10) ) t.collect() ```
  Added 3 column values with 0 errors.
In addition to get\_metadata(), convert(), and rotate(), Pixeltable has a sizable library of other common image operations that can be used as UDFs in computed columns. For the most part, the image UDFs are analogs of the operations provided by the Pillow library (in fact, Pixeltable is just using Pillow under the covers). You can read more about the provided image (and other) UDFs in the Pixeltable SDK Documentation. Let’s have a look at our table schema. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} t ```
### Image Detection In addition to simple operations like `rotate()` and `convert()`, the Pixeltable API includes UDFs for various off-the-shelf image models. Let’s look at one example: object detection using the ResNet-50 model. Model inference is a UDF too, and it can be inserted into a computed column like any other. This one may take a little more time to compute, since it involves first downloading the ResNet-50 model (if it isn’t already cached), then running inference on the images in our table. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} from pixeltable.functions.huggingface import detr_for_object_detection t.add_computed_column( detections=detr_for_object_detection( t.source, model_id='facebook/detr-resnet-50', threshold=0.8 ) ) ```
  Added 3 column values with 0 errors.
  3 rows updated, 3 values computed.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} t.select(t.source, t.detections).collect() ```
It’s great that the DETR model gave us so much information about the images, but it’s not exactly in human-readable form. Those are JSON structures that encode bounding boxes, confidence scores, and categories for each detected object. Let’s do something more useful with them: we’ll use Pixeltable’s `bboxes_draw()` API to superimpose bounding boxes on the images, using different colors to distinguish different object categories. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} from pixeltable.functions.vision import bboxes_draw t.add_computed_column( image_with_bb=bboxes_draw( t.source, t.detections.boxes, labels=t.detections.label_text, fill=True, ) ) t.select(t.source, t.image_with_bb).collect() ```
  Added 3 column values with 0 errors.
It can be a little hard to see what’s going on, so let’s zoom in on just one image. If you select a single image in a notebook, Pixeltable will enlarge its display: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} t.select(t.image_with_bb).head(1) ```
Let’s check in on our schema. We now have five computed columns, all derived from the single source column. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} t ```
And as always, when we add new data to the table, its computed columns are updated automatically. Let’s try this on a few more images. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} more_images = ['000000000108.jpg', '000000000885.jpg'] t.insert({'source': f'{url_prefix}/{image}'} for image in more_images) ```
  Inserting rows into \`image\_ops\`: 2 rows \[00:00, 944.77 rows/s]
  Inserted 2 rows with 0 errors.
  2 rows inserted, 14 values computed.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} t.select( t.source, t.image_with_bb, t.detections.label_text, t.metadata ).tail(2) ```
It bears repeating that Pixeltable is persistent! Anything you put into a table, including computed columns, will be saved in persistent storage. This includes inference outputs such as t.detections, as well as generated images such as t.image\_with\_bb. (Later we’ll see how to tune this behavior in cases where it might be undesirable to store everything, but the default behavior is that computed column output is always persisted.) ### Expressions Let’s have a closer look at that call to `bboxes_draw()` in the last example. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} bboxes_draw(t.source, t.detections.boxes, labels=t.detections.label_text, fill=True) ``` There are a couple of things going on. `bboxes_draw()` is, of course, a UDF, and its first argument is a column reference of the sort we’ve used many times now: `t.source`, the source image. The other two arguments are more than simple column references, though: they’re compound expressions that include the column reference `t.detections` along with a suffix (`.boxes` or `.label_text`) that tells Pixeltable to look inside the dictionary stored in `t.detections`. These are all examples of Pixeltable expressions. In fact, we’ve seen other types of Pixeltable expressions as well, without explicitly calling them out: * Calls to a UDF are expressions, such as `t.source.rotate(10)`, or the `bboxes_draw()` example above; * Arithmetic operations are expressions, such as year-over-year calculation in our first example: `100 * pop_t.yoy_change / pop_t.pop_2022`. ## Next Steps Learn more about working with Pixeltable: * [Queries and Expressions](/tutorials/queries-and-expressions) * [Tables and Data Operations](/tutorials/tables-and-data-operations) ## Ship it as an app This notebook used `create_table` and `add_computed_column`. On a `TableModel`, a computed column is an `=` on the class, not `add_computed_column()`: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} import pixeltable as pxt TableModel = pxt.model_base() class Population(TableModel, name='population'): country: pxt.String year_2022: pxt.Int year_2023: pxt.Int yoy_change = year_2023 - year_2022 ``` ```bash theme={"theme":{"light":"light-plus","dark":"dark-plus"}} pxt schema update app.py fundamentals ``` # Queries and Expressions Source: https://docs.pixeltable.com/tutorials/queries-and-expressions Open in Kaggle  Open in Colab  Download Notebook This documentation page is also available as an interactive notebook. You can launch the notebook in Kaggle or Colab, or download it for use with an IDE or local Jupyter installation, by clicking one of the above links. This notebook uses `pxt.create_table()` so you can run cells without a project. An app puts the same columns on a `TableModel` in `app.py` and creates the tables with `pxt schema update`. Once the table exists, `select()` is the same in an app. Expressions are the basic building blocks of Pixeltable. This guide explores how to use queries and expressions, including: * Different types of Pixeltable expressions * Column references and arithmetic operations * Function calls and media operations * The Pixeltable type system ## Prerequisites This guide assumes you’re familiar with: * Creating and managing tables * Basic table operations and queries * Computed columns If you’re new to these concepts, start with: * [Tables and Data Operations](/tutorials/tables-and-data-operations) * [Computed Columns](/tutorials/computed-columns) ## Understanding Expressions You can use Pixeltable expressions in queries: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} pop_t.select(yoy_change=(pop_t.pop_2023 - pop_t.pop_2022)).collect() ``` Or as computed columns that update automatically: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} pop_t.add_column(yoy_change=(pop_t.pop_2023 - pop_t.pop_2022)) ``` Both examples use the expression `pop_t.pop_2023 - pop_t.pop_2022`. You can also chain operations: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} t.source.convert('RGBA').rotate(10) ``` Or invoke models: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} detr_for_object_detection( t.source, model_id='facebook/detr-resnet-50', threshold=0.8 ) ``` You can include an expression in a `select()` statement to evaluate it dynamically, or in an `add_column()` statement to add it to the table schema as a computed column. To get started, let’s import the necessary libraries and set up a demo directory. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} %pip install -qU pixeltable datasets torch transformers ``` ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} import pixeltable as pxt pxt.drop_dir('demo', force=True) pxt.create_dir('demo') ```
  Connected to Pixeltable database at: postgresql+psycopg://postgres:@/pixeltable?host=/Users/asiegel/.pixeltable/pgdata
  Created directory 'demo'.
In this guide we’ll work with a subset of the MNIST dataset, a classic reference database of hand-drawn digits. A copy of the MNIST dataset is hosted on the Hugging Face datasets repository, so we can use `create_table()` with the `source` parameter to load it into a Pixeltable table. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} import datasets # Download the first 50 images of the MNIST dataset ds = datasets.load_dataset('ylecun/mnist', split='train[:50]') # Import them into a Pixeltable table t = pxt.create_table('demo/mnist', source=ds) ```
  Created table 'mnist'.
  Inserting rows into \`mnist\`: 50 rows \[00:00, 7516.67 rows/s]
  Inserted 50 rows with 0 errors.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} t.head(5) ```
### Column References The most basic type of expression is a **column reference**: that’s what you get when you type, say, `t.image`. An expression such as `t.image` by itself is just a Python object; it doesn’t contain any actual data, and no data will be loaded until you use the expression in a `select()` query or `add_column()` statement. Here’s what we get if we type `t.image` by itself: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} t.image ```
This is true of all Pixeltable expressions: we can freely create them and manipulate them in various ways, but no actual data will be loaded until we use them in a query. ### JSON Collections (Dicts and Lists) Data is commonly presented in JSON format: for example, API responses and model output often take the shape of JSON dictionaries or lists of dictionaries. Pixeltable has native support for JSON accessors. To demonstrate this, let’s add a computed column that runs an image classification model against the images in our dataset. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} from pixeltable.functions.huggingface import vit_for_image_classification t.add_computed_column( classification=vit_for_image_classification( t.image, model_id='farleyknight-org-username/vit-base-mnist' ) ) ```
  Added 50 column values with 0 errors.
  50 rows updated, 50 values computed.
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} t.select(t.image, t.classification).head(3) ```
We see that the output is returned as a dict containing three lists: the five most likely labels (classes) for the image, the corresponding text labels (in this case, just the string form of the class number), and the scores (confidences) of each prediction. The Pixeltable type of the `classification` column is `pxt.Json`: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} t ```
Pixeltable provides a range of operators on `Json`-typed output that behave just as you’d expect. To look up a key in a dictionary, use the syntax `t.classification['labels']`: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} t.select(t.classification['labels']).head(3) ```
You can also use a convenient “attribute” syntax for dictionary lookups. This follows the standard [JSONPath](https://en.wikipedia.org/wiki/JSONPath) expression syntax. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} t.select(t.classification.labels).head(3) ```
The “attribute” syntax isn’t fully general (it won’t work for dictionary keys that are not valid Python identifiers), but it’s handy when it works. `t.classification.labels` is another Pixeltable expression; you can think of it as saying, “do the `'labels'` lookup from every dictionary in the column `t.classification`, and return the result as a new column.” As before, the expression by itself contains no data; it’s the query that does the actual work of retrieving data. Here’s what we see if we just give the expression by itself, without a query: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} t.classification.labels ```
  classification.labels
Similarly, one can pull out a specific item in a list (for this model, we’re probably mostly interested in the first item anyway): ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} t.select(t.classification.labels[0]).head(3) ```
Or slice a list in the usual manner: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} t.select(t.classification.labels[:2]).head(3) ```
Pixeltable is resilient against out-of-bounds indices. If an index doesn’t exist for a particular row, you’ll get a `None` output for that row. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} t.select(t.classification.labels[10]).head(3) ```
As always, any expression can be used to create a computed column. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Use label_text to be consistent with t.label, which was given # to us as a string t.add_computed_column(pred_label=t.classification.label_text[0]) t ```
  Added 50 column values with 0 errors.
Finally, just as it’s possible to extract items from lists and dictionaries using Pixeltable expressions, you can also construct new lists and dictionaries: just package them up in the usual way. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} custom_dict = { # Keys must be strings; values can be any expressions 'ground_truth': t.label, 'prediction': t.pred_label, 'is_correct': t.label == t.pred_label, # You can also use constants as values 'engine': 'pixeltable', } t.select(t.image, custom_dict).head(5) ```
### UDF Calls UDF calls are another common type of expression. For example, we used one earlier when we added a model invocation to our workload: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} vit_for_image_classification( t.image, model_id='farleyknight-org-username/vit-base-mnist' ) ``` This calls the `vit_for_image_classification` UDF in the `pxt.functions.huggingface` module. Note that `vit_for_image_classification` is a Pixeltable UDF, not an ordinary Python function. You can think of a Pixeltable UDF as a function that operates on columns of data, iteratively applying an underlying operation to each row in the column (or columns). In this case, `vit_for_image_classification` operates on `t.image`, running the model against every image in the column. Notice that in addition to the column `t.image`, this call to `vit_for_image_classification` also takes a constant argument specifying the `model_id`. Any UDF call argument may be a constant, and the constant value simply means “use this value for every row being evaluated”. You can always compose Pixeltable expressions to form more complicated ones; here’s an example that runs the model against a 90-degree rotation of every image in the sample and extracts the label. Not surprisingly, the model doesn’t perform as well on the rotated images. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} rot_model_result = vit_for_image_classification( t.image.rotate(90), model_id='farleyknight-org-username/vit-base-mnist', ) t.select(t.image, rot_label=rot_model_result.labels[0]).head(5) ```
Note that we employed a useful trick here: we assigned an expression to the variable rot\_model\_result for later reuse. Every Pixeltable expression is a Python object, so you can freely assign them to variables, reuse them, compose them, and so on. Remember that nothing actually happens until the expression is used in a query - so in this example, setting the variable rot\_model\_result doesn’t itself result in any data being retrieved; that only happens later, when we actually use it in the select() query. There are a large number of built-in UDFs that ship with Pixeltable; you can always refer back to the [SDK Documentation](/sdk/latest/) for details. ### Method Calls Many built-in UDFs allow a convenient alternate syntax. The following two expressions are exactly equivalent: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} a = t.image.rotate(90) b = pxt.functions.image.rotate(t.image, 90) ``` `a` and `b` can always be used interchangeably in queries, with identical results. Just like in standard Python classes, whenever Pixeltable sees the **method call** `t.image.rotate(90)`, it interprets it as a **function call** `pxt.functions.image.rotate(self, 90)`, with (in this case) `self` equal to `t.image`. Any method call can also be written as a function call, but (just like in standard Python) not every function call can be written as a method call. For example, the following won’t work: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} t.image.vit_for_image_classification( model_id='farleyknight-org-username/vit-base-mnist' ) ``` That’s because `vit_for_image_classification` is part of the `pxt.functions.huggingface` module, not the core module `pxt.functions.image`. Most Pixeltable types have a corresponding **core module** of UDFs that can be used as method calls (`pxt.functions.image` for `Image`; `pxt.functions.string` for `String`; and so on), described fully in the [SDK Documentation](/sdk/latest/). ### Arithmetic and Boolean Operations Expressions can also be combined using standard arithmetic and boolean operators. As with everything else, arithmetic and boolean expressions are operations on columns that (when used in a query) are applied to every row. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} t.select(t.image, t.label, t.label == '4', t.label < '5').head(5) ```
When you use a `where` clause in a query, you’re giving it a Pixeltable expression, too (a boolean-valued one). ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} t.where(t.label == '4').select(t.image).show() ```
The following example shows how boolean expressions can be assigned to variables and used to form more complex expressions. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Reuse `rot_model_result` from above, extracting # the dominant label as a new expression rot_label = rot_model_result.label_text[0] # Select all the rows where the ground truth label is '5', # and the "rotated" version of the model got it wrong # (by returning something other than a '5') t.where((t.label == '5') & (rot_label != '5')).select( t.image, t.label, rot_label=rot_label ).show() ```
Notice that to form a logical “and”, we wrote ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} (t.label == '5') & (rot_label != '5') ``` using the operator `&` rather than `and`. Likewise, to form a logical “or”, we’d use `|` rather than `or`: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} (t.label == '5') | (rot_label != '5') ``` For logical negation: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} ~(t.label == '5') ``` This follows the convention used by other popular data-manipulation frameworks such as Pandas, and it’s necessary because the Python language does not allow the meanings of `and`, `or`, and `not` to be customized. There is one more instance of this to be aware of: to check whether an expression is `None`, it’s necessary to write (say) ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} t.label == None ``` rather than `t.label is None`, for the same reason. ### Arrays In addition to lists and dicts, Pixeltable also has built-in support for numerical arrays. A typical place where arrays show up is as the output of an embedding. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} from pixeltable.functions.huggingface import clip # Add a computed column that computes a CLIP embedding for each image t.add_computed_column( clip=clip(t.image, model_id='openai/clip-vit-base-patch32') ) t.select(t.image, t.clip).head(5) ```
  Added 50 column values with 0 errors.
The underlying Python type of `pxt.Array` is an ordinary NumPy array (`np.ndarray`), so that an array-typed column is a column of NumPy arrays (in this example, representing the embedding output of each image in the table). As with lists, arrays can be sliced in all the usual ways. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} t.select(t.clip[0], t.clip[5:10], t.clip[-3:]).head(5) ```
### Ad hoc UDFs with `apply` We’ve now seen the most commonly encountered Pixeltable expression types. There are a few other less commonly encountered expressions that are occasionally useful. You can use `apply` to map any Python function onto a column of data. You can think of `apply` as a quick way of constructing an “on-the-fly” UDF for one-off use. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} import numpy as np t.select(t.clip.apply(np.ndarray.dumps, col_type=pxt.String)).head(2) ``` Note, however, that if the function you’re `apply`ing doesn’t have type hints (as in the example here), you’ll need to specify the output column type explicitly. ### Type Conversion with `astype` Sometimes it’s useful to transform an expression of one type into a different type. For example, you can use `astype` to turn an expression of type `pxt.Json` into one of type `pxt.String`. This assumes that the value being converted is actually a string; otherwise, you’ll get an exception. Here’s an example: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Select the text in position 0 of `t.classification.label_text`; since # `t.classification.label_text` has type `pxt.Json`, so does # `t.classification.label_text[0]` t.classification.label_text[0].col_type ```
  Optional\[Json]
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Select the text in position 0 of `t.classification.label_text`, this time # cast as a `pxt.String` t.classification.label_text[0].astype(pxt.String).col_type ```
  Optional\[String]
### Column Properties Some `ColumnRef` expressions have additional useful properties. A media column (image, video, audio, or document) has the following two properties: * `localpath`: the media location on the local filesystem * `fileurl`: the original URL where the media resides (could be the same as `localpath`) ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} t.select(t.image, t.image.localpath).head(5) ```
Any computed column will have two additional properties, `errortype` and `errormsg`. These properties will usually be `None`. However, if the computed column was created with `on_error='ignore'` and an exception was encountered during column execution, then the properties will contain additional information about the exception. To demonstrate this feature, we’re going to deliberately trigger an exception in a computed column. The images in our example table are black and white, meaning they have only one color channel. If we try to extract a channel other than channel number `0`, we’ll get an exception. Ordinarily when we call `add_computed_column`, the exception is raised and the `add_computed_column` operation is aborted. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} t.add_computed_column(channel=t.image.getchannel(1)) ```
  Error: Error while evaluating computed column 'channel':
  band index out of range
  \[0;31m---------------------------------------------------------------------------\[0m
  \[0;31mValueError\[0m                                Traceback (most recent call last)
  File \[0;32m\~/Dropbox/workspace/pixeltable/pixeltable/pixeltable/exec/expr\_eval/evaluators.py:225\[0m, in \[0;36mFnCallEvaluator.eval\[0;34m(self, call\_args\_batch)\[0m
  \[1;32m    224\[0m \[38;5;28;01mtry\[39;00m:
  \[0;32m--> 225\[0m     item\[38;5;241m.\[39mrow\[\[38;5;28mself\[39m\[38;5;241m.\[39mfn\_call\[38;5;241m.\[39mslot\_idx] \[38;5;241m=\[39m \[38;5;28;43mself\[39;49m\[38;5;241;43m.\[39;49m\[43mscalar\_py\_fn\[49m\[43m(\[49m\[38;5;241;43m*\[39;49m\[43mitem\[49m\[38;5;241;43m.\[39;49m\[43margs\[49m\[43m,\[49m\[43m \[49m\[38;5;241;43m*\[39;49m\[38;5;241;43m\*\[39;49m\[43mitem\[49m\[38;5;241;43m.\[39;49m\[43mkwargs\[49m\[43m)\[49m
  \[1;32m    226\[0m \[38;5;28;01mexcept\[39;00m \[38;5;167;01mException\[39;00m \[38;5;28;01mas\[39;00m exc:

  File \[0;32m/opt/miniconda3/envs/pxt/lib/python3.10/site-packages/PIL/Image.py:2682\[0m, in \[0;36mImage.getchannel\[0;34m(self, channel)\[0m
  \[1;32m   2680\[0m         \[38;5;28;01mraise\[39;00m \[38;5;167;01mValueError\[39;00m(msg) \[38;5;28;01mfrom\[39;00m \[38;5;21;01me\[39;00m
  \[0;32m-> 2682\[0m \[38;5;28;01mreturn\[39;00m \[38;5;28mself\[39m\[38;5;241m.\[39m\_new(\[38;5;28;43mself\[39;49m\[38;5;241;43m.\[39;49m\[43mim\[49m\[38;5;241;43m.\[39;49m\[43mgetband\[49m\[43m(\[49m\[43mchannel\[49m\[43m)\[49m)

  \[0;31mValueError\[0m: band index out of range

  The above exception was the direct cause of the following exception:

  \[0;31mError\[0m                                     Traceback (most recent call last)
  Cell \[0;32mIn\[27], line 1\[0m
  \[0;32m----> 1\[0m \[43mt\[49m\[38;5;241;43m.\[39;49m\[43madd\_computed\_column\[49m\[43m(\[49m\[43mchannel\[49m\[38;5;241;43m=\[39;49m\[43mt\[49m\[38;5;241;43m.\[39;49m\[43mimage\[49m\[38;5;241;43m.\[39;49m\[43mgetchannel\[49m\[43m(\[49m\[38;5;241;43m1\[39;49m\[43m)\[49m\[43m)\[49m

  File \[0;32m\~/Dropbox/workspace/pixeltable/pixeltable/pixeltable/catalog/table.py:697\[0m, in \[0;36mTable.add\_computed\_column\[0;34m(self, stored, destination, print\_stats, on\_error, if\_exists, \*\*kwargs)\[0m
  \[1;32m    695\[0m \[38;5;28mself\[39m\[38;5;241m.\[39m\_verify\_column(new\_col)
  \[1;32m    696\[0m \[38;5;28;01massert\[39;00m \[38;5;28mself\[39m\[38;5;241m.\[39m\_tbl\_version \[38;5;129;01mis\[39;00m \[38;5;129;01mnot\[39;00m \[38;5;28;01mNone\[39;00m
  \[0;32m--> 697\[0m result \[38;5;241m+\[39m\[38;5;241m=\[39m \[38;5;28;43mself\[39;49m\[38;5;241;43m.\[39;49m\[43m\_tbl\_version\[49m\[38;5;241;43m.\[39;49m\[43mget\[49m\[43m(\[49m\[43m)\[49m\[38;5;241;43m.\[39;49m\[43madd\_columns\[49m\[43m(\[49m\[43m\[\[49m\[43mnew\_col\[49m\[43m]\[49m\[43m,\[49m\[43m \[49m\[43mprint\_stats\[49m\[38;5;241;43m=\[39;49m\[43mprint\_stats\[49m\[43m,\[49m\[43m \[49m\[43mon\_error\[49m\[38;5;241;43m=\[39;49m\[43mon\_error\[49m\[43m)\[49m
  \[1;32m    698\[0m FileCache\[38;5;241m.\[39mget()\[38;5;241m.\[39memit\_eviction\_warnings()
  \[1;32m    699\[0m \[38;5;28;01mreturn\[39;00m result

  File \[0;32m\~/Dropbox/workspace/pixeltable/pixeltable/pixeltable/catalog/table\_version.py:666\[0m, in \[0;36mTableVersion.add\_columns\[0;34m(self, cols, print\_stats, on\_error)\[0m
  \[1;32m    664\[0m         all\_cols\[38;5;241m.\[39mappend(undo\_col)
  \[1;32m    665\[0m \[38;5;66;03m# Add all columns\[39;00m
  \[0;32m--> 666\[0m status \[38;5;241m=\[39m \[38;5;28;43mself\[39;49m\[38;5;241;43m.\[39;49m\[43m\_add\_columns\[49m\[43m(\[49m\[43mall\_cols\[49m\[43m,\[49m\[43m \[49m\[43mprint\_stats\[49m\[38;5;241;43m=\[39;49m\[43mprint\_stats\[49m\[43m,\[49m\[43m \[49m\[43mon\_error\[49m\[38;5;241;43m=\[39;49m\[43mon\_error\[49m\[43m)\[49m
  \[1;32m    667\[0m \[38;5;66;03m# Create indices and their md records\[39;00m
  \[1;32m    668\[0m \[38;5;28;01mfor\[39;00m col, (idx, val\_col, undo\_col) \[38;5;129;01min\[39;00m index\_cols\[38;5;241m.\[39mitems():

  File \[0;32m\~/Dropbox/workspace/pixeltable/pixeltable/pixeltable/catalog/table\_version.py:732\[0m, in \[0;36mTableVersion.\_add\_columns\[0;34m(self, cols, print\_stats, on\_error)\[0m
  \[1;32m    730\[0m plan\[38;5;241m.\[39mopen()
  \[1;32m    731\[0m \[38;5;28;01mtry\[39;00m:
  \[0;32m--> 732\[0m     excs\_per\_col \[38;5;241m=\[39m \[38;5;28;43mself\[39;49m\[38;5;241;43m.\[39;49m\[43mstore\_tbl\[49m\[38;5;241;43m.\[39;49m\[43mload\_column\[49m\[43m(\[49m\[43mcol\[49m\[43m,\[49m\[43m \[49m\[43mplan\[49m\[43m,\[49m\[43m \[49m\[43mon\_error\[49m\[43m \[49m\[38;5;241;43m==\[39;49m\[43m \[49m\[38;5;124;43m'\[39;49m\[38;5;124;43mabort\[39;49m\[38;5;124;43m'\[39;49m\[43m)\[49m
  \[1;32m    733\[0m \[38;5;28;01mexcept\[39;00m sql\_exc\[38;5;241m.\[39mDBAPIError \[38;5;28;01mas\[39;00m exc:
  \[1;32m    734\[0m     Catalog\[38;5;241m.\[39mget()\[38;5;241m.\[39mconvert\_sql\_exc(exc, \[38;5;28mself\[39m\[38;5;241m.\[39mid, \[38;5;28mself\[39m\[38;5;241m.\[39mhandle, convert\_db\_excs\[38;5;241m=\[39m\[38;5;28;01mTrue\[39;00m)

  File \[0;32m\~/Dropbox/workspace/pixeltable/pixeltable/pixeltable/store.py:247\[0m, in \[0;36mStoreBase.load\_column\[0;34m(self, col, exec\_plan, abort\_on\_exc)\[0m
  \[1;32m    245\[0m \[38;5;28;01mif\[39;00m abort\_on\_exc \[38;5;129;01mand\[39;00m row\[38;5;241m.\[39mhas\_exc():
  \[1;32m    246\[0m     exc \[38;5;241m=\[39m row\[38;5;241m.\[39mget\_first\_exc()
  \[0;32m--> 247\[0m     \[38;5;28;01mraise\[39;00m excs\[38;5;241m.\[39mError(\[38;5;124mf\[39m\[38;5;124m'\[39m\[38;5;124mError while evaluating computed column \[39m\[38;5;132;01m\{\[39;00mcol\[38;5;241m.\[39mname\[38;5;132;01m!r}\[39;00m\[38;5;124m:\[39m\[38;5;130;01m\n\[39;00m\[38;5;132;01m\{\[39;00mexc\[38;5;132;01m}\[39;00m\[38;5;124m'\[39m) \[38;5;28;01mfrom\[39;00m \[38;5;21;01mexc\[39;00m
  \[1;32m    248\[0m table\_row, num\_row\_exc \[38;5;241m=\[39m row\_builder\[38;5;241m.\[39mcreate\_store\_table\_row(row, \[38;5;28;01mNone\[39;00m, row\[38;5;241m.\[39mpk)
  \[1;32m    249\[0m num\_excs \[38;5;241m+\[39m\[38;5;241m=\[39m num\_row\_exc

  \[0;31mError\[0m: Error while evaluating computed column 'channel':
  band index out of range
But if we use `on_error='ignore'`, the exception will be logged in the column properties instead. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} t.add_computed_column(channel=t.image.getchannel(1), on_error='ignore') ```
  Added 50 column values with 50 errors.
  50 rows updated, 50 values computed, 50 exceptions.
Notice that the update status informs us that there were 50 errors. If we query the table, we see that the column contains only `None` values, but the `errortype` and `errormsg` fields contain details of the error. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} t.select( t.image, t.channel, t.channel.errortype, t.channel.errormsg ).head(5) ```
More details on Pixeltable’s error handling can be found in the [External Files](/platform/external-files) guide. ## The Pixeltable Type System We’ve seen that every column and every expression in Pixeltable has an associated **Pixeltable type**. In this section, we’ll briefly survey the various Pixeltable types and their uses. Here are all the supported types and their corresponding Python types:
The Python type is what you’ll get back if you query an expression of the given Pixeltable type. For `pxt.Json`, it can be any of `str`, `int`, `float`, `bool`, `list`, or `dict`. pxt.Audio, pxt.Video, and pxt.Document all correspond to the Python type str. This is because those types are represented by file paths that reference the media in question. When you query for, say, t.select(t.video\_col), you’re guaranteed to get a file path on the local filesystem (Pixeltable will download and cache a local copy of the video if necessary to ensure this). If you want the original URL, use t.video\_col.fileurl instead. Several types can be **specialized** to constrain the allowable data in a column. * `pxt.Image` can be specialized with a resolution and/or an image mode: * `pxt.Image[(300,200)]` - images with width 300 and height 200 * `pxt.Image['RGB']` - images with mode `'RGB'`; see the [PIL Documentation](https://pillow.readthedocs.io/en/stable/handbook/concepts.html) for the full list * `pxt.Image[(300,200), 'RGB']` - combines the above constraints * `pxt.Array` can be specialized with a shape and/or a dtype: * `pxt.Array[pxt.Float]` - arrays with dtype `pxt.Float` * `pxt.Array[(64,64,3), pxt.Float]` - 3-dimensional arrays with dtype `pxt.Float` and 64x64x3 shape If we look at the structure of our table now, we see examples of specialized image and array types. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} t ```
`t.clip` has type `pxt.Array[(512,), pxt.Float]`, since the output of the embedding is always a 1x512 array. `t.channel` has type `Image['L']`, since it’s always an `'L'` mode (1-channel) image. You can freely use pxt.Image by itself to mean “any image, without constraints”, but numerical arrays must always specify a shape and a dtype; pxt.Array by itself will raise an error. Array shapes follow standard numpy conventions: a shape is a tuple of integers, such as (512,) or (64,64,3). A None may be used in place of an integer to indicate an unconstrained size for that dimension, as in (None,None,3) (3-dimensional array with two unconstrained dimensions), or simply (None,) (unconstrained 1-dimensional array). ## Ship it as an app Once the table exists, `select()` is the same in an app. To create this table from a file, put the columns on a `TableModel` (CSV/HF import stays `create_table(..., source=)` in notebooks): ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} import pixeltable as pxt TableModel = pxt.model_base() class Mnist(TableModel, name='mnist'): image: pxt.Image label: pxt.Int ``` ```bash theme={"theme":{"light":"light-plus","dark":"dark-plus"}} pxt schema update app.py demo ``` # Tables and Data Operations Source: https://docs.pixeltable.com/tutorials/tables-and-data-operations Open in Kaggle  Open in Colab  Download Notebook This documentation page is also available as an interactive notebook. You can launch the notebook in Kaggle or Colab, or download it for use with an IDE or local Jupyter installation, by clicking one of the above links. This notebook uses `pxt.create_table()` so you can run cells without a project. An app puts the same columns on a `TableModel` in `app.py` and creates the tables with `pxt schema update`. This guide shows you how to: * Create and manage tables: Understand Pixeltable’s table structure, create and modify tables, and work with table schemas * Manipulate data: Insert, update, and delete data within tables, and retrieve data from tables into Python variables * Filter and select data: Use `where()`, `select()`, and `order_by()` to query for specific rows and columns * Import data from CSV files and other file types First, let’s ensure the Pixeltable library is installed in your environment. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} %pip install -qU pixeltable ``` ### Tables All data in Pixeltable is stored in tables. At a high level, a Pixeltable table behaves similarly to an ordinary SQL database table, but with many additional capabilities to support complex AI workflows. We’ll introduce those advanced capabilities gradually throughout this tutorial; in this section, the focus is on basic table and data operations. Tables in Pixeltable are grouped into **directories**, which are simply user-defined namespaces. The following command creates a new directory, `fundamentals`, which we’ll use to store the tables in our tutorial. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} import pixeltable as pxt # First we delete the `fundamentals` directory and all its contents (if # it exists), in order to ensure a clean environment for the tutorial. pxt.drop_dir('fundamentals', force=True) # Now we create the directory. pxt.create_dir('fundamentals') ```
  Connected to Pixeltable database at: postgresql+psycopg://postgres:@/pixeltable?host=/Users/asiegel/.pixeltable/pgdata
  Created directory 'fundamentals'.
Now let’s create our first table. To create a table, we must give it a name and a **schema** that describes the table structure. Note that prefacing the name with `fundamentals` causes it to be placed in our newly-created directory. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} films_t = pxt.create_table( 'fundamentals/films', {'film_name': pxt.String, 'year': pxt.Int, 'revenue': pxt.Float}, ) ```
  Created table 'films'.
To insert data into a table, we use the `insert()` method, passing it a list of Python dicts. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} films_t.insert( [ {'film_name': 'Jurassic Park', 'year': 1993, 'revenue': 1037.5}, {'film_name': 'Titanic', 'year': 1997, 'revenue': 2257.8}, { 'film_name': 'Avengers: Endgame', 'year': 2019, 'revenue': 2797.5, }, ] ) ```
  Inserting rows into \`films\`: 3 rows \[00:00, 572.84 rows/s]
  Inserted 3 rows with 0 errors.
  3 rows inserted, 3 values computed.
If you’re inserting just a single row, you can use an alternate syntax that is sometimes more convenient. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} films_t.insert( [{'film_name': 'Inside Out 2', 'year': 2024, 'revenue': 1462.7}] ) ```
  Inserting rows into \`films\`: 1 rows \[00:00, 318.76 rows/s]
  Inserted 1 row with 0 errors.
  1 row inserted, 1 value computed.
We can peek at the data in our table with the `collect()` method, which retrieves all the rows in the table. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} films_t.collect() ```
Pixeltable also provides `update()` and `delete()` methods for modifying and removing data from a table; we’ll see examples of them shortly. ### Filtering and Selecting Data Often you want to select only certain rows and/or certain columns in a table. You can do this with the `where()` and `select()` methods. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} films_t.where(films_t.revenue >= 2000.0).collect() ```
```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} films_t.select(films_t.film_name, films_t.year).collect() ```
Note the expressions that appear inside the calls to `where()` and `select()`, such as `films_t.year`. These are **column references** that point to specific columns within a table. In place of `films_t.year`, you can also use dictionary syntax and type `films_t['year']`, which means exactly the same thing but is sometimes more convenient. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} films_t.select(films_t['film_name'], films_t['year']).collect() ```
In addition to selecting columns directly, you can use column references inside various kinds of expressions. For example, our `revenue` numbers are given in millions of dollars. Let’s say we wanted to select revenue in thousands of dollars instead; we could do that as follows: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} films_t.select(films_t.film_name, films_t.revenue * 1000).collect() ```
Note that since we selected an abstract expression rather than a specific column, Pixeltable gave it the generic name `col_1`. You can assign it a more informative name with Python keyword syntax: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} films_t.select( films_t.film_name, revenue_thousands=films_t.revenue * 1000 ).collect() ```
### Tables are Persistent This is a good time to mention a few key differences between Pixeltable tables and other familiar datastructures, such as Python dicts or Pandas dataframes. First, **Pixeltable is persistent. Unlike in-memory Python libraries such as Pandas, Pixeltable is a database**. When you reset a notebook kernel or start a new Python session, you’ll have access to all the data you’ve stored previously in Pixeltable. Let’s demonstrate this by using the IPython `%reset -f` command to clear out all our notebook variables, so that `films_t` is no longer defined. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} %reset -f films_t.collect() # Throws an exception now ```
  NameError: name 'films\_t' is not defined
  \[0;31m---------------------------------------------------------------------------\[0m
  \[0;31mNameError\[0m                                 Traceback (most recent call last)
  Cell \[0;32mIn\[11], line 2\[0m
  \[1;32m      1\[0m get\_ipython()\[38;5;241m.\[39mrun\_line\_magic(\[38;5;124m'\[39m\[38;5;124mreset\[39m\[38;5;124m'\[39m, \[38;5;124m'\[39m\[38;5;124m-f\[39m\[38;5;124m'\[39m)
  \[0;32m----> 2\[0m \[43mfilms\_t\[49m\[38;5;241m.\[39mcollect()  \[38;5;66;03m# Throws an exception now\[39;00m

  \[0;31mNameError\[0m: name 'films\_t' is not defined
The `films_t` variable (along with all other variables in our Python session) has been cleared out - but that’s ok, because it wasn’t the source of record for our data. The `films_t` variable is just a reference to the underlying database table. We can recover it with the `get_table` command, referencing the `films` table by name. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} import pixeltable as pxt films_t = pxt.get_table('fundamentals/films') films_t.collect() ```
You can always get a list of existing tables with the Pixeltable `pxt.ls()` command. Let’s use it to see the contents of the `fundamentals` directory. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} pxt.ls(path='fundamentals') ```
Note that if you’re running Pixeltable on colab or kaggle, the database will persist only for as long as your colab/kaggle session remains active. If you’re running it locally or on your own server, then your database will persist indefinitely (until you actively delete it). ### Tables are Typed The second major difference is that **Pixeltable is strongly typed**. Because Pixeltable is a database, every column has a data type: that’s why we specified `String`, `Int`, and `Float` for the three columns when we created the table. These **type specifiers** are *mandatory* when creating tables, and they become part of the table schema. You can always see the table schema with the `describe()` method. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} films_t.describe() ```
In a notebook, you can also just type `films_t` to see the schema; its output is identical to `films_t.describe()`. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} films_t ```
In addition to String, Int, and Float, Pixeltable provides several additional data types:
  • Bool, whose values are True or False;
  • Array for numerical arrays;
  • Json, for lists or dicts that correspond to valid JSON structures; and
  • The media types Image, Video, Audio, and Document.
  • We’ll see examples of each of these types later in this guide. Besides the column names and types, there’s a third element to the schema, `Computed With`. To learn more about this, see the [Computed Columns](/tutorials/computed-columns) guide. All of the methods we’ve discussed so far, such as `insert()` and `get_table()`, are documented in the [Pixeltable SDK](/sdk/latest/) Documentation. The following pages are particularly relevant: * [pixeltable](/sdk/latest/pixeltable) package reference * [pxt.Table](/sdk/latest/table) class reference ### A Real-World Example: Earthquake Data Now let’s dive a little deeper into Pixeltable’s data operations. To showcase all the features, it’ll be helpful to have a real-world dataset, rather than our toy dataset with four movies. The dataset we’ll be using consists of Earthquake data drawn from the US Geological Survey: all recorded Earthquakes that occurred within 100 km of Seattle, Washington, between January 1, 2023 and June 30, 2024. The dataset is in CSV format, and we can load it into Pixeltable by using `create_table()` with the `source` parameter, which creates a new Pixeltable table from the contents of a CSV file. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} eq_t = pxt.create_table( 'fundamentals/earthquakes', # Name for the new table source='https://raw.githubusercontent.com/pixeltable/pixeltable/release/docs/resources/earthquakes.csv', primary_key='id', # Column 'id' is the primary key schema_overrides={ 'timestamp': pxt.Timestamp }, # Interpret column 3 as a timestamp ) ```
      Created table 'earthquakes'.
      Inserting rows into \`earthquakes\`: 1823 rows \[00:00, 19554.24 rows/s]
      Inserted 1823 rows with 0 errors.
    
    In Pixeltable, you can always import external data by giving a URL instead of a local file path. This applies to CSV datasets, media files (such images and video), and other types of content. The URL will often be an http\:// URL, but it can also be an s3:// URL referencing an S3 bucket. Pixeltable’s create\_table() function with the source parameter can import data from various formats including CSV, Excel, and Hugging Face datasets. You can also use source to import from a Pandas dataframe. For more details, see the pixeltable.io package reference. Let’s have a peek at our new dataset. The dataset contains 1823 rows, and we probably don’t want to display them all at once. We can limit our query to fewer rows with the `limit()` method. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} eq_t.limit(5).collect() ```
    A different way of achieving something similar is to use the `head()` and `tail()` methods. Pixeltable keeps track of the insertion order of all its data, and `head()` and `tail()` will always return the *earliest inserted* and *most recently inserted* rows in a table, respectively. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} eq_t.head(5) ```
    ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} eq_t.tail(5) ```
    head(n) and limit(n).collect() appear similar in this example. But head() always returns the earliest rows in a table, whereas limit() makes no promises about the ordering of its results (unless you specify an order\_by() clause - more on this below). Let’s also peek at the schema: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} eq_t.describe() ```
    Note that while specifying a schema is mandatory when *creating* a table, it’s not always required when *importing* data. This is because Pixeltable uses the structure of the imported data to infer the column types, when feasible. You can always override the inferred column types with the `schema_overrides` parameter of `import_csv()`. The following examples showcase some common data operations. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} eq_t.count() # Number of rows in the table ```
      1823
    
    ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # 5 highest-magnitude earthquakes eq_t.order_by(eq_t.magnitude, asc=False).limit(5).collect() ```
    ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} from datetime import datetime # 5 highest-magnitude earthquakes in Q3 2023 eq_t.where( (eq_t.timestamp >= datetime(2023, 6, 1)) & (eq_t.timestamp < datetime(2023, 10, 1)) ).order_by(eq_t.magnitude, asc=False).limit(5).collect() ```
    Note that Pixeltable uses Pandas-like operators for filtering data: the expression ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} (eq_t.timestamp >= datetime(2023, 6, 1)) & (eq_t.timestamp < datetime(2023, 10, 1)) ``` means *both* conditions must be true; similarly (say), ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} (eq_t.timestamp < datetime(2023, 6, 1)) | (eq_t.timestamp >= datetime(2023, 10, 1)) ``` would mean *either* condition must be true. You can also use the special `isin` operator to select just those values that appear within a particular list: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Earthquakes with specific ids eq_t.where(eq_t.id.isin([123, 456, 789])).collect() ```
    In addition to basic operators like `>=` and `isin`, a Pixeltable `where` clause can also contain more complex operations. For example, the `location` column in our dataset is a string that contains a lot of information, but in a relatively unstructured way. Suppose we wanted to see all Earthquakes in the vicinity of Rainier, Washington; one way to do this is with the `contains()` method: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # All earthquakes in the vicinity of Rainier eq_t.where(eq_t.location.contains('Rainier')).collect() ```
    Pixeltable also supports various **aggregators**; here’s an example showcasing two fairly simple ones, `max()` and `min()`: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Min and max ids eq_t.select( min=pxt.functions.min(eq_t.id), max=pxt.functions.max(eq_t.id) ).collect() ```
    To learn more about Pixeltable functions and expressions, see the [Computed Columns](/tutorials/computed-columns) guide. They’re also exhaustively documented in the [Pixeltable SDK Documentation](/sdk/latest). ### Extracting Data from Tables into Python/Pandas Sometimes it’s handy to pull out data from a table into a Python object. We’ve actually already done this; the call to `collect()` returns an in-memory result set, which we can then dereference in various ways. For example: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} result = eq_t.limit(5).collect() result[0] # Get the first row of the results as a dict ```
      \{'id': 0,
       'magnitude': 1.15,
       'location': '10 km NW of Belfair, Washington',
       'timestamp': datetime.datetime(2023, 1, 1, 8, 10, 37, 50000, tzinfo=zoneinfo.ZoneInfo(key='America/Los\_Angeles')),
       'longitude': -122.93,
       'latitude': 47.51}
    
    ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} result[ 'timestamp' ] # Get a list of the `timestamp` field of all the rows that were queried ```
      \[datetime.datetime(2023, 1, 1, 8, 10, 37, 50000, tzinfo=zoneinfo.ZoneInfo(key='America/Los\_Angeles')),
       datetime.datetime(2023, 1, 2, 1, 2, 43, 950000, tzinfo=zoneinfo.ZoneInfo(key='America/Los\_Angeles')),
       datetime.datetime(2023, 1, 2, 12, 5, 1, 420000, tzinfo=zoneinfo.ZoneInfo(key='America/Los\_Angeles')),
       datetime.datetime(2023, 1, 2, 12, 45, 14, 220000, tzinfo=zoneinfo.ZoneInfo(key='America/Los\_Angeles')),
       datetime.datetime(2023, 1, 2, 13, 19, 27, 200000, tzinfo=zoneinfo.ZoneInfo(key='America/Los\_Angeles'))]
    
    ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} df = result.to_pandas() # Convert the result set into a Pandas dataframe df['magnitude'].describe() ```
      count    5.000000
      mean     0.744000
      std      0.587988
      min      0.200000
      25%      0.290000
      50%      0.520000
      75%      1.150000
      max      1.560000
      Name: magnitude, dtype: float64
    
    `collect()` without a preceding `limit()` returns the entire contents of a query or table. Be careful! For very large tables, this could result in out-of-memory errors. In this example, the 1823 rows in the table fit comfortably into a dataframe. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} df = eq_t.collect().to_pandas() df['magnitude'].describe() ```
      count    1823.000000
      mean        0.900378
      std         0.625492
      min        -0.830000
      25%         0.420000
      50%         0.850000
      75%         1.310000
      max         4.300000
      Name: magnitude, dtype: float64
    
    ### Adding Columns Like other database tables, Pixeltable tables aren’t fixed entities: they’re meant to evolve over time. Suppose we want to add a new column to hold user-specified comments about particular earthquake events. We can do this with the `add_column()` method: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} eq_t.add_column(note=(pxt.String | None)) ```
      Added 1823 column values with 0 errors.
      1823 rows updated, 1823 values computed.
    
    Here, `note` is the column name, and `pxt.String | None` specifies the type of the new column. Note that the new column *must* be nullable (`| None`) so that Pixeltable can fill in empty values for existing rows. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} eq_t.add_column(contact_email=(pxt.String | None)) ```
      Added 1823 column values with 0 errors.
      1823 rows updated, 1823 values computed.
    
    Let’s have a look at the revised schema. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} eq_t.describe() ```
    ### Updating Rows in a Table Table rows can be modified and deleted with the SQL-like `update()` and `delete()` commands. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Add a comment to records with IDs 123 and 127 ( eq_t.where(eq_t.id.isin([121, 123])).update( { 'note': 'Still investigating.', 'contact_email': 'contact@pixeltable.com', } ) ) ```
      Inserting rows into \`earthquakes\`: 2 rows \[00:00, 366.84 rows/s]
      2 rows updated, 4 values computed.
    
    ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} eq_t.where(eq_t.id >= 120).select( eq_t.id, eq_t.magnitude, eq_t.note, eq_t.contact_email ).head(5) ```
    `update()` can also accept an expression, rather than a constant value. For example, suppose we wanted to shorten the location strings by replacing every occurrence of `Washington` with `WA`. One way to do this is with an `update()` clause, using a Pixeltable expression with the `replace()` method. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} eq_t.update({'location': eq_t.location.replace('Washington', 'WA')}) ```
      Inserting rows into \`earthquakes\`: 1823 rows \[00:00, 21494.07 rows/s]
      1823 rows updated, 1823 values computed.
    
    ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} eq_t.head(5) ```
    Notice that in all cases, the `update()` clause takes a Python dictionary, but its values can be either constants such as `'contact@pixeltable.com'`, or more complex expressions such as `eq_t.location.replace('Washington', 'WA')`. Also notice that if `update()` appears without a `where()` clause, then every row in the table will be updated, as in the preceding example. ### Batch Updates The `batch_update()` method provides an alternative way to update multiple rows with different values. With a `batch_update()`, the contents of each row are specified by individual `dict`s, rather than according to a formula. Here’s a toy example that shows `batch_update()` in action. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} updates = [ {'id': 500, 'note': 'This is an example note.'}, {'id': 501, 'note': 'This is a different note.'}, {'id': 502, 'note': 'A third note, unrelated to the others.'}, ] eq_t.batch_update(updates) ```
      Inserting rows into \`earthquakes\`: 3 rows \[00:00, 984.58 rows/s]
      3 rows updated, 3 values computed.
    
    ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} eq_t.where(eq_t.id >= 500).select( eq_t.id, eq_t.magnitude, eq_t.note, eq_t.contact_email ).head(5) ```
    ### Deleting Rows To delete rows from a table, use the `delete()` method. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Delete all rows in 2024 eq_t.where(eq_t.timestamp >= datetime(2024, 1, 1)).delete() ```
      587 rows deleted.
    
    ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} eq_t.count() # How many are left after deleting? ```
      1236
    
    Don’t forget to specify a `where()` clause when using `delete()`! If you run `delete()` without a `where()` clause, the entire contents of the table will be deleted. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} eq_t.delete() ```
      1236 rows deleted.
    
    ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} eq_t.count() ```
      0
    
    ### Table Versioning Every table in Pixeltable is versioned: some or all of its modification history is preserved. We’ve seen a reference to this already; `pxt.ls()` will show the most recent version along with each table it lists. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} pxt.ls('fundamentals') ```
    To see the version history of a particular table: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} eq_t.history() ```
    If you ever make a mistake, you can always call `revert()` to undo the most recent change to a table and roll back to the previous version. Let’s try it out: we’ll use it to revert the successive `delete()` calls that we just executed. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} eq_t.revert() ``` ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} eq_t.count() ```
      1236
    
    ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} eq_t.revert() ``` ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} eq_t.count() ```
      1823
    
    Be aware: calling revert() cannot be undone! ### Multimodal Data In addition to the structured data we’ve been exploring so far, Pixeltable has native support for **media types**: images, video, audio, and unstructured documents such as pdfs. Media support is one of Pixeltable’s core capabilities. Here’s an example showing how media data lives side-by-side with structured data in Pixeltable. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Add a new column of type `Image` eq_t.add_column(map_image=(pxt.Image | None)) eq_t.describe() ```
      Added 1823 column values with 0 errors.
    
    ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Update the row with id == 1002, adding an image to the `map_image` column eq_t.where(eq_t.id == 1002).update( { 'map_image': 'https://raw.githubusercontent.com/pixeltable/pixeltable/release/docs/resources/port-townsend-map.jpeg' } ) ```
      Inserting rows into \`earthquakes\`: 1 rows \[00:00, 192.79 rows/s]
      1 row updated, 1 value computed.
    
    Note that in Pixeltable, you can always insert images into a table by giving the file path or URL of the image (as a string). It’s not necessary to load the image first; Pixeltable will manage the loading and caching of images in the background. The same applies to other media data such as documents and videos. Pixeltable will also embed image thumbnails in your notebook when you do a query: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} eq_t.where(eq_t.id >= 1000).select( eq_t.id, eq_t.magnitude, eq_t.location, eq_t.map_image ).head(5) ```
    ### Directory Hierarchies So far we’ve only seen an example of a single directory with a table inside it, but one can also put directories inside other directories, in whatever fashion makes the most sense for a given application. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} pxt.create_dir('fundamentals/subdir') pxt.create_dir('fundamentals/subdir/subsubdir') pxt.create_table( 'fundamentals/subdir/subsubdir/my_table', {'my_col': pxt.String} ) ```
      Created directory 'fundamentals/subdir'.
      Created directory 'fundamentals/subdir/subsubdir'.
      Created table 'my\_table'.
    
    ### Deleting Columns, Tables, and Directories `drop_column()`, `drop_table()`, and `drop_dir()` are used to delete columns, tables, and directories, respectively. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Delete the `contact_email` column eq_t.drop_column('contact_email') ``` ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} eq_t.describe() ```
    ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Delete the entire table (cannot be reverted!) pxt.drop_table('fundamentals/earthquakes') ``` ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} # Delete the entire directory and all its contents, including any nested # subdirectories (cannot be reverted) pxt.drop_dir('fundamentals', force=True) ``` ## Next Steps Learn more about working with Pixeltable: * [Computed Columns](/tutorials/computed-columns) * [Queries and Expressions](/tutorials/queries-and-expressions) ## Ship it as an app This notebook used `create_table` so you can run cells without a project. Columns you added later with `add_column` belong on the class only after you write those fields. Starting schema in `app.py`: ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} import pixeltable as pxt TableModel = pxt.model_base() class Films(TableModel, name='films'): film_name: pxt.String year: pxt.Int revenue: pxt.Float ``` ```bash theme={"theme":{"light":"light-plus","dark":"dark-plus"}} pxt schema update app.py fundamentals ``` # Agentic workflows Source: https://docs.pixeltable.com/use-cases/agentic-workflows Insert a message. Tool calls run as columns. Insert a message. The model picks a tool, `invoke_tools` runs it, and both results are columns on that row. Create the tables with `pxt schema update app.py my_app`, then open them with `t = pxt.get_table('my_app.assistant')`. Cookbooks on this topic use `pxt.create_table()` so you can run cells without a project. An app puts the same columns on a `TableModel` in `app.py` and creates the tables with `pxt schema update`. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} import pixeltable as pxt import pixeltable.functions as pxtf from pixeltable.serving import FastAPIRouter TableModel = pxt.model_base() @pxt.udf def get_weather(city: str) -> str: return f'Weather in {city}: 72°F, sunny' tools = pxt.tools(get_weather) class Assistant(TableModel, name='assistant'): message: pxt.String response = pxtf.openai.chat_completions( messages=[{'role': 'user', 'content': message}], model='gpt-4o-mini', tools=tools, ) tool_output = pxtf.openai.invoke_tools(tools, response) ask = FastAPIRouter(name='ask') ask.add_insert_route( Assistant, path='/ask', inputs=[Assistant.message], outputs=[Assistant.tool_output] ) ``` ```bash theme={"theme":{"light":"light-plus","dark":"dark-plus"}} pxt schema update app.py my_app pxt service update app.py my_app ``` ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} assistant = pxt.get_table('my_app.assistant') assistant.insert([{'message': 'Weather in Paris?'}]) assistant.select(assistant.message, assistant.tool_output).collect() ``` `pxt service list` prints the URL. POST `/ask` with `{"message": "..."}`. Chat history is another table with an embedding index: [Agent memory](/howto/cookbooks/agents/pattern-agent-memory). MCP tools load with `pxt.mcp_udfs(...)` and go into the same `pxt.tools()` list. A full chat app: [`uvx pixeltable-new`](https://github.com/pixeltable/pixeltable-new) copies the [starter kit](/resources/starter-kit) agent. Pass `agent` as the last argument to `pxt schema update`. `/ask` needs `ANTHROPIC_API_KEY`. On Cloud, sign in with `pxt login`, or set `PIXELTABLE_API_KEY`. `pxt org create` provisions `main`. Then run `pxt db update`, `pxt schema update`, and `pxt service update` against `pxt://org:main`. [Pixeltable Cloud](/cloud). Search over chunks instead of tools: [RAG and live APIs](/use-cases/multimodal-backend). Coming from LangGraph: [Migrate](/howto/coming-from#langgraph). Register tools and run `invoke_tools` as a column. Patterns for agents as tables. Search over chunks instead of tools. POST `/ask` from the same file. # Media processing Source: https://docs.pixeltable.com/use-cases/media-processing Insert a video. Frames are rows. Export when the dataset is ready. Insert media. Iterators explode it into rows. Computed columns run on the new rows. Export when you are ready to train. Create the tables with `pxt schema update app.py my_app`, then open them with `t = pxt.get_table('my_app.videos')`. Cookbooks on this topic use `pxt.create_table()` so you can run cells without a project. An app puts the same columns on a `TableModel` in `app.py` and creates the tables with `pxt schema update`. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} import pixeltable as pxt import pixeltable.functions as pxtf TableModel = pxt.model_base() class Videos(TableModel, name='videos'): video: pxt.Video class Frames( TableModel, name='frames', base=Videos, iterator=pxtf.video.frame_iterator(Videos.video, fps=1), ): thumb = frame.resize((256, 256)) # type: ignore[name-defined] # iterator output ``` ```bash theme={"theme":{"light":"light-plus","dark":"dark-plus"}} pxt schema update app.py my_app ``` ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} videos = pxt.get_table('my_app.videos') videos.insert([{'video': 'demo.mp4'}]) frames = pxt.get_table('my_app.frames') frames.select(frames.thumb).head(10) ``` Add Whisper, YOLOX, and other models from the cookbooks as computed columns on these tables. When the rows look ready for training, export with `to_pytorch_dataset()`. Skip `pxt service update` on this path. [Self-hosting](/howto/deployment/overview). A video search app with endpoints: `uvx pixeltable-new myapp --video`, then pass `videointel` to `pxt schema update`. Live search over chunks: [RAG and live APIs](/use-cases/multimodal-backend). Frames as rows from a `pxt.Video` column. Transcripts as computed columns. `to_pytorch_dataset()` when the rows are ready. # Multimodal backend Source: https://docs.pixeltable.com/use-cases/multimodal-backend Insert a document. Chunks are rows. POST search. Insert a document. The iterator explodes it into chunks. The embedding index stays current. `pxt service update` starts the insert and search endpoints. Create the tables with `pxt schema update app.py my_app`, then open them with `t = pxt.get_table('my_app.docs')`. Cookbooks on this topic use `pxt.create_table()` so you can run cells without a project. An app puts the same columns on a `TableModel` in `app.py` and creates the tables with `pxt schema update`. ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} import pixeltable as pxt import pixeltable.functions as pxtf from pixeltable.serving import FastAPIRouter TableModel = pxt.model_base() embed = pxtf.huggingface.sentence_transformer.using( model_id='sentence-transformers/all-MiniLM-L6-v2' ) class Docs(TableModel, name='docs'): document: pxt.Document class Chunks( TableModel, name='chunks', base=Docs, iterator=pxtf.document.document_splitter( Docs.document, separators='token_limit', limit=300 ), ): __indexes__ = [ pxt.EmbeddingIndex(text, embedding=embed, name='chunks_embed') ] @pxt.query def search_docs(query_text: str): sim = Chunks.text.similarity(string=query_text) return Chunks.order_by(sim, asc=False).limit(10).select(Chunks.text, sim) api = FastAPIRouter(name='rag') api.add_insert_route( Docs, path='/docs', uploadfile_inputs=[Docs.document], outputs=[Docs.document], ) api.add_query_route(path='/search', query=search_docs, method='post') ``` ```bash theme={"theme":{"light":"light-plus","dark":"dark-plus"}} pxt schema update app.py my_app pxt service update app.py my_app ``` ```python theme={"theme":{"light":"light-plus","dark":"dark-plus"}} docs = pxt.get_table('my_app.docs') docs.insert([{'document': 'report.pdf'}]) ``` `pxt service list` prints the URL. POST `/docs` to insert. POST `/search` with `{"query_text": "..."}` to rank chunks. On Cloud, sign in with `pxt login`, or set `PIXELTABLE_API_KEY`. `pxt org create` provisions `main`. Then run `pxt db update`, `pxt schema update`, and `pxt service update` against `pxt://org:main`. [Pixeltable Cloud](/cloud). Export those tables for training: [Datasets from media](/use-cases/media-processing). Tool calls as columns: [Agents](/use-cases/agentic-workflows). POST `/docs` to insert. Chunks stay current. Rank chunks with `{"query_text": "..."}`. The same index pattern on images. Insert and search routes from the same file.