How to
How do I add computed columns with LLM calls in Python?
A computed column is a value produced by an expression or an LLM call when the row or its dependencies change, then cached. A declarative data pipeline is that schema: you name the columns, and the engine decides which rows run. Adding an LLM call is an assignment on the class, not a job you schedule around a table.
Updated · Part of How do I ... in Python?
Before you start
About 10 min.
- Python 3.11+
- pip install -U 'pixeltable[serve]'
- pxt init in the project root
- An OpenAI API key for gpt-4o-mini
1. Start with the stored field the model will read
A computed column is an expression over other columns. The source text (or image, or document) is stored. The model output is derived.
import pixeltable as pxtfrom pixeltable.functions.openai import chat_completionsTableModel = pxt.model_base()class Docs(TableModel, name='docs'):text: pxt.String
2. Assign the LLM call on the class
Computed fields are assignments, not annotations. chat_completions returns a response object; .choices[0].message.content is the string you cache. Insert a row and that expression runs. Edit the text and dependents of that row recompute.
class Docs(TableModel, name='docs'):text: pxt.Stringsummary = chat_completions(messages=[{'role': 'user', 'content': text}],model='gpt-4o-mini',).choices[0].message.content
3. Chain another column on the cached output
A declarative data pipeline is a graph of these assignments. A second call can classify the summary. Unchanged rows keep both caches. You do not enqueue each step.
class Docs(TableModel, name='docs'):text: pxt.Stringsummary = chat_completions(messages=[{'role': 'user', 'content': text}],model='gpt-4o-mini',).choices[0].message.contentlabel = chat_completions(messages=[{'role': 'user','content': 'Classify as bug, feature, or question. Reply with one word.\n\n' + summary,}],model='gpt-4o-mini',).choices[0].message.content# pxt schema update app.py docs -f
Computed LLM columns versus a job around the table
| Computed column | A scheduled job | |
|---|---|---|
| You write | An assignment on the class | A worker that reads and writes rows |
| Runs when | The row or a dependency changes | A cron tick or a queue |
| Cache | Per row, with lineage | A column you overwrite by hand |
| Failure | That row, visible | A retry of the batch |
Where Pixeltable fits
In Pixeltable a computed column is an assignment on TableModel. chat_completions is one expression. Insert a row and the LLM runs; unchanged rows stay cached. That is a declarative data pipeline, not an Airflow task or a LangChain chain with no store. See Pixeltable vs LangChain and Pixeltable vs Supabase (edge functions are not this cache).
import pixeltable as pxtfrom pixeltable.functions.openai import chat_completionsTableModel = pxt.model_base()class Docs(TableModel, name='docs'):text: pxt.Stringsummary = chat_completions(messages=[{'role': 'user', 'content': text}],model='gpt-4o-mini',).choices[0].message.content
Questions
- Is a computed column an edge function?
- No. An edge function runs on a request and does not cache per row by default. A computed column stores the result with the row and recomputes when dependencies change. Pixeltable vs Supabase covers Neon/Supabase functions.
- Does every insert call the model again for old rows?
- No. Unchanged rows keep their cached values. A new or edited source column recomputes dependents of that row. That is incremental computation.
- Can the LLM see an image or a document?
- Yes, if the provider UDF accepts that type. Text is chat_completions on a string. Gemini generate_content accepts an image and a prompt as contents. Use the UDF that matches the modality.