-
Notifications
You must be signed in to change notification settings - Fork 1
feat(runtimed): streaming notebook load with jiter, add_cell_full, and blob store outputs #672
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 1 commit
ee7e91a
b278262
321ccbd
b2a64fc
4800025
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -372,6 +372,49 @@ impl NotebookDoc { | |||||||||||||||||||||||||||||||||||
| Ok(()) | ||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| /// Insert a fully-populated cell at the given index in a single operation. | ||||||||||||||||||||||||||||||||||||
| /// | ||||||||||||||||||||||||||||||||||||
| /// Unlike calling `add_cell` + `update_source` + `set_outputs` + | ||||||||||||||||||||||||||||||||||||
| /// `set_execution_count` sequentially, this method reuses the `ObjId` | ||||||||||||||||||||||||||||||||||||
| /// returned by each Automerge insertion — no `find_cell_index` lookup | ||||||||||||||||||||||||||||||||||||
| /// is needed. This eliminates the O(n) linear scan per operation that | ||||||||||||||||||||||||||||||||||||
| /// makes sequential calls O(n²) during bulk loads. | ||||||||||||||||||||||||||||||||||||
| pub fn add_cell_full( | ||||||||||||||||||||||||||||||||||||
| &mut self, | ||||||||||||||||||||||||||||||||||||
| index: usize, | ||||||||||||||||||||||||||||||||||||
| cell_id: &str, | ||||||||||||||||||||||||||||||||||||
| cell_type: &str, | ||||||||||||||||||||||||||||||||||||
| source: &str, | ||||||||||||||||||||||||||||||||||||
| outputs: &[String], | ||||||||||||||||||||||||||||||||||||
| execution_count: &str, | ||||||||||||||||||||||||||||||||||||
| ) -> Result<(), AutomergeError> { | ||||||||||||||||||||||||||||||||||||
| let cells_id = self | ||||||||||||||||||||||||||||||||||||
| .cells_list_id() | ||||||||||||||||||||||||||||||||||||
| .ok_or_else(|| AutomergeError::InvalidObjId("cells list not found".into()))?; | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| let len = self.doc.length(&cells_id); | ||||||||||||||||||||||||||||||||||||
| let index = index.min(len); | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| let cell_map = self.doc.insert_object(&cells_id, index, ObjType::Map)?; | ||||||||||||||||||||||||||||||||||||
| self.doc.put(&cell_map, "id", cell_id)?; | ||||||||||||||||||||||||||||||||||||
| self.doc.put(&cell_map, "cell_type", cell_type)?; | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| let source_id = self.doc.put_object(&cell_map, "source", ObjType::Text)?; | ||||||||||||||||||||||||||||||||||||
| if !source.is_empty() { | ||||||||||||||||||||||||||||||||||||
| self.doc.update_text(&source_id, source)?; | ||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| self.doc | ||||||||||||||||||||||||||||||||||||
| .put(&cell_map, "execution_count", execution_count)?; | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| let outputs_id = self.doc.put_object(&cell_map, "outputs", ObjType::List)?; | ||||||||||||||||||||||||||||||||||||
| for (i, output) in outputs.iter().enumerate() { | ||||||||||||||||||||||||||||||||||||
| self.doc.insert(&outputs_id, i, output.as_str())?; | ||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| Ok(()) | ||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| /// Delete a cell by ID. Returns `true` if the cell was found and deleted. | ||||||||||||||||||||||||||||||||||||
| pub fn delete_cell(&mut self, cell_id: &str) -> Result<bool, AutomergeError> { | ||||||||||||||||||||||||||||||||||||
| let cells_id = match self.cells_list_id() { | ||||||||||||||||||||||||||||||||||||
|
|
@@ -387,6 +430,20 @@ impl NotebookDoc { | |||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||
| /// Remove all cells from the document. | ||||||||||||||||||||||||||||||||||||
| /// | ||||||||||||||||||||||||||||||||||||
| /// Used to clean up after a failed streaming load so the next | ||||||||||||||||||||||||||||||||||||
| /// connection can retry from a clean state. | ||||||||||||||||||||||||||||||||||||
| pub fn clear_all_cells(&mut self) { | ||||||||||||||||||||||||||||||||||||
| if let Some(cells_id) = self.cells_list_id() { | ||||||||||||||||||||||||||||||||||||
| let len = self.doc.length(&cells_id); | ||||||||||||||||||||||||||||||||||||
| // Delete from the end to avoid index shifting | ||||||||||||||||||||||||||||||||||||
| for i in (0..len).rev() { | ||||||||||||||||||||||||||||||||||||
| let _ = self.doc.delete(&cells_id, i); | ||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||
| pub fn clear_all_cells(&mut self) { | |
| if let Some(cells_id) = self.cells_list_id() { | |
| let len = self.doc.length(&cells_id); | |
| // Delete from the end to avoid index shifting | |
| for i in (0..len).rev() { | |
| let _ = self.doc.delete(&cells_id, i); | |
| } | |
| } | |
| pub fn clear_all_cells(&mut self) -> Result<(), AutomergeError> { | |
| if let Some(cells_id) = self.cells_list_id() { | |
| let len = self.doc.length(&cells_id); | |
| // Delete from the end to avoid index shifting | |
| for i in (0..len).rev() { | |
| self.doc.delete(&cells_id, i)?; | |
| } | |
| } | |
| Ok(()) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed in b2a64fc — clear_all_cells now returns Result<(), AutomergeError> and propagates delete errors.
Copilot
AI
Mar 10, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
New public add_cell_full/clear_all_cells APIs don’t have direct unit tests here, even though this file has extensive coverage for other cell operations. Adding targeted tests (e.g., add_cell_full populates source/outputs/execution_count in one op; clear_all_cells leaves cell_count()==0 and preserves notebook_id) would help lock in the intended semantics.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added in b2a64fc — four tests covering add_cell_full (all fields populated, empty source, index ordering) and clear_all_cells (cells removed, notebook_id preserved).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Adding
jiterintroduces a transitive dependency onpyo3(see Cargo.lock), which can significantly increase build times and may require Python tooling/headers in environments that previously builtruntimedwithout Python. Please confirm this is acceptable for the daemon build targets, or consider an alternative JSON parser / ajiterconfiguration that avoids pulling inpyo3if possible.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is not the case — jiter is configured with
default-features = false, features = ["num-bigint"]specifically to avoid pyo3. Thepythonfeature (which pulls in pyo3) is opt-in and not enabled. Confirmed withcargo tree -p runtimed -i pyo3which prints "nothing to print."