add_cell
ChatGPTAdds CELLS to the plix's pipeline (each: label, its code body, its inputs / outputs ports). The plix's PIPELINE is its cells, drawn as ONE column in dependency order (a cell below what feeds it; ties in the pipeline's own order: a new cell lands last, reorder_cells sets it) — there are no positions. Every cell write takes expectedRev, the pipeline's rev (returned by get_current_state, get_pipeline, get_cell, create_plix and every write or run): when the pipeline changed since that rev, nothing is written and the reply carries the pipeline as it is now. One call takes a list of items, applied together. An edit to code, ports or an environment reaches the slides only through a run: with run: true the plix runs right after the edit, in the same call, and the reply carries the run's report and the pipeline after it (rev, every port's row with its shape and value — what get_pipeline and read_dataset_content would return). In that pipeline, an input row with no data and no upstream writer stops the run, and a cell that feeds nothing stays until delete_cell removes it. Changes appear live in the open editor. A cell's RUNNER and PACKAGES are its ENVIRONMENT's (the account's, the same list in every plix — list_environments: Browser / Cloud-CPU / Cloud-GPU / a paired machine + packages) — add_cell / update_cell {environment: name}; a new cell without one takes the plix's default environment (Browser unless the owner made another the default in the rail); "Browser", "Cloud-CPU" and "Cloud-GPU" are every account's built-in environments (that runner alone). The plix pins no runner. PORT GRANULARITY: one port per value usable on its own; parts meaningless alone travel as ONE dict on one port (a spectrum = {wavelength, intensity}, a fit = {params, residual}) — split only when each part is read by itself. A cell's declared input ports arrive in its code as variables named after each port; after the cell runs, declared output ports are read from variables of the same name — declare ports for everything the code consumes or produces. PORTS ARE ROWS: a port IS the plix row of its name — its name is the row's name (the code identifier), its kind the row's. A WIRE = two cells naming the same row. FEED an input = declare it under an existing row's name (list_assets; create_dataset / upload_asset make one; an upstream cell's output is a row: its port name) — the row's kind must fit; a name no row has is REFUSED — make the row first (a run refuses a cell whose input row holds no data, naming cell and input). An OUTPUT under a new name mints its EMPTY row; each run rewrites it — read_dataset_content reads it, add_dataset places it (a data row draws as a box / table; a media row: a plotly Figure is a LIVE chart; image / video / gltf / fem / glyphs / ifc / pdb / xyz draw as themselves). One writer per row. RENAME a port = rename_row (every cell naming the row follows, code identifiers included). A row outlives its port: a port dropped from a cell leaves its row; a row goes only by delete_row (off every cell naming it). CELL NAMES: a cell's name (label) is REQUIRED — a Python function name no other cell of the plix has; a taken name (also on a rename) is refused, never renumbered. WHAT A CELL COMPUTES is said in its CODE: open it with a short comment (what it computes, which output is THE result vs a diagnostic, units, caveats such as 'testing design, 5 iterations') — read it (get_cell) before composing with its outputs. CELLS ARE THE PLIX'S: a cell's name is unique in the plix; its code, ports and environment are its own — editing it changes nothing elsewhere. To reuse one in another plix the user pastes a COPY in the rail (a new cell there).
add_dataset
ChatGPTPut a ROW on a slide — THE placement (the rail's "+ Current slide", a drag): the element binds the row and draws it WHOLE, by its kind. A yaml → A BOX of its keys and values (nested keys as dotted paths; a one-row box for a scalar), a csv → A TABLE (its columns, a longer one scrolls), a markdown → THE TEXT (prose at its role's size; role / align / fontSize / color on the element style it — add_element {type: text} makes and places one in one call), a mermaid → THE DIAGRAM, a latex → THE EQUATION, a shape → the shape; a picture (image, svg), a movie (video), a LIVE chart (plotly — redrawn on every run: never re-create it to refresh), a 3D model (gltf / fem / glyphs / ifc), a structure (pdb / xyz → 3Dmol) → drawn as what it is; an h5 → THE DATASET: a read-only line per array (path, shape, dtype, first values) — never a chart: a cell plots it. EITHER a row (dataset: its name or id — list_assets; a cell's output is a row: its port's name, or under a sweep one sample's "<port>/<sample>") OR a cell's output port (cell + port: the port's row — it draws once the cell has run and follows every later run; port defaults to the cell's only drawing output). A slide element pins a row, always: two combinations can sit side by side. ONE CALL: datasets is a LIST — put every row to place (any slides) in this single call (done in order; the first failure stops the list, the ones before it stay done).
add_element
ChatGPTAdd TEXTS and SHAPES to a slide — ONE call per slide: pass every element the slide needs in elements, they land in ONE atomic write (one rev). EVERY ELEMENT IS A ROW of the plix made and placed by this call: a text a markdown row from its content (markdown: paragraphs, bold, lists, inline math $…$; role / align / fontSize / color on its element; update_element content rewrites it), a shape a shape row of its form and colours (placed UNDER everything: a card or band behind text, in any order; update_element fill / stroke change it; a diagram is a mermaid ROW and an equation a latex ROW — create_dataset format mermaid / latex, then add_dataset). Position/size are percentages of the slide (0-100), x/y anchor the TOP-LEFT corner; fontSize is px on the 1920x1080 raster. Layout rules: 5% margins; title band y=5 h=10 (role title); content from y=18; body (role body) left-aligned; columns x=5/52 width 43; center = x:50-width/2; elements must NEVER overlap. Every text names a ROLE (hero / title / heading / body / caption) — its size and colour are the plix style's; no fontSize / color. Returns the new elements' IDs (in order) and the slide's new rev. For a row use add_dataset (any kind: a data row, a picture, a movie, a chart, a model), never this.
add_environment_entries
ChatGPTAn entry is a pip requirement ('scipy>=1.12', 'pkg[extra]==1.0'), a PUBLIC GitHub repository that IS a Python package ('owner/repo[@ref]' / github.com URL — pyproject.toml or setup.py at its root), or an environment VARIABLE 'NAME=value' (MKL_NUM_THREADS=16 …; set in os.environ at the top of every cell before any import, on every backend; whoever a plix using it is shared with sees them, so never a secret). Adds entries to an environment. The change is saved in one step: validate → resolve the packages ON THE ENVIRONMENT'S RUNNER (Cloud: its image; a paired runner: its own Python and installed packages; Browser: micropip in the plix's open tab) into ONE reproducible lock (exact versions + hashes, refs → commit SHAs) → persist. What the runner already has never changes; a conflict names those packages — relax or change the package. If any step fails the environment is UNCHANGED and the error says why. The runner installs only what it lacks before the next run. A Browser environment saved from here resolves as soon as the plix's tab is open; GitHub packages need Cloud or a runner. Any change gives the environment a new identity, so the cells using it are not replayed from cache — Run again.
add_slide
ChatGPTAdd slides to a plix — each a WHOLE SLIDE: its title and its elements (add_element's specs: text, shape; a diagram is a mermaid ROW and an equation a latex ROW: create_dataset + add_dataset). Returns each slide's ID, its rev (expectedRev of your next write on it) and its elements' IDs. Plix rows go on them afterwards with add_dataset. Layout rules: 5% margins; title band y=5 h=10 (role title); content from y=18; body (role body) left-aligned; columns x=5/52 width 43; center = x:50-width/2; elements must NEVER overlap. Every text names a ROLE (hero / title / heading / body / caption) — its size and colour are the plix style's; no fontSize / color. ONE CALL: slides is a LIST — put every slide to add, each with its title and elements — a whole plix in one call in this single call (done in order; the first failure stops the list, the ones before it stay done).
change_layer
ChatGPTBring an element to the front or send it to the back of a slide's stacking order.
clear_cell_outputs
ChatGPTDrops CELLS' CACHED RESULTS (each by nodeId): its outputs and its run-cache entries, so the next Run recomputes it (definition, ports and rows untouched: the next run replaces the rows' content) — for results that are stale or were never justified by the graph. The plix's PIPELINE is its cells, drawn as ONE column in dependency order (a cell below what feeds it; ties in the pipeline's own order: a new cell lands last, reorder_cells sets it) — there are no positions. Every cell write takes expectedRev, the pipeline's rev (returned by get_current_state, get_pipeline, get_cell, create_plix and every write or run): when the pipeline changed since that rev, nothing is written and the reply carries the pipeline as it is now. One call takes a list of items, applied together. An edit to code, ports or an environment reaches the slides only through a run: with run: true the plix runs right after the edit, in the same call, and the reply carries the run's report and the pipeline after it (rev, every port's row with its shape and value — what get_pipeline and read_dataset_content would return). In that pipeline, an input row with no data and no upstream writer stops the run, and a cell that feeds nothing stays until delete_cell removes it. Changes appear live in the open editor.
clear_slide
ChatGPTDelete EVERY element on a slide, leaving it blank. The slide itself stays.
copy
ChatGPTCOPY an asset or a cell into a plix — the rail's ⋮ Copy then Paste, in one call: asset (an input's name; every sample comes along, the same files, no upload) or cell (a cell's name or id: its code, its ports by name and kind, its environment — the workspace's of that name, else the plix's default) of plix from (default: the plix itself — a duplicate beside the original), pasted into plixId as its own. A taken asset, cell or output name gets _2 (the code's identifier follows); a pasted cell's input with no asset of its name stays unbound until one exists; its outputs are empty rows until it runs. An OUTPUT has no copy: copy its cell.
create_dataset
ChatGPTCreate a data row from TEXT — the text IS the row's file, stored as you write it. format names it: yaml (the default) holds a value — one number (a scalar input: 0.5), a string, a list, a dict of them (JSON is YAML: {"n": 3, "label": "run A", "R": [1.5, 2.0]}); an integer literal is an int, any other number a float; csv holds a table — a header row, then the rows, every column one length (numbers, or text). A cell reads a yaml row as the document (number lists as numpy arrays) and a csv row as {column: float64 array | list of str}. Arrays that matter (large, N-D, a dtype) are an h5 row: a FILE — upload_asset. The row belongs to the plix, its bytes on storage (the plix's default storage when absent). mermaid holds A DIAGRAM — the bare mermaid source (flowchart LR …, no markdown fence), drawn to fill its box on a slide; latex holds AN EQUATION — bare LaTeX math (E = mc^2, no $ and no size command), typeset by KaTeX at the text size (neither is ever a cell's input or output: a port holds data or media). Bind a yaml / csv to a cell input with update_cell; place a row on a slide with add_dataset (a csv draws as a table, a yaml as a box, a mermaid as the diagram, a latex as the equation). ONE CALL: datasets is a LIST — put every row to create in this single call (done in order; the first failure stops the list, the ones before it stay done).
create_environment
ChatGPTAn ENVIRONMENT is the ACCOUNT's — stored once, the SAME list in every plix (an edit is live: the cells of every plix using it go stale and rerun) — naming its RUNNER (Browser / Cloud-CPU / Cloud-GPU / one of the user's paired machines; the plix pins none) plus packages + variables on top of it. EVERY CELL NAMES ONE environment (get_pipeline shows environment per cell; update_cell {environment: name}; every account has 'Browser', 'Cloud-CPU' and 'Cloud-GPU' — one per built-in runner, that runner alone, listed first, never renamed / deleted / moved — and a new cell is on Browser; a Browser cell and a Cloud cell can live in one plix), so a cell runs with no setup wherever it goes. The user sees the same rows in the rail (Compute) and picks a cell's from its rail row (Environment). Creates a new EMPTY environment (name: default env_N; runner: a name from list_runners or its engineUrl, default Browser) — then add_environment_entries fills it and update_cell {environment: name} assigns it. This is how a cell gets a package its runner lacks (an ImportError): the environment holds it, not a pip install inside the cell.
create_plix
ChatGPTCreate a new PLIX — a presentation whose slides are computed by its own Python cells (the user may ask for a presentation, a deck or slides: this is it). Every plix is born with one slide AND its ONE pipeline (the graph of its cells; there is no other), empty — add_cell authors it. Build the SLIDES in this same call — slides: each its title (every slide has a real title) and its elements (add_element's specs); the first fills the slide the plix is born with. The reply carries the plix's appUrl and every rev: open that link for the user right away (or give it to them) — never leave a new plix unlinked; no open_plix call is needed for a plix you just created. Layout rules: 5% margins; title band y=5 h=10 (role title); content from y=18; body (role body) left-aligned; columns x=5/52 width 43; center = x:50-width/2; elements must NEVER overlap. Every text names a ROLE (hero / title / heading / body / caption) — its size and colour are the plix style's; no fontSize / color.
delete_cell
ChatGPTDeletes CELLS of the pipeline (each by nodeId: its id or name): a cell leaves the plix; its rows stay in the plix (delete_row removes a row). The plix's PIPELINE is its cells, drawn as ONE column in dependency order (a cell below what feeds it; ties in the pipeline's own order: a new cell lands last, reorder_cells sets it) — there are no positions. Every cell write takes expectedRev, the pipeline's rev (returned by get_current_state, get_pipeline, get_cell, create_plix and every write or run): when the pipeline changed since that rev, nothing is written and the reply carries the pipeline as it is now. One call takes a list of items, applied together. An edit to code, ports or an environment reaches the slides only through a run: with run: true the plix runs right after the edit, in the same call, and the reply carries the run's report and the pipeline after it (rev, every port's row with its shape and value — what get_pipeline and read_dataset_content would return). In that pipeline, an input row with no data and no upstream writer stops the run, and a cell that feeds nothing stays until delete_cell removes it. Changes appear live in the open editor.
delete_element
ChatGPTDelete elements from a slide by ID — every one to remove in ONE call (elementIds). Other elements are untouched.
delete_environment
ChatGPTDeletes an environment — refused while a cell of any plix runs under it (the refusal names them); the built-ins never delete.
delete_plix
ChatGPTDelete a plix and everything in it — every slide, its pipeline, and the resources only it used. This cannot be undone. Only its creator, or an admin of the team, can (list_plixes canManage).
delete_row
ChatGPTDeletes by name: the whole ASSET (every row of it; every cell port naming it goes with it: the output off its writer, the input off its readers), its slide elements go; by "<name>/<sample>": that one row. A CELL is deleted with delete_cell. ONE CALL: rows is a LIST — put every row to delete row in this single call (done in order; the first failure stops the list, the ones before it stay done).
delete_slide
ChatGPTDelete slides — every one to remove in ONE call (slideIds). Its elements go with it; the rows those elements viewed are left in place.
disable_sharing
ChatGPTStop sharing a plix. Everyone it was shared with loses access at once; sharing again makes a new link. Only its creator, or an admin of the team, can (list_plixes canManage).
download
ChatGPTA URL to DOWNLOAD a row's file — the counterpart of upload: fetch it from your execution environment with any HTTP client (curl -sS -o <filename> '<url>'); the bytes must NOT pass through your context. ANY row on ANY storage, as it is stored: an image row its PNG, a video its mp4, a gltf its GLB, a plotly row its figure JSON, an svg its markup, a data row its .yaml / .csv / .h5 (read_dataset_content gives a yaml or a csv as text), a fem / glyphs row its .h5, a pdb / xyz row its structure file. NOT a public link — nothing is minted or published (the row's ⋮ Link in the app is the link for Overleaf or a README — no tool makes one): the URL serves the content the row holds NOW (after a re-run, call again); one on a storage the user paired is signed, expires within two days and is reached only from that storage's network. ONE CALL: rows is a LIST — put every row to download in this single call (done in order; the first failure stops the list, the ones before it stay done).
duplicate_element
ChatGPTDuplicate an element on a slide, slightly offset from the original.
duplicate_plix
ChatGPTDuplicate a plix as a fully independent copy — slides, its pipeline, and the rows they reference all come along, each on its storage. A plix shared with you whose environments run on its owner's machine can't be imported (they are not yours).
enable_sharing
ChatGPTShare a plix read-only and return its link. Whoever opens the link signs in (or signs up) and the plix joins their list, read-only; knowing the plix id alone grants nothing. Disabling sharing or regenerating the link (in the app) revokes everyone. Refused while any row is on one of the user's own storages (a viewer could never read it): move_row it to Plixlab Storage first — a row moved onto one later stops the plix serving its viewers. Environments on the user's machines are fine: viewers see the rows, they just can't import the plix.
exclude_row
ChatGPTA SAMPLE sits out of every Run and every collected dict (the rail's Exclude); include_row brings it back. ONE CALL: rows is a LIST — put every row to exclude a sample in this single call (done in order; the first failure stops the list, the ones before it stay done).
fetch_pdb_molecule
ChatGPTFetch a structure from the RCSB Protein Data Bank by its 4-character PDB code into the plix as a pdb row (named pdb_<CODE>: the .pdb file exactly as RCSB serves it — a Biopython Structure at a cell port) and place it on a slide as an interactive 3D molecule. Use search_pdb first if you only know the name. A structure you have as a file or text (a .pdb or an .xyz you wrote or were given): upload_asset (the file as it is → a pdb / xyz row), then add_dataset.
forget
ChatGPTDeletes recorded facts the conversation contradicted, or the user asks to forget (each by id, from help / get_current_state / a memory tool's read-back). Memory is injected at the start of every session (help / get_current_state), so write what a future session must not rediscover: stated preferences (accent colour, chart style, which engine runs what), corrections the user made to your work, standing context about a plix (what it is for, what its data is). One short sentence per fact; no secrets, nothing the user would not expect kept. scope 'global' = every plix; 'plix' + plixId = that plix only. Budget 30 global + 20 per plix, oldest-touched evicted. The reply reads every fact back.
get_cell
ChatGPTRead code cells of a pipeline — EVERY cell you need in ONE call (cells): full source code (its leading comment says what it computes), declared input/output ports, environment (runner + packages), and draft when the user is typing in its editor (uncommitted — not what runs). Read a cell only when you do not hold it, or the pipeline's rev moved (get_current_state gives the pipeline's rev and cell names; a write's reply carries the new rev). rev = the pipeline rev, the cell tools' expectedRev.
get_current_state
ChatGPTTHE SYNC — call it ONCE at the start of every turn on a plix (each new user message), before you think about the edit: the user may have changed the plix by hand since your last call. Returns where the USER is right now (their open tab's current slide, selected element, active view), the content of that slide, and the plix's OUTLINE: every slide, and the pipeline, with its rev (the pipeline with its cell names). COMPARE each rev with the one you hold: equal = that slide / pipeline is exactly as you last saw it — work from what you hold, read nothing; moved = re-read only the ones you are about to touch (get_slide {slideIds} / get_cell {cells}, each ONE call). OMIT plixId to get the plix the user is looking at (their tab's latest report) — use it whenever the user says "this plix" / "the one I'm on". After it, no list_slides / get_plix is needed.
get_environment
ChatGPTONE environment by name: its runner, every entry (a GitHub entry with the commit it is pinned to, a pip one with its resolved version), whether its packages are resolved on its runner, its envId and the cells of this plix using it.
get_pipeline
ChatGPTGet a summary of the plix's pipeline: its code cells (each with its environment: runner + packages) with their ports — each port with its row: datasetId (an input without one is unwired), row = the row's name, storage = where its bytes live (Plixlab Storage | the name of its machine; a Cloud cell can't read or write a private row), empty = never written, else — for a row holding one value — that value. Use this to learn the cells' ids and the rows their ports name.
get_plix
ChatGPTGet a plix's raw structure (slide IDs, metadata — defaultEnvironment / defaultStorage: where a new cell runs and a new row lives; autoRun: whether a change runs the plix by itself; update_plix sets them) as JSON — for reasoning about or editing it. To SHOW/OPEN a plix for the user to look at, use open_plix instead.
get_run_status
ChatGPTCheck a pipeline run request (every engine). A run still going is WAITED FOR — up to 30 s — before the reply: call it once and read the answer, never in a quick loop (each call is one status per half minute of run). Statuses: waiting (Browser cells queued for the plix's tab and no tab has the plix open — waiting reads "waiting for the plix's tab — open <appUrl>" and names the cells: open that link; once a tab claims them it reads running), running (cells carries per-cell state so far — ONE entry per cell × combination (comboKey; '' without a sweep), each as waiting/starting/running/done/cached/error/skipped with logs; live carries the stdout/stderr so far of every RUNNING cell, the same lines the log panel shows live), done (cells is the per-cell report; outputs are persisted in the plix), or error
get_slide
ChatGPTGet slides with all their elements — EVERY slide you need in ONE call (slideIds). Read a slide only when you do not hold it, or its rev moved (get_current_state lists every slide's rev; a write's reply carries the new one): a slide whose rev you hold unchanged is exactly what you last saw. Each rev is the expectedRev of your next write on that slide.
goto_slide
ChatGPTJump every live view of a plix to a specific slide (1-based number)
help
ChatGPTREQUIRED READING — returns the complete PlixLab skill: design rules, proven layouts, geometry, type scale, compute/pipeline authoring, engine selection, chart/equation/mermaid rules, and this surface's execution semantics. Call this ONCE at the start of any conversation in which you will create or edit slides, elements, charts, datasets, or pipelines — BEFORE the first write tool — and follow all of it. Read-only, no arguments.
include_row
ChatGPTA SAMPLE excluded before is back in every Run and every collected dict (the rail's Include). ONE CALL: rows is a LIST — put every row to include a sample in this single call (done in order; the first failure stops the list, the ones before it stay done).
list_assets
ChatGPTList the rows (assets) of a plix. A ROW IS ONE FILE, stored as written; its kind is the format of that file: yaml (application/yaml: numbers, strings, lists, dicts), csv (text/csv: a table), mermaid (text/vnd.mermaid: the source of a diagram, drawn), latex (application/x-tex: an equation, typeset), h5 (application/x-hdf5: a numpy array, or numpy arrays in dicts and groups), image (a PNG), svg, video (an mp4), plotly (a figure's JSON), gltf (a GLB), ifc (a STEP file), fem / glyphs (a mesh, an HDF5 file), pdb (a PDB structure file: a Biopython Structure at the port), xyz (an extended XYZ: an ase.Atoms at the port). A yaml / csv / mermaid / latex row is TEXT: read_dataset_content returns it, rewrite_row rewrites it. Any other row moves as a file: download (a signed URL), upload_asset (the user edits a yaml / csv / h5 row in its card too). Each row lists which cells use it. EVERY ROW IS THE PLIX'S OWN and is name/sample (its address): an asset with no samples is one row labelled root (addressed by its name alone); a SAMPLED asset is one row per sample, each labelled (sample; excluded: sitting out of the Run — the cell reading the asset runs once per included sample); a cell's OUTPUT is one row per combination it was computed under, labelled with the chosen samples joined by "/" ("scene/fourier", "scene/fourier/C2_tanh"). A name is one asset of the plix. usedBy lists every cell node of this plix with a port of the row's name — empty = no port names it yet. Its KIND says how it draws on a slide: a yaml as A BOX of its keys and values (nested keys as dotted paths), a csv as A TABLE — an h5 NEVER draws (a cell plots it, or writes a csv / yaml of the values to show); a media row is drawn by its kind — add_dataset places any. What goes on a slide is its own row, drawn WHOLE: a flat yaml of the few values to show, a csv for a table — configs stay in rows you do not place. storage is where its bytes live: "Plixlab Storage" or the name of the machine holding it (move_row moves it). A row's _id addresses it too.
list_environments
ChatGPTAn ENVIRONMENT is the ACCOUNT's — stored once, the SAME list in every plix (an edit is live: the cells of every plix using it go stale and rerun) — naming its RUNNER (Browser / Cloud-CPU / Cloud-GPU / one of the user's paired machines; the plix pins none) plus packages + variables on top of it. EVERY CELL NAMES ONE environment (get_pipeline shows environment per cell; update_cell {environment: name}; every account has 'Browser', 'Cloud-CPU' and 'Cloud-GPU' — one per built-in runner, that runner alone, listed first, never renamed / deleted / moved — and a new cell is on Browser; a Browser cell and a Cloud cell can live in one plix), so a cell runs with no setup wherever it goes. The user sees the same rows in the rail (Compute) and picks a cell's from its rail row (Environment). Lists every environment: its runner, entries, envId, whether its packages are resolved and the cells of THIS plix using it.
list_local_packages
ChatGPTThe LOCAL PACKAGES of a runner are the ones it was started with (--include <path>: packages under development, installed editable in the runner's venv) — and nothing else on that machine. READ-ONLY: the runner never writes a package (the owner edits it on their machine; the tree hash shows the edit). runner = the runner's name (list_runners, kind 'hosted'; default: the only online one). Docs (runners, --include): https://docs.plixlab.com/#runners Lists the runner's local packages (name, path, git HEAD, working-tree hash, dirty) — or, with package, that package's files (path, size).
list_plixes
ChatGPTList all the user's PLIXES — a plix is a presentation (a slide deck) whose slides are computed by its own Python cells — in their Plixlab workspace. canManage: whether YOU may rename or delete it — only its creator, or an admin of the team, can (in a team the others' creators are named: createdBy).
list_runners
ChatGPTA RUNNER is where a cell computes — the runner menu of an environment's card. Three built-ins (Browser = Pyodide in the user's tab; Cloud-CPU = PlixLab Cloud base image; Cloud-GPU = the same packages with CUDA torch and jax on ONE NVIDIA L4 — a second there uses 15.4 compute seconds of the plan, Cloud-CPU's 1) plus the user's paired runner MACHINES. A cloud run is charged the time its machine is up for it: startup, the run, and the time it stays ready after the last run (1 min); a cloud cell is stopped at 10 minutes. An ENVIRONMENT picks one (set_environment_runner) — the plix pins none; packages / variables are the environment's, never the runner's. A cell whose environment is on a runner runs server-side there (run, no tab) as soon as the runner process is online. A runner only computes: where rows are kept is a storage (list_storages), which any runner reads and writes. Lists every runner option with the exact engine / engineUrl values, each paired runner's online state, and the startCommand. ADDING a runner is AUTH-BASED and happens on the machine + in the browser, not through a tool: the startCommand (uv run <app>/pair.py runner — a self-contained script the app serves) runs on the target machine in the directory that becomes the runner's root (the runner builds its OWN venv there: --python 3.12 is required, -r requirements.txt seeds the base packages, --include <path> adds a local package editable and reports its git state) and prints a link <app>/pair/<code>; the USER opens it signed in and confirms — the runner comes online with a key it keeps in that directory, so later starts need no pairing. If you have shell access on that machine, start it yourself in the background, read the link from its output and give it to the user to open. The runner pulls its jobs over an outbound connection: nothing to expose, no URL. ONE directory = ONE runner: pairing under an existing runner's name re-keys that runner instead of adding another. Runners persist per user across sessions and devices. Docs (runners, pairing): https://docs.plixlab.com/#runners
list_slides
ChatGPTList a plix's slides in plix order: id, title, rev, element count and excluded (skipped when presenting; get_slide reads one whole).
list_storages
ChatGPTA STORAGE is where rows keep their bytes — the rail's Storage section. Plixlab Storage ('cloud') is always there; the user can PAIR their own — a directory of one of their machines served as an S3-compatible storage, reached by their tab and by any of their runners on its network through signed URLs (never by a Cloud cell). A row's storage is chosen per row (create_dataset / upload_asset storage, move_row; update_plix defaultStorage = where a plix's new rows start), apart from where its cells compute. Lists Plixlab Storage and each paired storage with its online state, endpoint, machine and directory, how much the account keeps on it, and the startCommand. ADDING a storage is AUTH-BASED and happens on the machine + in the browser, not through a tool: the startCommand (uv run <app>/pair.py storage) runs on the target machine in the directory whose .plixlab/blobs becomes the store (--port picks the port it listens on) and prints a link <app>/pair/<code>; the USER opens it signed in and confirms — the storage comes up with a key it keeps in that directory, so later starts need no pairing. The process must keep running for its rows to be read.
manage_workspace
ChatGPTTHE WORKSPACE this connector acts in: the user's personal account, or an organization they belong to — a workspace's plixes, runners, storage, plan and allowances are its own, shared by its members. No argument: the workspaces and the current one. workspace (an id from that list, or 'personal'): act in it from the NEXT call on — the plix ids you hold stay in the workspace you leave. Switch only when the user asks.
move_row
ChatGPTMoves a row's bytes — the row card's storage menu: storage = 'cloud' (Plixlab Storage — every cell, viewer and link reaches it) or one of the user's OWN STORAGES (list_storages — reached by the user's tab and their runners on its network, never by a Cloud cell); the row keeps its identity and nothing recomputes; waited for up to 90 s. ONE CALL: rows is a LIST — put every row to move row to a storage in this single call (done in order; the first failure stops the list, the ones before it stay done).
next_slide
ChatGPTAdvance every live view of a plix to the next slide
open_plix
ChatGPTSHOW / OPEN / DISPLAY / VIEW / PRESENT a plix (a presentation, a slide deck, whose slides its own Python cells compute). Use this whenever the user asks to see, open, show, view, present, or 'pull up' a specific plix. Returns a private, ready-to-open link to the LIVE plix — if you can run shell commands, open it in the user's default browser yourself; otherwise give the user the link. It is not rendered inline in this conversation. The plix stays live once open: subsequent update_slide / goto_slide / run calls are reflected in it instantly, and the user can edit in it directly. Works for the user's own private plixes (no sharing needed). Do NOT use get_plix for showing a plix (that returns raw JSON, not a view).
prev_slide
ChatGPTGo back to the previous slide in every live view of a plix
read_dataset_content
ChatGPTRead a row — by its id or its name (list_assets). A cell's OUTPUT is a row: read it by its port's name (its LAST-RUN value: run first if the cell never ran); under a SWEEP a cell writes one row per sample, named as its port — read one as "<port>/<sample>". A yaml / csv / mermaid / latex / markdown row comes back as the TEXT its file is (a csv cut to its first maxRows rows, any text to 100,000 characters); every other row is a FILE (an h5, an image, a video, a gltf…), described by its kind and size: download gives the file itself. ONE CALL: datasets is a LIST — put every row to read in this single call (done in order; the first failure stops the list, the ones before it stay done).
read_local_package_file
ChatGPTThe LOCAL PACKAGES of a runner are the ones it was started with (--include <path>: packages under development, installed editable in the runner's venv) — and nothing else on that machine. READ-ONLY: the runner never writes a package (the owner edits it on their machine; the tree hash shows the edit). runner = the runner's name (list_runners, kind 'hosted'; default: the only online one). Docs (runners, --include): https://docs.plixlab.com/#runners Reads ONE file of a local package (path inside the package): its text.
remember
ChatGPTRecords durable FACTS about THIS USER in the connector's memory (each: content, its type, its scope). Memory is injected at the start of every session (help / get_current_state), so write what a future session must not rediscover: stated preferences (accent colour, chart style, which engine runs what), corrections the user made to your work, standing context about a plix (what it is for, what its data is). One short sentence per fact; no secrets, nothing the user would not expect kept. scope 'global' = every plix; 'plix' + plixId = that plix only. Budget 30 global + 20 per plix, oldest-touched evicted. The reply reads every fact back.
remove_environment_entries
ChatGPTAn entry is a pip requirement ('scipy>=1.12', 'pkg[extra]==1.0'), a PUBLIC GitHub repository that IS a Python package ('owner/repo[@ref]' / github.com URL — pyproject.toml or setup.py at its root), or an environment VARIABLE 'NAME=value' (MKL_NUM_THREADS=16 …; set in os.environ at the top of every cell before any import, on every backend; whoever a plix using it is shared with sees them, so never a secret). Removes entries from an environment, each by its text (a variable by its NAME). The change is saved in one step: validate → resolve the packages ON THE ENVIRONMENT'S RUNNER (Cloud: its image; a paired runner: its own Python and installed packages; Browser: micropip in the plix's open tab) into ONE reproducible lock (exact versions + hashes, refs → commit SHAs) → persist. What the runner already has never changes; a conflict names those packages — relax or change the package. If any step fails the environment is UNCHANGED and the error says why. The runner installs only what it lacks before the next run. A Browser environment saved from here resolves as soon as the plix's tab is open; GitHub packages need Cloud or a runner. Any change gives the environment a new identity, so the cells using it are not replayed from cache — Run again.
remove_runner
ChatGPTRemoves a paired runner machine: its key stops working immediately; environments on it keep their pin until re-pointed (set_environment_runner).
remove_storage
ChatGPTDetaches a paired storage (nothing on the machine is deleted; its key stops working) — refused while rows are still kept on it: move_row them first.
rename_environment
ChatGPTRenames an environment (the cells using it follow; the built-ins never rename).
rename_row
ChatGPTRenames a row: name = the ASSET's name (every row of it and every port on it follow — the identifier in the cells' code too; a taken name is refused); sample = THIS row's label (a sample of an input; free among its asset's). One of the two. ONE CALL: rows is a LIST — put every row to rename row in this single call (done in order; the first failure stops the list, the ones before it stay done).
rename_runner
ChatGPTRenames a paired runner machine (its engineUrl key is unchanged).
rename_storage
ChatGPTRenames a paired storage.
reorder_cells
ChatGPTThe column's order (the editor's twin: a cell dragged by its header onto another): nodeIds = EVERY cell id of the plix, once, in the order wanted. The editor still draws a cell below what feeds it; nothing a Run computes changes. The plix's PIPELINE is its cells, drawn as ONE column in dependency order (a cell below what feeds it; ties in the pipeline's own order: a new cell lands last, reorder_cells sets it) — there are no positions. Every cell write takes expectedRev, the pipeline's rev (returned by get_current_state, get_pipeline, get_cell, create_plix and every write or run): when the pipeline changed since that rev, nothing is written and the reply carries the pipeline as it is now. One call takes a list of items, applied together. An edit to code, ports or an environment reaches the slides only through a run: with run: true the plix runs right after the edit, in the same call, and the reply carries the run's report and the pipeline after it (rev, every port's row with its shape and value — what get_pipeline and read_dataset_content would return). In that pipeline, an input row with no data and no upstream writer stops the run, and a cell that feeds nothing stays until delete_cell removes it. Changes appear live in the open editor.
reorder_slides
ChatGPTReorder slides in a plix
replace_in_cell
ChatGPTEdits a cell's code IN PLACE without resending it — the same write as update_cell {code} with the substitution applied: old_str → new_str, which must occur EXACTLY once (or pass replace_all) — otherwise nothing is written. Use it for any change smaller than a rewrite (a renamed key, a constant, one line); never resend a whole cell for a small change. The plix's PIPELINE is its cells, drawn as ONE column in dependency order (a cell below what feeds it; ties in the pipeline's own order: a new cell lands last, reorder_cells sets it) — there are no positions. Every cell write takes expectedRev, the pipeline's rev (returned by get_current_state, get_pipeline, get_cell, create_plix and every write or run): when the pipeline changed since that rev, nothing is written and the reply carries the pipeline as it is now. One call takes a list of items, applied together. An edit to code, ports or an environment reaches the slides only through a run: with run: true the plix runs right after the edit, in the same call, and the reply carries the run's report and the pipeline after it (rev, every port's row with its shape and value — what get_pipeline and read_dataset_content would return). In that pipeline, an input row with no data and no upstream writer stops the run, and a cell that feeds nothing stays until delete_cell removes it. Changes appear live in the open editor. While the user has unsaved typing in a cell's editor, a code write to that cell is refused and the reply shows their draft (discardDrafts).
restart_runner
ChatGPTAsks a LIVE paired runner process to restart itself: delivered on its next heartbeat (≤ 20 s), it re-execs with the freshly served pair.py once its running cells finish — use after the served script changed.
rewrite_row
ChatGPTRewrites a yaml / csv / mermaid / latex / markdown / shape row's TEXT: the text IS the row's file, stored as written (the row keeps its kind: a yaml row takes a YAML / JSON document, a csv row a table). Any other row is a FILE (an h5, a picture, an mp4, a GLB): download it, change it, upload_asset it with replace {rowId}. A row a cell writes is overwritten by its next run. ONE CALL: rows is a LIST — put every row to rewrite row in this single call (done in order; the first failure stops the list, the ones before it stay done).
run
ChatGPTRun a plix: every cell of its pipeline (the app recomputes only what changed). Cells on PlixLab Cloud or one of the user's runners run on the server: a quick run returns its report, a longer one a request ID that get_run_status follows (a runner must be online — list_runners). Browser cells run in the plix's open tab: with no tab on the plix the reply is status 'waiting' with its appUrl — open it or give it to the user — and the run starts once a tab opens it. One run at a time per plix: a second call meanwhile is rejected with the running one's request ID.
search_pdb
ChatGPTSearch the RCSB Protein Data Bank by text (protein name, gene, organism, ligand) and return matching PDB entries with their 4-character codes. Follow with fetch_pdb_molecule to put one on a slide.
search_workspace
ChatGPTSearch the user's PRIOR WORK across ALL their plixes: code cells (name, code) and datasets (name), keyword-matched and ranked. Useful before writing a cell on a topic the user may have built before (their field, a method, a library, a material): a hit returns its full code and ports, which add_cell can reuse in the current plix. Several words match any-of; every hit names the plix it came from — to see HOW a cell was wired (its inputs, the cells around it, what fed the slides), get_current_state {plixId} on that plix gives its pipeline: get_pipeline it.
set_environment_runner
ChatGPTMoves an environment onto a RUNNER (runner: a name from list_runners — 'Browser', 'Cloud-CPU', 'Cloud-GPU', a paired machine's name — or its engineUrl): its packages resolve again there and its cells re-run; the built-ins never move.
stop_run
ChatGPTStop the plix's run in flight — THE Stop button: every running run request of the plix ends AT ONCE (queued cells skipped, running cells stopped — their engine kills them on its next report, and nothing they post afterwards is written), which releases the one-run-per-plix rule: a new run can start right away. No-op when nothing is running.
update_cell
ChatGPTUpdates CELLS of the pipeline (each by nodeId: its id or name): the whole code, the full inputs / outputs lists, label, environment, collect, excluded — only the fields given change. For a change smaller than a rewrite of the code use replace_in_cell. The plix's PIPELINE is its cells, drawn as ONE column in dependency order (a cell below what feeds it; ties in the pipeline's own order: a new cell lands last, reorder_cells sets it) — there are no positions. Every cell write takes expectedRev, the pipeline's rev (returned by get_current_state, get_pipeline, get_cell, create_plix and every write or run): when the pipeline changed since that rev, nothing is written and the reply carries the pipeline as it is now. One call takes a list of items, applied together. An edit to code, ports or an environment reaches the slides only through a run: with run: true the plix runs right after the edit, in the same call, and the reply carries the run's report and the pipeline after it (rev, every port's row with its shape and value — what get_pipeline and read_dataset_content would return). In that pipeline, an input row with no data and no upstream writer stops the run, and a cell that feeds nothing stays until delete_cell removes it. Changes appear live in the open editor. While the user has unsaved typing in a cell's editor, a code write to that cell is refused and the reply shows their draft (discardDrafts). A cell's RUNNER and PACKAGES are its ENVIRONMENT's (the account's, the same list in every plix — list_environments: Browser / Cloud-CPU / Cloud-GPU / a paired machine + packages) — add_cell / update_cell {environment: name}; a new cell without one takes the plix's default environment (Browser unless the owner made another the default in the rail); "Browser", "Cloud-CPU" and "Cloud-GPU" are every account's built-in environments (that runner alone). The plix pins no runner. PORT GRANULARITY: one port per value usable on its own; parts meaningless alone travel as ONE dict on one port (a spectrum = {wavelength, intensity}, a fit = {params, residual}) — split only when each part is read by itself. A cell's declared input ports arrive in its code as variables named after each port; after the cell runs, declared output ports are read from variables of the same name — declare ports for everything the code consumes or produces. PORTS ARE ROWS: a port IS the plix row of its name — its name is the row's name (the code identifier), its kind the row's. A WIRE = two cells naming the same row. FEED an input = declare it under an existing row's name (list_assets; create_dataset / upload_asset make one; an upstream cell's output is a row: its port name) — the row's kind must fit; a name no row has is REFUSED — make the row first (a run refuses a cell whose input row holds no data, naming cell and input). An OUTPUT under a new name mints its EMPTY row; each run rewrites it — read_dataset_content reads it, add_dataset places it (a data row draws as a box / table; a media row: a plotly Figure is a LIVE chart; image / video / gltf / fem / glyphs / ifc / pdb / xyz draw as themselves). One writer per row. RENAME a port = rename_row (every cell naming the row follows, code identifiers included). A row outlives its port: a port dropped from a cell leaves its row; a row goes only by delete_row (off every cell naming it). CELL NAMES: a cell's name (label) is REQUIRED — a Python function name no other cell of the plix has; a taken name (also on a rename) is refused, never renumbered. WHAT A CELL COMPUTES is said in its CODE: open it with a short comment (what it computes, which output is THE result vs a diagnostic, units, caveats such as 'testing design, 5 iterations') — read it (get_cell) before composing with its outputs. CELLS ARE THE PLIX'S: a cell's name is unique in the plix; its code, ports and environment are its own — editing it changes nothing elsewhere. To reuse one in another plix the user pastes a COPY in the rail (a new cell there). EDITOR TWINS: a rail row (under a slide, under a cell, in the samples) or another cell's out…
update_element
ChatGPTUpdate properties of elements on a slide by ID — position, size, rotation, a text's content (markdown: written to its row), role / align / fontSize / color, a shape's colours — EVERY change to the slide in ONE call (updates: one atomic write, one rev). Only the provided fields change; pass null for a field to remove that optional property. Works on every element type, dataset elements included (move/resize/rotate).
update_memory
ChatGPTRewrites recorded facts the conversation refined (each by id, from help / get_current_state / a memory tool's read-back; the fields given replace the fact's). Memory is injected at the start of every session (help / get_current_state), so write what a future session must not rediscover: stated preferences (accent colour, chart style, which engine runs what), corrections the user made to your work, standing context about a plix (what it is for, what its data is). One short sentence per fact; no secrets, nothing the user would not expect kept. scope 'global' = every plix; 'plix' + plixId = that plix only. Budget 30 global + 20 per plix, oldest-touched evicted. The reply reads every fact back.
update_plix
ChatGPTUpdate a plix: rename, or set its DEFAULTS (the rail's Compute / Storage ⋮ Make default; get_plix shows them): defaultEnvironment = the environment a NEW cell is born on, defaultStorage = the storage a NEW row starts on, style = the style a NEW slide is born with (the Styles ⋮ Make default) — existing cells, rows and slides stay as they are; a slide's own style is update_slide {style}. autoRun (the switch by the Run button) = AUTO-RUN: while on, every change that leaves the plix out of date — a cell's code, an input row's content, a sweep's rows, a wire, an environment; made in the app or by a tool — starts a run by itself a moment later (the same run run makes; unchanged cells replay from cache), so do NOT call run after an edit: read get_run_status / the cells instead. A state whose run failed or was stopped is not retried until something changes. The plix pins NO runner: a cell's runner and packages are its ENVIRONMENT's (set_environment_runner moves an environment onto Browser / Cloud-CPU / Cloud-GPU / a paired machine; update_cell {environment} picks a cell's).
update_slide
ChatGPTUpdate a slide's title, style (the style THIS slide draws with, by name — like a cell's environment; new slides are born on the plix's default), background color, excluded (true: skipped when presenting — the tree's ⋮ Exclude; false: shown again), or animationOrder (the order elements appear in when presenting). Only the fields given change. Elements change through add_element / update_element / delete_element (granular, revision-checked). ONE CALL: slides is a LIST — put every slide to update, each with its fields in this single call (done in order; the first failure stops the list, the ones before it stay done).
upload_asset
ChatGPTUpload a file you HAVE (conversation attachment, local file, generated output) into the plix's rows. Every file becomes a ROW of its kind, stored AS IT IS: a PNG → image (a PIL Image at the port), an mp4 → video, a GLB / .gltf → gltf, a JSON plotly figure → plotly, a STEP .ifc → ifc, an .svg → svg (a vector picture: YOUR OWN drawn SVG goes here — the drawing only, no text or background in it (the words are slide text elements beside it); a str of its markup at the port), a structure file as it is — a .pdb → pdb (a Biopython Structure at the port), an .xyz (extended XYZ) → xyz (an ase.Atoms at the port): THE way to bring a structure you have as text (write the file, upload it) — a data file as the row of its format — .yaml → yaml, .csv → csv, .h5 → h5 (a numpy array, or numpy arrays in dicts: THE file for arrays); JPEG / GIF / WebP (PNG only), WebM, ifcJSON, VTK, .npy and .npz are refused. DEFAULT PATH (two calls): (1) call with no storageId/content (pass plixId and filename) → returns uploadUrl + method; from your execution environment send the raw file bytes there with ANY HTTP client (curl, Python urllib/requests, fetch), e.g. curl -sS -X <method> --data-binary @file.glb -H 'content-type: model/gltf-binary' '<uploadUrl>'. The JSON reply is {storageId} (Plixlab Storage, the default). With storage = one of the user's own storages (list_storages) the mint also needs hash (the file's sha-256, hex — sha256sum <file>) and filename, and answers a PUT with one header and the key it writes (the bytes go straight to that storage, from your network — you must be able to reach it); (2) call upload_asset again with filename + plixId and either that storageId or storage + that key+hash+size (+ placement: slideId, geometry) to finalize — the response carries datasetId and, if placed, the element ID. With replace {rowId} it REPLACES that row's content in place (the row keeps its kind; elements and bindings stay) — never upload a new row and delete the old. Same storage path as the in-app upload button; limits: 500 MB video, 100 MB for any other file, plus your storage quota. File bytes must NOT be base64-ed into your context. A file NEVER rides this call: no shell that reaches the upload URL = no file upload — send TEXT data (a number, a table, a config) with create_dataset instead.
view_runner
ChatGPTA RUNNER is where a cell computes — the runner menu of an environment's card. Three built-ins (Browser = Pyodide in the user's tab; Cloud-CPU = PlixLab Cloud base image; Cloud-GPU = the same packages with CUDA torch and jax on ONE NVIDIA L4 — a second there uses 15.4 compute seconds of the plan, Cloud-CPU's 1) plus the user's paired runner MACHINES. A cloud run is charged the time its machine is up for it: startup, the run, and the time it stays ready after the last run (1 min); a cloud cell is stopped at 10 minutes. An ENVIRONMENT picks one (set_environment_runner) — the plix pins none; packages / variables are the environment's, never the runner's. A cell whose environment is on a runner runs server-side there (run, no tab) as soon as the runner process is online. A runner only computes: where rows are kept is a storage (list_storages), which any runner reads and writes. Shows ONE runner's base environment exactly as Compute's View does (name: 'Browser', 'Cloud-CPU', 'Cloud-GPU' or a paired runner's name): the Python (or cloud image / Pyodide version), pip packages (base pins) and local packages (--include, with git HEAD, edited flag and working-tree hash).