ipygraph 0.1.2__tar.gz → 1.14.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (60) hide show
  1. {ipygraph-0.1.2 → ipygraph-1.14.1}/PKG-INFO +72 -62
  2. ipygraph-1.14.1/README.md +158 -0
  3. {ipygraph-0.1.2 → ipygraph-1.14.1}/myextension/_version.py +1 -1
  4. ipygraph-1.14.1/myextension/app.py +29 -0
  5. {ipygraph-0.1.2 → ipygraph-1.14.1}/myextension/labextension/package.json +2 -2
  6. ipygraph-0.1.2/myextension/labextension/static/694.cb943d1ae1290ce8.js → ipygraph-1.14.1/myextension/labextension/static/694.255a445d5c77b9d5.js +1 -1
  7. ipygraph-0.1.2/myextension/labextension/static/remoteEntry.358ed3d3675a8367.js → ipygraph-1.14.1/myextension/labextension/static/remoteEntry.ec753dbe935c4f70.js +7 -7
  8. {ipygraph-0.1.2 → ipygraph-1.14.1}/package.json +1 -1
  9. {ipygraph-0.1.2 → ipygraph-1.14.1}/pyproject.toml +4 -0
  10. ipygraph-0.1.2/README.md +0 -149
  11. {ipygraph-0.1.2 → ipygraph-1.14.1}/.copier-answers.yml +0 -0
  12. {ipygraph-0.1.2 → ipygraph-1.14.1}/.gitignore +0 -0
  13. {ipygraph-0.1.2 → ipygraph-1.14.1}/.prettierignore +0 -0
  14. {ipygraph-0.1.2 → ipygraph-1.14.1}/.yarnrc.yml +0 -0
  15. {ipygraph-0.1.2 → ipygraph-1.14.1}/CHANGELOG.md +0 -0
  16. {ipygraph-0.1.2 → ipygraph-1.14.1}/LICENSE +0 -0
  17. {ipygraph-0.1.2 → ipygraph-1.14.1}/NOTICE +0 -0
  18. {ipygraph-0.1.2 → ipygraph-1.14.1}/RELEASE.md +0 -0
  19. {ipygraph-0.1.2 → ipygraph-1.14.1}/babel.config.js +0 -0
  20. {ipygraph-0.1.2 → ipygraph-1.14.1}/install.json +0 -0
  21. {ipygraph-0.1.2 → ipygraph-1.14.1}/jest.config.js +0 -0
  22. {ipygraph-0.1.2 → ipygraph-1.14.1}/jupyter-config/server-config/myextension.json +0 -0
  23. {ipygraph-0.1.2 → ipygraph-1.14.1}/myextension/__init__.py +0 -0
  24. {ipygraph-0.1.2 → ipygraph-1.14.1}/myextension/agent.py +0 -0
  25. {ipygraph-0.1.2 → ipygraph-1.14.1}/myextension/agent_providers.py +0 -0
  26. {ipygraph-0.1.2 → ipygraph-1.14.1}/myextension/contents_manager.py +0 -0
  27. {ipygraph-0.1.2 → ipygraph-1.14.1}/myextension/handlers.py +0 -0
  28. {ipygraph-0.1.2 → ipygraph-1.14.1}/myextension/kaggle.py +0 -0
  29. {ipygraph-0.1.2 → ipygraph-1.14.1}/myextension/kernel_manager.py +0 -0
  30. {ipygraph-0.1.2 → ipygraph-1.14.1}/myextension/labextension/static/232.b7f55d8d983399ee.js +0 -0
  31. {ipygraph-0.1.2 → ipygraph-1.14.1}/myextension/labextension/static/style.js +0 -0
  32. {ipygraph-0.1.2 → ipygraph-1.14.1}/myextension/labextension/static/third-party-licenses.json +0 -0
  33. {ipygraph-0.1.2 → ipygraph-1.14.1}/setup.py +0 -0
  34. {ipygraph-0.1.2 → ipygraph-1.14.1}/src/__tests__/myextension.spec.ts +0 -0
  35. {ipygraph-0.1.2 → ipygraph-1.14.1}/src/agentActions.ts +0 -0
  36. {ipygraph-0.1.2 → ipygraph-1.14.1}/src/agentApi.ts +0 -0
  37. {ipygraph-0.1.2 → ipygraph-1.14.1}/src/agentPanel.ts +0 -0
  38. {ipygraph-0.1.2 → ipygraph-1.14.1}/src/agentTools.ts +0 -0
  39. {ipygraph-0.1.2 → ipygraph-1.14.1}/src/api.ts +0 -0
  40. {ipygraph-0.1.2 → ipygraph-1.14.1}/src/branchSelector.ts +0 -0
  41. {ipygraph-0.1.2 → ipygraph-1.14.1}/src/contract.ts +0 -0
  42. {ipygraph-0.1.2 → ipygraph-1.14.1}/src/datasetPicker.ts +0 -0
  43. {ipygraph-0.1.2 → ipygraph-1.14.1}/src/diagramView.ts +0 -0
  44. {ipygraph-0.1.2 → ipygraph-1.14.1}/src/document.ts +0 -0
  45. {ipygraph-0.1.2 → ipygraph-1.14.1}/src/dropUpload.ts +0 -0
  46. {ipygraph-0.1.2 → ipygraph-1.14.1}/src/graphView.ts +0 -0
  47. {ipygraph-0.1.2 → ipygraph-1.14.1}/src/icon.ts +0 -0
  48. {ipygraph-0.1.2 → ipygraph-1.14.1}/src/index.ts +0 -0
  49. {ipygraph-0.1.2 → ipygraph-1.14.1}/src/mdRender.ts +0 -0
  50. {ipygraph-0.1.2 → ipygraph-1.14.1}/src/model.ts +0 -0
  51. {ipygraph-0.1.2 → ipygraph-1.14.1}/src/parallel.ts +0 -0
  52. {ipygraph-0.1.2 → ipygraph-1.14.1}/src/secretsPanel.ts +0 -0
  53. {ipygraph-0.1.2 → ipygraph-1.14.1}/src/widget.ts +0 -0
  54. {ipygraph-0.1.2 → ipygraph-1.14.1}/style/base.css +0 -0
  55. {ipygraph-0.1.2 → ipygraph-1.14.1}/style/index.css +0 -0
  56. {ipygraph-0.1.2 → ipygraph-1.14.1}/style/index.js +0 -0
  57. {ipygraph-0.1.2 → ipygraph-1.14.1}/test_backend.py +0 -0
  58. {ipygraph-0.1.2 → ipygraph-1.14.1}/tsconfig.json +0 -0
  59. {ipygraph-0.1.2 → ipygraph-1.14.1}/tsconfig.test.json +0 -0
  60. {ipygraph-0.1.2 → ipygraph-1.14.1}/yarn.lock +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ipygraph
3
- Version: 0.1.2
3
+ Version: 1.14.1
4
4
  Summary: Turn your Jupyter notebook into a branching graph of experiments — a JupyterLab extension with a plan-first AI copilot, branch isolation, and fork-join parallel execution.
5
5
  Project-URL: Homepage, https://github.com/jupyterlab/jupyterlab-ipygb
6
6
  Project-URL: Bug Tracker, https://github.com/jupyterlab/jupyterlab-ipygb/issues
@@ -221,18 +221,21 @@ Classifier: Programming Language :: Python :: 3.14
221
221
  Requires-Python: >=3.10
222
222
  Requires-Dist: dill
223
223
  Requires-Dist: jupyter-server<3,>=2.0.0
224
+ Requires-Dist: jupyterlab<5,>=4.0.0
224
225
  Description-Content-Type: text/markdown
225
226
 
226
- <h1 align="center">ipyGraph</h1>
227
+ <p align="center">
228
+ <img src="https://raw.githubusercontent.com/anishchelliahcr/ipygraph-assets/main/logo-banner.png" alt="ipyGraph — Notebooks that branch" width="620">
229
+ </p>
227
230
 
228
231
  <p align="center"><b>Turn your Jupyter notebook into a branching graph of experiments.</b></p>
229
232
 
230
233
  <p align="center">
231
- <img src="docs/images/plan-view-detail.png" alt="ipyGraph plan view — a chess evaluation model built as a branching graph of self-describing steps" width="850">
234
+ <img src="https://raw.githubusercontent.com/anishchelliahcr/ipygraph-assets/main/plan-view-detail.png" alt="ipyGraph plan view — a chess evaluation model built as a branching graph of self-describing steps" width="820">
232
235
  </p>
233
236
 
234
237
  [![Build](https://github.com/jupyterlab/jupyterlab-ipygb/workflows/Build/badge.svg)](https://github.com/jupyterlab/jupyterlab-ipygb/actions/workflows/build.yml)
235
- &nbsp;JupyterLab 4 extension&nbsp;·&nbsp;`.ipygb` format&nbsp;·&nbsp;source-available
238
+ &nbsp;JupyterLab 4 · `.ipygb` file format · runs at `/graph` · source-available
236
239
 
237
240
  ---
238
241
 
@@ -244,92 +247,105 @@ Notebooks are linear. **Experimentation isn't.**
244
247
 
245
248
  ## What ipyGraph is
246
249
 
247
- **ipyGraph** (a.k.a. *Graphbook*, file extension `.ipygb`) is a JupyterLab 4 extension that turns a notebook into a **branching DAG of cells**. Every experiment is a *branch* on a living graph instead of a copy-pasted block you'll never find again. A shared data-prep step forks into competing models; each branch keeps its own state; you compare tips and keep the winner.
250
+ **ipyGraph** (a.k.a. *Graphbook*) is a JupyterLab 4 extension that turns a notebook into a **branching DAG of cells**. Every experiment is a *branch* on a living graph instead of a copy-pasted block you'll never find again. A shared data-prep step forks into competing models; each branch keeps its own state; you compare tips and keep the winner.
248
251
 
249
252
  It ships with a **plan-first AI copilot** that designs the experiment graph, writes the code, runs it, and iterates — and an execution engine where **one branch can never silently contaminate another**.
250
253
 
251
- The cells are real, runnable Jupyter cells — so outputs, plots, and the kernel all work exactly as you expect. When you're done, export any path as a plain `.ipynb`.
254
+ You launch it with its own command and it opens at `/graph`:
255
+
256
+ ```bash
257
+ pip install ipygraph
258
+ jupytergraph # opens JupyterLab at http://localhost:8888/graph
259
+ ```
260
+
261
+ ## The `.ipygb` file format
262
+
263
+ ipyGraph adds a new document type: **`.ipygb`** (a "Graphbook"). It's a **superset of the Jupyter notebook format** — the same `nbformat` JSON with cells, outputs, and metadata — with a little extra per-cell `ipygb` metadata that records the graph:
264
+
265
+ ```jsonc
266
+ "metadata": {
267
+ "ipygb": {
268
+ "parent_id": "root-1", // which cell's outputs this one builds on
269
+ "branch_name": "encode-nn", // the branch this cell starts (optional)
270
+ "parent_ids": ["a", "b"], // 2+ parents = a merge cell
271
+ "description": "…", // shown in Plan view
272
+ "inputs": { … } // explicit variables a merge consumes
273
+ }
274
+ }
275
+ ```
276
+
277
+ Because it's a superset, the kernel, outputs, and rendering are ordinary Jupyter — nothing proprietary about how your code runs. And you can always **export any root→tip path back to a plain `.ipynb`** to share or hand off.
252
278
 
253
279
  ---
254
280
 
255
281
  ## See it
256
282
 
257
- The screenshots below are a real ipyGraph project: *predicting a chess engine's position evaluation from the board.* A shared prep step splits into two feature representations (tabular vs. board-planes), which feed different model families.
283
+ The screenshots below are a real ipyGraph project — *predicting a chess engine's position evaluation from the board.* A shared prep step splits into two feature representations (tabular vs. board-planes), which feed different model families.
258
284
 
259
285
  **Plan view — the whole experiment reads like a flowchart, and every step documents itself:**
260
286
 
261
- <p align="center"><img src="docs/images/graph-plan-view.png" alt="Plan view: the full experiment graph" width="850"></p>
287
+ <p align="center"><img src="https://raw.githubusercontent.com/anishchelliahcr/ipygraph-assets/main/graph-plan-view.png" alt="Plan view: the full experiment graph" width="820"></p>
262
288
 
263
289
  **Code view — those same nodes are real, runnable Jupyter cells with live outputs:**
264
290
 
265
- <p align="center"><img src="docs/images/graph-code-view.png" alt="Code view: the same graph as runnable cells" width="850"></p>
291
+ <p align="center"><img src="https://raw.githubusercontent.com/anishchelliahcr/ipygraph-assets/main/graph-code-view.png" alt="Code view: the same graph as runnable cells" width="820"></p>
266
292
 
267
293
  **The AI copilot builds and iterates on the graph beside you (Autopilot is on by default):**
268
294
 
269
- <p align="center"><img src="docs/images/graph-with-assistant.png" alt="ipyGraph assistant panel" width="850"></p>
295
+ <p align="center"><img src="https://raw.githubusercontent.com/anishchelliahcr/ipygraph-assets/main/graph-with-assistant.png" alt="ipyGraph assistant panel with a live conversation" width="820"></p>
270
296
 
271
297
  ---
272
298
 
273
- ## Features
274
-
275
- ### 🕸️ The graph is the interface
276
- - **Graph View is the default view.** Cells are laid out as a 2D flow — column = branch, row = depth — with parents centered over their children.
277
- - **Two views of one graph.** Flip between **Plan view** (titles + prose descriptions — read the whole experiment at a glance) and **Code view** (the real editors and outputs). The agent can switch views for you to show you what matters.
278
- - **Branch anywhere.** Fork a new branch from any cell with one click; extend a tip with `+ Cell`.
279
- - **Edit in place.** Type directly into cells on the graph, rename branches, and click-to-edit descriptions — no round-trip to a separate editor.
280
- - **Arrow-key navigation** and a **👁 Watch agent** button that pans the graph to whatever cell is being worked on, live.
281
-
282
- ### 🤖 Plan-first AI copilot
283
- - **Describe a goal, get a plan on the graph.** The agent drafts the experiment as plan cells first — you approve, then it builds. It thinks in *dataflow*: shared prep on the trunk, a separate encoder per model family, models under their encoder.
284
- - **Multi-agent under the hood.** An architect-vs-critic design debate picks the model shortlist; parallel coder sub-agents fill in the branches concurrently.
285
- - **Finds data for you.** A full-screen dataset picker searches Kaggle and profiles candidates so you can choose the right one.
286
- - **Uses your Claude subscription** via the Claude Agent SDK (no API key needed), or bring an API key for Anthropic / OpenAI / Gemini.
287
- - **Minimal questions, fast turns** — it infers sensible defaults, asks only when a choice genuinely matters, and shows a live progress spinner while it works.
288
-
289
- ### 🔒 Reproducible, isolated execution
290
- - **A cell's variables come only from its own path.** One branch can never be polluted by code you ran on another branch.
291
- - **▶ Run** a single cell, or **⇥ Run to here** to rebuild a path from the start on a clean, isolated kernel.
292
- - **Fork-point kernel snapshots.** State is saved at each branch point (fast, on-disk), so a sibling branch **resumes from a saved copy instead of replaying the whole path** — and the copy rolls forward as you iterate down a branch.
293
- - **True fork-join parallelism.** *"Run Parallel"* on a 1-prep → 3-model graph runs the prep **once** and trains the three models **concurrently** on separate kernels.
294
- - **Staleness badges.** A cell warns you — *"changed after last run"* / *"upstream changed"* — when an edit made its last result stale, naming the variables that drifted.
295
-
296
- ### 🔀 Merge & compare
297
- - **Visual merge contracts.** Combine branches by **clicking the variables you want** right in each branch's code — hover to trace every occurrence, click to add it to the merge. No copy-paste, no name clashes.
298
- - Merge cells receive their inputs across kernels via an explicit, editable contract.
299
-
300
- ### 📓 Fits your existing workflow
301
- - **It's still Jupyter.** Real kernels, real outputs, MathJax, the works.
302
- - **Notebook side-view.** Open the linear notebook beside the graph any time.
303
- - **Export a path → `.ipynb`.** Pick a root-to-tip path and save it as an ordinary notebook to share or hand off.
299
+ ## Parallel execution
300
+
301
+ Branches are independent, so ipyGraph runs them like an experiment *should* run: **shared prep once, models concurrently.**
302
+
303
+ <p align="center"><img src="https://raw.githubusercontent.com/anishchelliahcr/ipygraph-assets/main/parallelism.png" alt="Fork-join parallelism: shared prep runs once, branches train in parallel on separate kernels, a merge compares them" width="820"></p>
304
+
305
+ - **Fork-join, not replay.** *Run Parallel* on a 1-prep → 3-model graph runs the prep **once**, then trains the three models **at the same time**, each on its own fresh kernel. A 10-model bake-off pays for data loading once, not ten times.
306
+ - **Fork-point kernel snapshots.** The kernel's state is saved at each branch point, so a sibling branch **resumes from a saved copy** instead of re-running the whole path — and the copy rolls forward as you iterate down a branch. Tweaking the deepest cell costs one cell's runtime plus a state restore.
307
+ - **True isolation.** A branch's variables come only from its own path. One branch can never be polluted by code you ran on another — proven by identical results whether a branch runs alone or beside others.
308
+ - **▶ Run** a single cell or **⇥ Run to here** to rebuild a path from scratch on a clean isolated kernel.
304
309
 
305
310
  ---
306
311
 
312
+ ## More features
313
+
314
+ - 🕸️ **The graph is the interface.** Graph View is the default; flip any card between **Plan view** (titles + prose) and **Code view** (editors + outputs). Edit code, rename branches, and click-to-edit descriptions in place. Arrow-key navigation; a **👁 Watch agent** button pans to whatever the agent is touching.
315
+ - 🤖 **Plan-first AI copilot.** Describe a goal → it drafts the plan on the graph, you approve, then it builds and iterates. Multi-agent: an architect-vs-critic debate picks the model shortlist; parallel coder sub-agents fill the branches. Finds data for you via a full-screen Kaggle dataset picker. Uses your Claude subscription (Claude Agent SDK, no API key) or an Anthropic/OpenAI/Gemini key.
316
+ - 🔀 **Visual merge contracts.** Combine branches by **clicking the variables you want** in each branch's code — hover to trace every occurrence, click to add it to the merge. No copy-paste, no name clashes.
317
+ - 🩺 **Staleness badges.** A cell warns you — *"changed after last run"* / *"upstream changed"* — when an edit made its last result stale, naming the variables that drifted.
318
+ - 📓 **Still Jupyter.** Real kernels, outputs, MathJax. Open the linear notebook side-by-side, and export any path to a plain `.ipynb`.
319
+
307
320
  ## Why it's useful
308
321
 
309
- - **You never lose an experiment.** Every model you try is a branch you can see, re-run, and compare — not a commented-out block.
310
- - **Your results are actually reproducible.** Path isolation + "Run to here" means a number on a branch was produced by *that branch's code and nothing else*.
311
- - **Iteration is cheap.** Fork snapshots mean tweaking the deepest cell costs one cell's runtime plus a state restore — not a full re-run of the pipeline. Expensive data prep is paid for **once**, even across parallel model trainings.
312
- - **The graph is self-documenting.** Plan view + descriptions turn a notebook into something a teammate (or future you) can read top to bottom.
313
- - **AI does the busywork, you keep control.** The agent plans, codes, runs, and compares; you approve the plan and own the graph.
322
+ - **You never lose an experiment** — every model you try is a branch you can see, re-run, and compare.
323
+ - **Results are actually reproducible** — path isolation + "Run to here" means a number came from *that branch's code and nothing else*.
324
+ - **Iteration is cheap** — fork snapshots + fork-join mean expensive prep is paid for once and deep edits re-run one cell.
325
+ - **The graph is self-documenting** — Plan view turns a notebook into something a teammate (or future you) can read top to bottom.
326
+ - **AI does the busywork, you keep control** — it plans, codes, runs, and compares; you approve the plan and own the graph.
314
327
 
315
328
  Great for **ML engineers and researchers** running model bake-offs, feature-engineering sweeps, and architecture searches — anyone whose "quick experiment" notebook always spirals.
316
329
 
317
330
  ---
318
331
 
319
- ## Install
332
+ ## Install & run
320
333
 
321
334
  ```bash
322
335
  pip install ipygraph
336
+ jupytergraph # launches JupyterLab at /graph
323
337
  ```
324
338
 
325
- Then start JupyterLab and create a new **Graphbook (.ipygb)** from the launcher, or open an existing `.ipygb` file — it opens in Graph View by default.
339
+ Then create a new **Graphbook (.ipygb)** from the launcher, or open an existing `.ipygb` — it opens in Graph View by default.
340
+
341
+ **Requirements:** JupyterLab ≥ 4, Python ≥ 3.10. For the AI copilot, either the `claude` CLI (subscription login) or an API key for your provider. Kaggle dataset search needs Kaggle credentials (entered once, stored server-side).
326
342
 
327
- **Requirements:** JupyterLab >= 4.0.0, Python >= 3.10. For the AI copilot, either the `claude` CLI (subscription login) or an API key for your provider of choice. Kaggle dataset search needs Kaggle credentials (entered once, stored server-side).
343
+ > `jupytergraph` is JupyterLab mounted at `/graph` with ipyGraph branding. Everything else — kernels, terminals, other extensions — works exactly as in JupyterLab.
328
344
 
329
345
  ## Quick start
330
346
 
331
- 1. Launcher → **Graphbook (.ipygb)**.
332
- 2. Open the **Assistant** panel and describe what you want to build — e.g. *"train tree and neural models to predict chess move quality, and iterate."*
347
+ 1. Run `jupytergraph`, then Launcher → **Graphbook (.ipygb)**.
348
+ 2. Open the **Assistant** and describe what to build — e.g. *"train tree and neural models to predict chess move quality, and iterate."*
333
349
  3. Approve the plan it draws on the graph → it builds, runs, and compares the branches.
334
350
  4. Fork your own branches, **Run Parallel**, and **Save a path as `.ipynb`** when you're happy.
335
351
 
@@ -337,26 +353,20 @@ Then start JupyterLab and create a new **Graphbook (.ipygb)** from the launcher,
337
353
 
338
354
  ## Development
339
355
 
340
- Note: You will need NodeJS to build the extension package. `jlpm` is JupyterLab's pinned version of yarn.
356
+ You will need NodeJS to build the extension. `jlpm` is JupyterLab's pinned yarn.
341
357
 
342
358
  ```bash
343
- # From the repo root: set up a dev environment
344
- python -m venv .venv
345
- source .venv/bin/activate
359
+ python -m venv .venv && source .venv/bin/activate
346
360
  pip install --editable "."
347
-
348
- # Link the dev build into JupyterLab
349
361
  jupyter labextension develop . --overwrite
350
-
351
- # Rebuild the TypeScript after changes
352
- jlpm build
362
+ jlpm build # rebuild the TypeScript after changes
353
363
  ```
354
364
 
355
- Watch mode (rebuild on save) in one terminal, JupyterLab in another:
365
+ Watch mode (rebuild on save) in one terminal, the app in another:
356
366
 
357
367
  ```bash
358
368
  jlpm watch
359
- jupyter lab
369
+ jupytergraph
360
370
  ```
361
371
 
362
372
  Tests: `jlpm test` (Jest) and Playwright/Galata integration tests under [`ui-tests`](./ui-tests/README.md). Packaging: see [RELEASE](RELEASE.md).
@@ -0,0 +1,158 @@
1
+ <p align="center">
2
+ <img src="https://raw.githubusercontent.com/anishchelliahcr/ipygraph-assets/main/logo-banner.png" alt="ipyGraph — Notebooks that branch" width="620">
3
+ </p>
4
+
5
+ <p align="center"><b>Turn your Jupyter notebook into a branching graph of experiments.</b></p>
6
+
7
+ <p align="center">
8
+ <img src="https://raw.githubusercontent.com/anishchelliahcr/ipygraph-assets/main/plan-view-detail.png" alt="ipyGraph plan view — a chess evaluation model built as a branching graph of self-describing steps" width="820">
9
+ </p>
10
+
11
+ [![Build](https://github.com/jupyterlab/jupyterlab-ipygb/workflows/Build/badge.svg)](https://github.com/jupyterlab/jupyterlab-ipygb/actions/workflows/build.yml)
12
+ &nbsp;JupyterLab 4 · `.ipygb` file format · runs at `/graph` · source-available
13
+
14
+ ---
15
+
16
+ ## The problem
17
+
18
+ Every ML notebook starts clean and ends as a 2,000-line scroll of dead cells, half-tried models, and *"wait — which run produced this number?"* You try five approaches, comment out four, lose track of which features fed which model, and can never reproduce the good result. AI can write the code for you now — but your **experiments** are still a linear mess.
19
+
20
+ Notebooks are linear. **Experimentation isn't.**
21
+
22
+ ## What ipyGraph is
23
+
24
+ **ipyGraph** (a.k.a. *Graphbook*) is a JupyterLab 4 extension that turns a notebook into a **branching DAG of cells**. Every experiment is a *branch* on a living graph instead of a copy-pasted block you'll never find again. A shared data-prep step forks into competing models; each branch keeps its own state; you compare tips and keep the winner.
25
+
26
+ It ships with a **plan-first AI copilot** that designs the experiment graph, writes the code, runs it, and iterates — and an execution engine where **one branch can never silently contaminate another**.
27
+
28
+ You launch it with its own command and it opens at `/graph`:
29
+
30
+ ```bash
31
+ pip install ipygraph
32
+ jupytergraph # opens JupyterLab at http://localhost:8888/graph
33
+ ```
34
+
35
+ ## The `.ipygb` file format
36
+
37
+ ipyGraph adds a new document type: **`.ipygb`** (a "Graphbook"). It's a **superset of the Jupyter notebook format** — the same `nbformat` JSON with cells, outputs, and metadata — with a little extra per-cell `ipygb` metadata that records the graph:
38
+
39
+ ```jsonc
40
+ "metadata": {
41
+ "ipygb": {
42
+ "parent_id": "root-1", // which cell's outputs this one builds on
43
+ "branch_name": "encode-nn", // the branch this cell starts (optional)
44
+ "parent_ids": ["a", "b"], // 2+ parents = a merge cell
45
+ "description": "…", // shown in Plan view
46
+ "inputs": { … } // explicit variables a merge consumes
47
+ }
48
+ }
49
+ ```
50
+
51
+ Because it's a superset, the kernel, outputs, and rendering are ordinary Jupyter — nothing proprietary about how your code runs. And you can always **export any root→tip path back to a plain `.ipynb`** to share or hand off.
52
+
53
+ ---
54
+
55
+ ## See it
56
+
57
+ The screenshots below are a real ipyGraph project — *predicting a chess engine's position evaluation from the board.* A shared prep step splits into two feature representations (tabular vs. board-planes), which feed different model families.
58
+
59
+ **Plan view — the whole experiment reads like a flowchart, and every step documents itself:**
60
+
61
+ <p align="center"><img src="https://raw.githubusercontent.com/anishchelliahcr/ipygraph-assets/main/graph-plan-view.png" alt="Plan view: the full experiment graph" width="820"></p>
62
+
63
+ **Code view — those same nodes are real, runnable Jupyter cells with live outputs:**
64
+
65
+ <p align="center"><img src="https://raw.githubusercontent.com/anishchelliahcr/ipygraph-assets/main/graph-code-view.png" alt="Code view: the same graph as runnable cells" width="820"></p>
66
+
67
+ **The AI copilot builds and iterates on the graph beside you (Autopilot is on by default):**
68
+
69
+ <p align="center"><img src="https://raw.githubusercontent.com/anishchelliahcr/ipygraph-assets/main/graph-with-assistant.png" alt="ipyGraph assistant panel with a live conversation" width="820"></p>
70
+
71
+ ---
72
+
73
+ ## Parallel execution
74
+
75
+ Branches are independent, so ipyGraph runs them like an experiment *should* run: **shared prep once, models concurrently.**
76
+
77
+ <p align="center"><img src="https://raw.githubusercontent.com/anishchelliahcr/ipygraph-assets/main/parallelism.png" alt="Fork-join parallelism: shared prep runs once, branches train in parallel on separate kernels, a merge compares them" width="820"></p>
78
+
79
+ - **Fork-join, not replay.** *Run Parallel* on a 1-prep → 3-model graph runs the prep **once**, then trains the three models **at the same time**, each on its own fresh kernel. A 10-model bake-off pays for data loading once, not ten times.
80
+ - **Fork-point kernel snapshots.** The kernel's state is saved at each branch point, so a sibling branch **resumes from a saved copy** instead of re-running the whole path — and the copy rolls forward as you iterate down a branch. Tweaking the deepest cell costs one cell's runtime plus a state restore.
81
+ - **True isolation.** A branch's variables come only from its own path. One branch can never be polluted by code you ran on another — proven by identical results whether a branch runs alone or beside others.
82
+ - **▶ Run** a single cell or **⇥ Run to here** to rebuild a path from scratch on a clean isolated kernel.
83
+
84
+ ---
85
+
86
+ ## More features
87
+
88
+ - 🕸️ **The graph is the interface.** Graph View is the default; flip any card between **Plan view** (titles + prose) and **Code view** (editors + outputs). Edit code, rename branches, and click-to-edit descriptions in place. Arrow-key navigation; a **👁 Watch agent** button pans to whatever the agent is touching.
89
+ - 🤖 **Plan-first AI copilot.** Describe a goal → it drafts the plan on the graph, you approve, then it builds and iterates. Multi-agent: an architect-vs-critic debate picks the model shortlist; parallel coder sub-agents fill the branches. Finds data for you via a full-screen Kaggle dataset picker. Uses your Claude subscription (Claude Agent SDK, no API key) or an Anthropic/OpenAI/Gemini key.
90
+ - 🔀 **Visual merge contracts.** Combine branches by **clicking the variables you want** in each branch's code — hover to trace every occurrence, click to add it to the merge. No copy-paste, no name clashes.
91
+ - 🩺 **Staleness badges.** A cell warns you — *"changed after last run"* / *"upstream changed"* — when an edit made its last result stale, naming the variables that drifted.
92
+ - 📓 **Still Jupyter.** Real kernels, outputs, MathJax. Open the linear notebook side-by-side, and export any path to a plain `.ipynb`.
93
+
94
+ ## Why it's useful
95
+
96
+ - **You never lose an experiment** — every model you try is a branch you can see, re-run, and compare.
97
+ - **Results are actually reproducible** — path isolation + "Run to here" means a number came from *that branch's code and nothing else*.
98
+ - **Iteration is cheap** — fork snapshots + fork-join mean expensive prep is paid for once and deep edits re-run one cell.
99
+ - **The graph is self-documenting** — Plan view turns a notebook into something a teammate (or future you) can read top to bottom.
100
+ - **AI does the busywork, you keep control** — it plans, codes, runs, and compares; you approve the plan and own the graph.
101
+
102
+ Great for **ML engineers and researchers** running model bake-offs, feature-engineering sweeps, and architecture searches — anyone whose "quick experiment" notebook always spirals.
103
+
104
+ ---
105
+
106
+ ## Install & run
107
+
108
+ ```bash
109
+ pip install ipygraph
110
+ jupytergraph # launches JupyterLab at /graph
111
+ ```
112
+
113
+ Then create a new **Graphbook (.ipygb)** from the launcher, or open an existing `.ipygb` — it opens in Graph View by default.
114
+
115
+ **Requirements:** JupyterLab ≥ 4, Python ≥ 3.10. For the AI copilot, either the `claude` CLI (subscription login) or an API key for your provider. Kaggle dataset search needs Kaggle credentials (entered once, stored server-side).
116
+
117
+ > `jupytergraph` is JupyterLab mounted at `/graph` with ipyGraph branding. Everything else — kernels, terminals, other extensions — works exactly as in JupyterLab.
118
+
119
+ ## Quick start
120
+
121
+ 1. Run `jupytergraph`, then Launcher → **Graphbook (.ipygb)**.
122
+ 2. Open the **Assistant** and describe what to build — e.g. *"train tree and neural models to predict chess move quality, and iterate."*
123
+ 3. Approve the plan it draws on the graph → it builds, runs, and compares the branches.
124
+ 4. Fork your own branches, **Run Parallel**, and **Save a path as `.ipynb`** when you're happy.
125
+
126
+ ---
127
+
128
+ ## Development
129
+
130
+ You will need NodeJS to build the extension. `jlpm` is JupyterLab's pinned yarn.
131
+
132
+ ```bash
133
+ python -m venv .venv && source .venv/bin/activate
134
+ pip install --editable "."
135
+ jupyter labextension develop . --overwrite
136
+ jlpm build # rebuild the TypeScript after changes
137
+ ```
138
+
139
+ Watch mode (rebuild on save) in one terminal, the app in another:
140
+
141
+ ```bash
142
+ jlpm watch
143
+ jupytergraph
144
+ ```
145
+
146
+ Tests: `jlpm test` (Jest) and Playwright/Galata integration tests under [`ui-tests`](./ui-tests/README.md). Packaging: see [RELEASE](RELEASE.md).
147
+
148
+ ---
149
+
150
+ ## License
151
+
152
+ **ipyGraph / Graphbook** is **source-available**, not OSI open source. See [`LICENSE`](./LICENSE) for the full terms and [`NOTICE`](./NOTICE) for third-party components.
153
+
154
+ - **Free for individuals and any non-commercial use** — development, evaluation, personal projects, academic research, and teaching.
155
+ - **Free for an organization's internal use up to 20 users (seats).**
156
+ - **A paid commercial license (with a 5% royalty) is required** to charge for something built on it, offer it as a hosted/SaaS service, or exceed 20 internal users. Contact **anishchelliah.cr@gmail.com** to arrange one.
157
+
158
+ This license covers only this project's own code. JupyterLab, Lumino, React, and other dependencies keep their own licenses (see [`NOTICE`](./NOTICE)); nothing here changes them.
@@ -1,4 +1,4 @@
1
1
  # This file is auto-generated by Hatchling. As such, do not:
2
2
  # - modify
3
3
  # - track in version control e.g. be sure to add to .gitignore
4
- __version__ = VERSION = '0.1.2'
4
+ __version__ = VERSION = '1.14.1'
@@ -0,0 +1,29 @@
1
+ """
2
+ `jupytergraph` — launch a JupyterLab server branded for ipyGraph and served
3
+ at /graph instead of /lab.
4
+
5
+ Same JupyterLab under the hood, just mounted at /graph so the product feels
6
+ like its own tool rather than "a JupyterLab extension". The server loads
7
+ JupyterLab via its own entry point, so a subclass's traits don't reach the
8
+ mounted app — the reliable lever is `LabApp.app_url` config, which we
9
+ default to /graph (still overridable on the command line).
10
+ """
11
+ import sys
12
+
13
+ from jupyterlab.labapp import LabApp
14
+
15
+ GRAPH_URL = "/graph"
16
+
17
+
18
+ def main():
19
+ argv = sys.argv[1:]
20
+ # Default the mount + landing page to /graph unless the user overrode it.
21
+ if not any(a.startswith("--LabApp.app_url") for a in argv):
22
+ argv = [f"--LabApp.app_url={GRAPH_URL}"] + argv
23
+ if not any(a.startswith("--LabApp.default_url") for a in argv):
24
+ argv = [f"--LabApp.default_url={GRAPH_URL}"] + argv
25
+ LabApp.launch_instance(argv=argv)
26
+
27
+
28
+ if __name__ == "__main__":
29
+ main()
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "myextension",
3
- "version": "0.1.2",
3
+ "version": "1.14.1",
4
4
  "description": "Turn your Jupyter notebook into a branching graph of experiments — a JupyterLab extension with a plan-first AI copilot, branch isolation, and fork-join parallel execution.",
5
5
  "keywords": [
6
6
  "jupyter",
@@ -126,7 +126,7 @@
126
126
  "extension": true,
127
127
  "outputDir": "myextension/labextension",
128
128
  "_build": {
129
- "load": "static/remoteEntry.358ed3d3675a8367.js",
129
+ "load": "static/remoteEntry.ec753dbe935c4f70.js",
130
130
  "extension": "./extension",
131
131
  "style": "./style"
132
132
  }
@@ -1,4 +1,4 @@
1
- "use strict";(self.rspackChunkmyextension=self.rspackChunkmyextension||[]).push([[694],{7549(e,t,n){n.r(t),n.d(t,{default:()=>eY});var l=n(4064),i=n(277),o=n(1404),s=n(9643),a=n(49),r=n(2012),d=n(2200),c=n(1758),p=n(8705),h=n(9996),u=n(5285),g=n(9645),f=n(7989),m=n(1660),y=n(4207);async function _(e,t={}){let n=y.ServerConnection.makeSettings(),l=o.URLExt.join(n.baseUrl,"ipygb",...e.split("/")),i=await y.ServerConnection.makeRequest(l,t,n),s={};try{s=await i.json()}catch(e){}if(!i.ok)throw Error(s.error||`HTTP ${i.status}`);return s}function b(e){return _("agent/config",{method:"POST",body:JSON.stringify(e)})}function v(e){return _(`kaggle/search?q=${encodeURIComponent(e)}`)}function x(e){return _("kaggle/download",{method:"POST",body:JSON.stringify({ref:e})})}function w(e){return _(`kaggle/files?ref=${encodeURIComponent(e)}`)}function C(e){return _("agent",{method:"POST",body:JSON.stringify(e)})}function k(e){let t=[],n=e=>{e&&!t.includes(e)&&t.push(e)};for(let t of e.split("\n")){if(/^\s/.test(t)||/^\s*#/.test(t))continue;let e=t.match(/^([A-Za-z_]\w*(?:\s*,\s*[A-Za-z_]\w*)*)\s*(?::[^=]+)?\s*(?:[-+*/|&^%@]|\/\/|\*\*|<<|>>)?=(?!=)/);if(e){e[1].split(",").forEach(e=>n(e.trim()));continue}if(e=t.match(/^(?:def|class)\s+([A-Za-z_]\w*)/)){n(e[1]);continue}(e=t.match(/^for\s+([A-Za-z_]\w*(?:\s*,\s*[A-Za-z_]\w*)*)\s+in\s/))&&e[1].split(",").forEach(e=>n(e.trim()))}return t}let E=`
1
+ "use strict";(self.rspackChunkmyextension=self.rspackChunkmyextension||[]).push([[694],{7549(e,t,n){n.r(t),n.d(t,{default:()=>eY});var l=n(4064),i=n(277),o=n(1404),s=n(2024),a=n(49),r=n(2012),d=n(2200),c=n(1758),p=n(8705),h=n(9996),u=n(5285),g=n(9645),f=n(7989),m=n(1660),y=n(4207);async function _(e,t={}){let n=y.ServerConnection.makeSettings(),l=o.URLExt.join(n.baseUrl,"ipygb",...e.split("/")),i=await y.ServerConnection.makeRequest(l,t,n),s={};try{s=await i.json()}catch(e){}if(!i.ok)throw Error(s.error||`HTTP ${i.status}`);return s}function b(e){return _("agent/config",{method:"POST",body:JSON.stringify(e)})}function v(e){return _(`kaggle/search?q=${encodeURIComponent(e)}`)}function x(e){return _("kaggle/download",{method:"POST",body:JSON.stringify({ref:e})})}function w(e){return _(`kaggle/files?ref=${encodeURIComponent(e)}`)}function C(e){return _("agent",{method:"POST",body:JSON.stringify(e)})}function k(e){let t=[],n=e=>{e&&!t.includes(e)&&t.push(e)};for(let t of e.split("\n")){if(/^\s/.test(t)||/^\s*#/.test(t))continue;let e=t.match(/^([A-Za-z_]\w*(?:\s*,\s*[A-Za-z_]\w*)*)\s*(?::[^=]+)?\s*(?:[-+*/|&^%@]|\/\/|\*\*|<<|>>)?=(?!=)/);if(e){e[1].split(",").forEach(e=>n(e.trim()));continue}if(e=t.match(/^(?:def|class)\s+([A-Za-z_]\w*)/)){n(e[1]);continue}(e=t.match(/^for\s+([A-Za-z_]\w*(?:\s*,\s*[A-Za-z_]\w*)*)\s+in\s/))&&e[1].split(",").forEach(e=>n(e.trim()))}return t}let E=`
2
2
  def __ipygb_inspect():
3
3
  import json, types
4
4
  out = []