shell = get_ipython()
type(shell)ipykernel.zmqshell.ZMQInteractiveShell
A Jupyter notebook is a convenient way to build context for a LLM one cell after the other: you are working in a fully editable conversation, while interacting with AI and with code.
Jeremy Howard and his team at Answer.ai explored how to work efficiently in this kind of conversation: they developed a method and platform called Solveit.
https://solve.it.com/
We would like to replicate this approach to working with AI in a wordslab notebooks environment.
Here is how we chose to do it: - in a jupyterlab notebook, there are two types of cells: markdown and code - we want to simulate a third type of cell: a “prompt” cell - the content of this cell is a prompt (text in markdown format) which is sent to an llm when the cell is executed, along with the text of all the cells situated above in the notebook (context) - the llm response is streamed just below and formatted as markdown.
To simulate this “prompt” cell we need to develop a Jupyterlab frontend extension which implements the following behaviors : - three buttons are added to the cell toolbar: “note”, “prompt”, “code” - a click on one of these buttons changes the type of the cell - “note” selects a classic markdown cell - “prompt” selects a code cell, modified with the special “prompt behavior” defined below - “code” selects a classic code cell - a “prompt” cell is distinguished from a regular code cell by a metadata property registered in the ipynb file - each cell type is visualized by a specific color in the left border of the cell - “note” cell has a green border - “prompt” cell a red border - “code” cell has a blue border - the “prompt” cell is a code cell with the specific modified behaviors - the code syntax highlighting is replaced by markdown syntax highlighting when the user types text in this cell - when the user executes this cell, the frontend extension does the following - calls the kernel to inject the following variables - __notebook_path with the path and name of the notebook in the workspace - __notebook_content with the full json representation of the notebook - __cell_id with the id of the current cell - then calls the kernel to execute a specific chat(message) python function - where the message parameter is the content of the cell - and the content of the notebook above the current cell is inluded as context - the python chat() function streams the response tokens from the llm to the output section of the code cell, with markdown rendering
See the section “Develop a Jupyterlab frontend extension” at the bottom of this page to understand how the extension was developed.
If you want to use “prompt” cells, you will first need to install the Jupyterlab frontend extension: - activate your Jupyterlab python virtual environment - pip install wordslab-notebooks-lib - restart your Jupyterlab server
The extension is already pre-installed in the wordslab-notebooks environment.
To be clear: the wordslab-notebooks-lib package contains both: the Javascript Jupyterlab frontend extension AND the python library wich is loaded in the python kernel.
The Jupyterlab frontend extension is reloaded and re-initialized each time you refresh your browser page: - to check is the extension is installed and running, look at the browser console and llok for the message ‘Wordslab notebooks extension vx.y.z activated’ - hit the refresh button if you encounter a bug and the extension stops working
You also need to install the wordslab-notebooks-lib library in the virtual environnement used by the ipython kernel which runs each notebook in which you want to use the Solveit method.
It is the client of the Jupyterlab extension, and provides many utilties and tools which support this new way of working with AI.
The main python object used to interact with an ipython kernel is the InteractiveShell. You get an instance of it with the get_ipython() method.
We will start by extending this shell with capabilities useful to work in a notebook with the Solveit method. These extensions are inspired by the library ipykernel_helper from Answer.ai. As of december 2025, this library is not open source, but it is available to users in the solve.it.com environment and is a dependency of other Apache 2.0 libraries, so I think it is OK to use it as an inspiration.
def escape(s, quote=True):
"""
Replace special characters "&", "<" and ">" to HTML-safe sequences.
If the optional flag quote is true (the default), the quotation mark
characters, both double quote (") and single quote (') characters are also
translated.
"""
s = s.replace("&", "&") # Must be done first!
s = s.replace("<", "<")
s = s.replace(">", ">")
if quote:
s = s.replace('"', """)
s = s.replace('\'', "'")
return sFile: /home/python/cpython-3.12.12-linux-x86_64-gnu/lib/python3.12/html/__init__.py
Get an overview of the variables & functions defined by the user so far in the notebook. The value addded by this function is to filter out all internal ipython and wordslab variables. Returns a tuple of dictionaries (user_variables, user_functions): - the keys are the variables or function names - the value is a truncated string representation of the variable value or the function signature The max_str_len parameter is used to truncate the string representation of the variables. The xtra_ignore parameter is used to hide additional names from the result.
({'Code': "functools.partial(<function ft>, 'code', void_=False)",
'Source': "functools.partial(<function ft>, 'source', void_=True)",
'shell': '<ipykernel.zmqshell.ZMQInteractiveShell object>',
'user_items': 'None'},
{'_safe_str': '(obj, max_str_len=200)'})
Get a safe and serializable representation of variables values from the user namespace. This method preserves real Python values when they are safe literals, otherwise it falls back to strings. You can call it in two modes: - literal = True : Preserve actual Python values when safe, best for internal tools - literal = False : Force everything to strings, best for logging / UI display / debug output
{'variables': {'Code': "functools.partial(<function ft>, 'code', void_=False)",
'Source': "functools.partial(<function ft>, 'source', void_=True)",
'shell': '<ipykernel.zmqshell.ZMQInteractiveShell object>',
'user_items': 'None'},
'functions': {'_safe_str': '(obj, max_str_len=200)'}}
Get a json schema and a function object for the functions defined in the user namespace which can be used as tools.
{'example_sum': ({'type': 'function',
'function': {'name': 'example_sum',
'description': 'Adds a + b.\n\nReturns:\n- type: integer',
'parameters': {'type': 'object',
'properties': {'a': {'type': 'integer',
'description': 'First thing to sum'},
'b': {'type': 'integer',
'description': 'Second thing to sum',
'default': 1}},
'required': ['a']}}},
<function __main__.example_sum(a: int, b: int = 1) -> int>)}
The 4 notebook properties below are silently injected by the Jupyterlab frontend extension before each code cell is executed.
This will not work if the wordslab-notebooks-lib Jupyterlab extension is not installed.
Search for var in all frames of the call stack
Jupyterlab notebook introspection and metaprogramming.
{'kernelspec': {'display_name': 'wordslab-notebooks-lib',
'language': 'python',
'name': 'wordslab-notebooks-lib'},
'language_info': {'codemirror_mode': {'name': 'ipython', 'version': 3},
'file_extension': '.py',
'mimetype': 'text/x-python',
'name': 'python',
'nbconvert_exporter': 'python',
'pygments_lexer': 'ipython3',
'version': '3.12.12'}}
These methods can be used to manipulate the notebook cells when the wordslab-notebooks-lib Jupyterlab frontend extension is installed.
They are inspired by the library dialoghelper from Answer.ai, but are adapted to the standard Jupyterlab context.
def add_cell(
content:str, # Content of the cell (i.e the prompt, code, or note cell text)
placement:str='add_after', # Can be 'add_after', 'add_before', 'at_start', 'at_end'
cell_id:str=None, # id of the cell that placement is relative to (if None, uses current cell)
cell_type:str='note', # Cell type, can be 'code', 'note', or 'prompt'
notebook_path:str='', # Notebook to update, defaults to current notebook
):
Add a cell to the current notebook or any other opened notebook (notebook_path), at the start/end of the notebook or before/after any cell (placementand cell_id), with a cell_type (note|prompt|code) and content (text). Returns the new cell id.
Test note
'984dea0c-450c-488f-8115-82e92d56c0ea'
Test note 2
Test note 3
'1e24f157-66dc-4200-8b12-523c92bb447d'
'28690fe7-bf10-4781-8067-432f45d18c7f'
'e80dbc6b-0832-423f-8e33-aacc0ea52039'
Test sometype
--------------------------------------------------------------------------- RuntimeError Traceback (most recent call last) Cell In[69], line 1 ----> 1 await notebook.add_cell("Test bad cell id", cell_id="bad_cell_id") Cell In[65], line 25, in add_cell(self, content, placement, cell_id, cell_type, notebook_path) 23 return result['cell_id'] 24 elif 'error' in result: ---> 25 raise RuntimeError(result['error']) RuntimeError: Cell not found: bad_cell_id
--------------------------------------------------------------------------- RuntimeError Traceback (most recent call last) Cell In[73], line 1 ----> 1 await notebook.add_cell("Test other notebook (bad notebook name)", placement="at_start", notebook_path="wordslab-notebooks-lib/nbs/temp.ipynb") Cell In[65], line 25, in add_cell(self, content, placement, cell_id, cell_type, notebook_path) 23 return result['cell_id'] 24 elif 'error' in result: ---> 25 raise RuntimeError(result['error']) RuntimeError: Notebook not found: wordslab-notebooks-lib/nbs/temp.ipynb. Make sure the notebook is opened in Jupyterlab.
'637eb2a9-421c-4a7c-99aa-e2426b9eab64'
--------------------------------------------------------------------------- RuntimeError Traceback (most recent call last) Cell In[75], line 1 ----> 1 await notebook.add_cell("Test other notebook (closed)", placement="at_start", notebook_path="wordslab-notebooks-lib/nbs/01_env.ipynb") Cell In[65], line 25, in add_cell(self, content, placement, cell_id, cell_type, notebook_path) 23 return result['cell_id'] 24 elif 'error' in result: ---> 25 raise RuntimeError(result['error']) RuntimeError: Notebook not found: wordslab-notebooks-lib/nbs/01_env.ipynb. Make sure the notebook is opened in Jupyterlab.
Update the cell identified by cell_id, in the current notebook or any other opened notebook (notebook_path), with a new content. Returns the updated cell id.
updated cell content
'293c77e8-5449-407a-8b4f-0d628c45bfc7'
--------------------------------------------------------------------------- ValueError Traceback (most recent call last) Cell In[95], line 1 ----> 1 await notebook.update_cell(content="no id") Cell In[94], line 13, in update_cell(self, cell_id, content, notebook_path) 11 self._ensure_jupyterlab_extension() 12 if not cell_id: ---> 13 raise ValueError("`cell_id` parameter is mandatory") 14 result = await self._comm.send({'action': 'update_cell', 'cell_id': cell_id, 'content': content, 'notebook_path': notebook_path }) 15 if 'success' in result and result['success']: ValueError: `cell_id` parameter is mandatory
--------------------------------------------------------------------------- RuntimeError Traceback (most recent call last) Cell In[96], line 1 ----> 1 await notebook.update_cell(cell_id="bad_cell_id", content="bad id") Cell In[94], line 18, in update_cell(self, cell_id, content, notebook_path) 16 return result['cell_id'] 17 elif 'error' in result: ---> 18 raise RuntimeError(result['error']) RuntimeError: Cell not found: bad_cell_id
'55d9b34d-7e39-4cb6-9c95-eb0b9e0176f3'
“Update the cell identified by cell_id, in the current notebook or any other opened notebook (notebook_path). Returns the deleted cell id.
'01d8ebd2-7a4d-479a-939a-d345a9660211'
'55d9b34d-7e39-4cb6-9c95-eb0b9e0176f3'
“Adds the cell identified by cell_id to the run queue, only in the current notebook (jupyterlab ‘run-cell’ command limitation). Returns the cell id. DOES NOT return the result of the execution: the target cell will only be run after the current cell execution finishes. Use the read_cell method later with the same cell_id to get the result of the execution.
“Read the text content of the cell identified by cell_id, only in the current notebook. Returns the text content of the cell as a single multiline string.
'#|export\n@patch\ndef read_cell(\n self: WordslabNotebook,\n cell_id: str = None, # id of cell to delete\n):\n """"Read the text content of the cell identified by `cell_id`, only in the current notebook.\n Returns the text content of the cell as a single multiline string."""\n self._ensure_jupyterlab_extension()\n if not cell_id:\n raise ValueError("`cell_id` parameter is mandatory")\n cell = next((c for c in self.content.cells if c.id == cell_id), None)\n if not cell:\n raise ValueError(f"Cell not found: {cell_id}")\n return cell.source'
Display the variables and functions defined by the user so far in the notebook.
| Name | Value |
|---|---|
| Code | functools.partial(<function ft at 0x7b36600a9580>, 'code', void_=False) |
| Source | functools.partial(<function ft at 0x7b36600a9580>, 'source', void_=True) |
| shell | <ipykernel.zmqshell.ZMQInteractiveShell object at 0x7b3670078680> |
| user_items | None |
| variables | {'Code': "functools.partial(<function ft at 0x7b36600a9580>, 'code', void_=False)", 'Source': "functools.partial(<function ft at 0x7b36600a9580>, 'source', void_=True)", 'shell': '<ipykernel.zmqshell.… |
| functions | {'_safe_str': '(obj, max_str_len=200)'} |
| get_variables_values | None |
| get_tools_schemas_and_functions | None |
| notebook | <__main__.WordslabNotebook object at 0x7b3640f956a0> |
| add_cell | None |
| update_cell | None |
| delete_cell | None |
| run_cell | None |
| read_cell | None |
| show_variables_and_functions | None |
| Name | Signature |
|---|---|
| _safe_str | (obj, max_str_len=200) |
| _get_schema | (ns: dict, t) |
| example_sum | (a: int, b: int = 1) -> int |
| _find_frame_dict | (var: str) |
| find_var | (var: str) |
Display the attributes and methods of a given python object
Jupyterlab notebook introspection and metaprogramming.
| Name | Type | Value | Doc |
|---|---|---|---|
| cell_id | str | b602a192-b7e7-4976-a26d-4fa1854879b4 | Unique ID of the current notebook cell, useful to locate the current cell in the full notebook content |
| chat_model | str | qwen3:30b | |
| chat_thinking | bool | True | |
| content | NotebookNode | {'metadata': {'kernelspec': {'display_name': 'wordslab-notebooks-lib', 'language': 'python', 'name': 'wordslab-notebooks-lib'}, 'language_info': {'codemirror_mode': {'name': 'ipython', 'version': 3}, … | Full content of the notebook returned as a NotebookNode object from the nbformat library |
| jupyterlab_extension_installed | bool | True | |
| jupyterlab_extension_version | str | 0.0.13 | wordslab-notebooks-lib version number injected by the Jupyterlab frontend extension |
| path | str | wordslab-notebooks-lib/nbs/04_notebook.ipynb | Relative path of the notebook .ipynb file in the notebook workspace |
| Name | Signatue | Type | Doc |
|---|---|---|---|
| JupyterlabExtensionComm | (target_name='wordslab_notebooks', timeout=2.0) | instance method | |
| add_cell | (content: str, placement: str = 'add_after', cell_id: str = None, cell_type: str = 'note', notebook_path: str = '') | instance method | Add a cell to the current notebook or any other opened notebook (`notebook_path`), at the start/end of the notebook or before/after any cell (`placement`and `cell_id`), with a `cell_type` (note|prompt… |
| delete_cell | (cell_id: str = None, notebook_path: str = '') | instance method | "Update the cell identified by `cell_id`, in the current notebook or any other opened notebook (`notebook_path`). Returns the deleted cell id. |
| read_cell | (cell_id: str = None) | instance method | "Read the text content of the cell identified by `cell_id`, only in the current notebook. Returns the text content of the cell as a single multiline string. |
| run_cell | (cell_id: str = None) | instance method | "Adds the cell identified by `cell_id` to the run queue, only in the current notebook (jupyterlab 'run-cell' command limitation). Returns the cell id. DOES NOT return the result of the execution: the … |
| show_object_members | (obj) | instance method | Display the attributes and methods of a given python object |
| show_variables_and_functions | () | instance method | Display the variables and functions defined by the user so far in the notebook. |
| update_cell | (cell_id: str = None, content: str = None, notebook_path: str = '') | instance method | Update the cell identified by `cell_id`, in the current notebook or any other opened notebook (`notebook_path`), with a new `content`. Returns the updated cell id. |
Get a json schema of functions which can be used as tools.
Get a safe and serializable representation of variables values.
{'variables': {'Code': "functools.partial(<function ft>, 'code', void_=False)",
'Source': "functools.partial(<function ft>, 'source', void_=True)",
'shell': '<ipykernel.zmqshell.ZMQInteractiveShell object>',
'user_items': 'None'},
'functions': {'_safe_str': '(obj, max_str_len=200)'}}
{'example_sum': ({'type': 'function',
'function': {'name': 'example_sum',
'description': 'Adds a + b.\n\nReturns:\n- type: integer',
'parameters': {'type': 'object',
'properties': {'a': {'type': 'integer',
'description': 'First thing to sum'},
'b': {'type': 'integer',
'description': 'Second thing to sum',
'default': 1}},
'required': ['a']}}},
<function __main__.example_sum(a: int, b: int = 1) -> int>)}
The notebook cells format is documented here:
https://nbformat.readthedocs.io/en/latest/format_description.html
Code cell outputs are a list, where each item has an output_type. The main types are:
Has name (stdout/stderr) and text fields
Has data dict with MIME types like text/plain, text/html, image/png
Same data dict structure as execute_result
Has ename, evalue, and traceback fields
The tricky part is that execute_result and display_data can contain multiple representations of the same data (e.g., a pandas DataFrame might have both text/plain and text/html versions).
Here is an example of “note” cell
{'id': 'eb560f48-42a2-4573-bf12-b3edb40bff20',
'cell_type': 'markdown',
'source': 'Code cell outputs in nbformat are a list, where each item has an output_type. The main types are:\n\n- stream — stdout/stderr text (e.g., from print())\n\nHas name (stdout/stderr) and text fields\n\n- execute_result — the return value of the last expression\n\nHas data dict with MIME types like text/plain, text/html, image/png\n\n- display_data — from display() calls\n\nSame data dict structure as execute_result\n\n- error — exceptions\n\nHas ename, evalue, and traceback fields\n\nThe tricky part is that execute_result and display_data can contain multiple representations of the same data (e.g., a pandas DataFrame might have both text/plain and text/html versions).',
'metadata': {}}Here is an example of “prompt” cell
{'id': '3d5a241d-890c-46db-acf5-d92886f9a77d',
'cell_type': 'code',
'source': '# This is an example of prompt\nprint("and this is an example of answer")',
'metadata': {'trusted': True,
'wordslab_cell_type': 'prompt',
'execution': {'iopub.status.busy': '2025-12-29T15:47:42.347364Z',
'iopub.execute_input': '2025-12-29T15:47:42.347549Z',
'iopub.status.idle': '2025-12-29T15:47:42.350815Z',
'shell.execute_reply.started': '2025-12-29T15:47:42.347534Z',
'shell.execute_reply': '2025-12-29T15:47:42.349884Z'}},
'outputs': [{'name': 'stdout',
'output_type': 'stream',
'text': 'and this is an example of answer\n'}],
'execution_count': 248}Here is an example of code cell
This code
import sys
from IPython.display import display, HTML, Markdown
# stream (stdout)
print("This is stdout")
# stream (stderr)
print("This is stderr", file=sys.stderr)
# display_data (multiple formats)
display(HTML("<b>Bold HTML</b>"))
display(Markdown("**Bold Markdown**"))
# execute_result (last expression)
{"key": "value", "number": 42}Produces these outputs
[{'name': 'stdout', 'output_type': 'stream', 'text': 'This is stdout\n'},
{'name': 'stderr', 'output_type': 'stream', 'text': 'This is stderr\n'},
{'output_type': 'display_data',
'data': {'text/plain': '<IPython.core.display.HTML object>',
'text/html': '<b>Bold HTML</b>'},
'metadata': {}},
{'output_type': 'display_data',
'data': {'text/plain': '<IPython.core.display.Markdown object>',
'text/markdown': '**Bold Markdown**'},
'metadata': {}},
{'execution_count': 223,
'output_type': 'execute_result',
'data': {'text/plain': "{'key': 'value', 'number': 42}"},
'metadata': {}},
{'traceback': ['\x1b[31m---------------------------------------------------------------------------\x1b[39m',
'\x1b[31mValueError\x1b[39m Traceback (most recent call last)',
'\x1b[36mCell\x1b[39m\x1b[36m \x1b[39m\x1b[32mIn[224]\x1b[39m\x1b[32m, line 1\x1b[39m\n\x1b[32m----> \x1b[39m\x1b[32m1\x1b[39m \x1b[38;5;28;01mraise\x1b[39;00m \x1b[38;5;167;01mValueError\x1b[39;00m(\x1b[33m"\x1b[39m\x1b[33mExample error message\x1b[39m\x1b[33m"\x1b[39m)\n',
'\x1b[31mValueError\x1b[39m: Example error message'],
'ename': 'ValueError',
'evalue': 'Example error message',
'output_type': 'error'}]In this code cell
{'id': 'a1d9fbe2-9a84-4d7d-9415-a2e4693ba7ac',
'cell_type': 'code',
'source': 'import sys\nfrom IPython.display import display, HTML, Markdown\n\n# stream (stdout)\nprint("This is stdout")\n\n# stream (stderr)\nprint("This is stderr", file=sys.stderr)\n\n# display_data (multiple formats)\ndisplay(HTML("<b>Bold HTML</b>"))\ndisplay(Markdown("**Bold Markdown**"))\n\n# execute_result (last expression)\n{"key": "value", "number": 42}',
'metadata': {'trusted': True,
'execution': {'iopub.status.busy': '2025-12-29T15:07:25.670149Z',
'iopub.execute_input': '2025-12-29T15:07:25.670496Z',
'iopub.status.idle': '2025-12-29T15:07:25.678474Z',
'shell.execute_reply.started': '2025-12-29T15:07:25.670471Z',
'shell.execute_reply': '2025-12-29T15:07:25.677678Z'}},
'outputs': [...],
'execution_count': 223}The following methods are inspired by the library toolslm from Answer.ai, but are adapted to our specific goal with prompt cells.
Test
Result
['<out type="stdout">This is stdout\n</out>',
'<out type="stderr">This is stderr\n</out>',
'<out type="html"><b>Bold HTML</b></out>',
'<out type="markdown">**Bold Markdown**</out>',
'<out type="text">{\'key\': \'value\', \'number\': 42}</out>',
'<out type="error">ValueError: Example error message</out>']Test
Result
'<note>Code cell outputs in nbformat are a list, where each item has an output_type. The main types are:\n\n- stream — stdout/stderr text (e.g., from print())\n\nHas name (stdout/stderr) and text fields\n\n- execute_result — the return value of the last expression\n\nHas data dict with MIME types like text/plain, text/html, image/png\n\n- display_data — from display() calls\n\nSame data dict structure as execute_result\n\n- error — exceptions\n\nHas ename, evalue, and traceback fields\n\nThe tricky part is that execute_result and display_data can contain multiple representations of the same data (e.g., a pandas DataFrame might have both text/plain and text/html versions).</note>'Test
Result
Test
Result
'<note>Code cell outputs in nbformat are a list, where each item has an output_type. The main types are:\n\n- stream — stdout/stderr text (e.g., from print())\n\nHas name (stdout/stderr) and text fields\n\n- execute_result — the return value of the last expression\n\nHas data dict with MIME types like text/plain, text/html, image/png\n\n- display_data — from display() calls\n\nSame data dict structure as execute_result\n\n- error — exceptions\n\nHas ename, evalue, and traceback fields\n\nThe tricky part is that execute_result and display_data can contain multiple representations of the same data (e.g., a pandas DataFrame might have both text/plain and text/html versions).</note><prompt><user># This is an example of prompt\nprint("and this is an example of answer")</user><assistant><out type="stdout">and this is an example of answer\n</out></assistant></prompt><code><source>import sys\nfrom IPython.display import display, HTML, Markdown\n\n# stream (stdout)\nprint("This is stdout")\n\n# stream (stderr)\nprint("This is stderr", file=sys.stderr)\n\n# display_data (multiple formats)\ndisplay(HTML("<b>Bold HTML</b>"))\ndisplay(Markdown("**Bold Markdown**"))\n\n# execute_result (last expression)\n{"key": "value", "number": 42}<outputs><out type="stdout">This is stdout\n</out><out type="stderr">This is stderr\n</out><out type="html"><b>Bold HTML</b></out><out type="markdown">**Bold Markdown**</out><out type="text">{\'key\': \'value\', \'number\': 42}</out></outputs></code>'Syntax to reference tools and variables
Define test tools and variables
def add(a: int, # The first number
b: int # The second number
) -> int: # The sum of the two numbers
"""Add two numbers"""
return a + b
def multiply(a: int, # The first number
b: int # The second number
) -> int: # The product of the two numbers
"""Multiply two numbers"""
return a * b
cat_name = "My cat is named Jerry"
dog_name = "My dog is named Rex"Mention them so they are available to the AI assistant:
&add, &multiply as tools$cat_name, $dog_nameas variables['myfunc', 'add', 'multiply']
['myvar', 'cat_name', 'dog_name']
{'add': ({'type': 'function',
'function': {'name': 'add',
'description': 'Add two numbers\n\nReturns:\n- type: integer',
'parameters': {'type': 'object',
'properties': {'a': {'type': 'integer',
'description': 'The first number'},
'b': {'type': 'integer', 'description': 'The second number'}},
'required': ['a', 'b']}}},
<function __main__.add(a: int, b: int) -> int>),
'multiply': ({'type': 'function',
'function': {'name': 'multiply',
'description': 'Multiply two numbers\n\nReturns:\n- type: integer',
'parameters': {'type': 'object',
'properties': {'a': {'type': 'integer',
'description': 'The first number'},
'b': {'type': 'integer', 'description': 'The second number'}},
'required': ['a', 'b']}}},
<function __main__.multiply(a: int, b: int) -> int>)}
[<function __main__.add(a: int, b: int) -> int>,
<function __main__.multiply(a: int, b: int) -> int>]
{'cat_name': 'My cat is named Jerry', 'dog_name': 'My dog is named Rex'}
'<var name="cat_name">My cat is named Jerry</var>\n<var name="dog_name">My dog is named Rex</var>'
def set_ollama_chat_model(
model:str, think:Union=None,
context_size:int=32768, # This is the default value for the ollama server in wordslab-notebooks
web_search:bool=False, # The ollama API key is necessary to activate web search
base_url:str='http://localhost:11434',
api_key:Optional=None, # If not provided, the optional key will be pulled from WordslabEnv
):
def set_openrouter_chat_model(
model:str, think:Union=None,
context_size:Optional=None, # For OpenRouter this parameter is ignored, we inherit the remote model config
web_search:bool=True, # Web search is activated by default four cloud models in openrouter
base_url:str='https://openrouter.ai/api/v1',
api_key:Optional=None, # If not provided, the mandatory key will be pulled from WordslabEnv
):
[Thinking] … thought in 1285 words
[Tool call] …
addreturned90327899
[Tool call] …
multiplyreturned37824085083058
[Thinking] … thought in 191 words
37824085083058
You can use the service &get_weather.
[Thinking] … thought in 309 words
[Tool call] …
get_weatherreturnedThe weather is nice in Paris today
[Thinking] … thought in 199 words
The weather is nice in Paris today.
Here is the check that the frontend extension will do before executing notebook.chat:
Jupyter kernels technical implementation details
https://chatgpt.com/share/692bea08-4510-8004-b9ab-c02feeb97c08
Jupyterlab extension development tutorial
https://jupyterlab.readthedocs.io/en/latest/extension/extension_tutorial.html
The source code of the Jupyterlab frontend extension can be found in the following files:
Typescript source code, dependencies, and compilation config:
src/index.tspackage.jsontsconfig.json.yarnrc.ymlExtension manifest and Javascript compiled code
This is how the extension files are included in the python package:
MANIFEST.ininclude install.json
include package.json
recursive-include wordslab_notebooks_lib/labextension *
graft wordslab_notebooks_lib/labextension
graft src
This is how the extension files are installed in Jupyterlab extensions directory when the python package is installed:
pyproject.toml[tool.setuptools]
include-package-data = true
[tool.setuptools.data-files]
"share/jupyter/labextensions/wordslab-notebooks-lib" = [
"wordslab_notebooks_lib/labextension/package.json",
"install.json"
]
"share/jupyter/labextensions/wordslab-notebooks-lib/static" = [
"wordslab_notebooks_lib/labextension/static/*"
]This how the command jupyter labextension develop finds the directory where the extension files live:
wordslab_notebooks_lib\__init__.pyThis is how the python package is identified as a Jupyterlab extension in pypi:
pyproject.tomlclassifiers = [ "Framework :: Jupyter :: JupyterLab :: Extensions :: Prebuilt" ]
Open a Terminal
cd $WORDSLAB_WORKSPACE/wordslab-notebooks-lib
source $JUPYTERLAB_ENV/.venv/bin/activate
# Install Javascript dependencies
jlpm install
# Build TypeScript extension
jlpm build
# Register the extension with JupyterLab during development
# jupyter labextension develop . --overwrite
rm $JUPYTERLAB_ENV/.venv/share/jupyter/labextensions/wordslab-notebooks-lib
ln -s $WORDSLAB_WORKSPACE/wordslab-notebooks-lib/wordslab_notebooks_lib/labextension/ $JUPYTERLAB_ENV/.venv/share/jupyter/labextensions/wordslab-notebooks-lib
# Verify extension is found
jupyter labextension listAfter installing the extension in development mode once, you can iterate fast: - update the code in src/index.ts - build the extension with jlpm build
No need to reinstall the extension or to restart Jupyterlab itself, just refresh your browser page.
https://nbformat.readthedocs.io/en/latest/format_description.html
nb = nbformat.from_dict(__notebook_content)
code_language = nb.metadata.language_info.name
print("> " + code_language + " notebook")
for cell in nb.cells:
if cell.id == __cell_id: break
is_markdown = cell.cell_type == "markdown"
is_code = cell.cell_type == "code"
is_raw = cell.cell_type == "raw"
print("---------------------")
print("cell", cell.id, cell.cell_type)
print("---------------------")
if is_markdown:
print(cell.source[:100])
elif is_code:
print(f"```{code_language}\n" + cell.source[:100] + "\n```")
elif is_raw:
print(cell.source[:100])
if is_code and cell.execution_count>0 and len(cell.outputs)>0:
print("---------------------")
print("cell outputs", cell.id, cell.execution_count)
print("---------------------")
for output in cell.outputs:
if output.output_type == "stream":
print(f"<{output.name}>")
print(output.text[:100])
print(f"</{output.name}>")
elif output.output_type == "display_data":
print("<display>")
if "data" in output:
print(" <data>")
repr(output.data)
print(" </data>")
if "metadata" in output and len(output.metadata)>0:
print(" <metadata>")
repr(output.metadata)
print(" </metadata>")
print("</display>")
elif output.output_type == "execute_result":
print("<result>")
if "data" in output:
print(" <data>")
print(output.data)
print(" </data>")
if "metadata" in output and len(output.metadata)>0:
print(" <metadata>")
print(output.metadata)
print(" </metadata>")
print("</result>")
elif output.output_type == "error":
print("<error>")
print(output.ename)
print(output.evalue)
for frame in output.traceback:
print(frame)
print("</error>")
print("---------------------")> python notebook
---------------------
cell 9d8a6aa0-8f58-4860-bcc1-2bfbdcb438b6 markdown
---------------------
# wordslab-notebooks-lib.jupyterlab
> Access wordslab-notebooks Jupyterlab extension version, curre
---------------------
cell 68f3493d-c252-4eb4-844b-abbd68ed3a70 markdown
---------------------
## Work together with AI in a Jupyterlab notebook - the Solveit method
A Jupyter notebook is a conv
---------------------
cell 0ff6fbdc-4a54-4e29-acbb-07529df8cfdd markdown
---------------------
## Install the Jupyterlab extension - wordslab-notebooks-lib
If you want to use "prompt" cells, you
---------------------
cell 65cd4cf8-d77b-4026-b428-bbd9550ea971 markdown
---------------------
## Communicate with the Jupyterlab extension
---------------------
cell ece4d545-8f78-4232-82fb-e837ea0185e4 code
---------------------
```python
#| export
import nbformat
```
---------------------
cell af2f3f45-f0da-4d37-9e39-b37d19ba5650 code
---------------------
```python
class JupyterlabNotebook:
def __init__(self):
if not "__wordslab_extension_version" in g
```
---------------------
cell 16180d26-5d5b-4c01-93d6-60c910f257bf code
---------------------
```python
notebook = JupyterlabNotebook()
```
---------------------
cell 13af8845-c7f8-440b-a80f-097c7cf1a541 code
---------------------
```python
notebook.jupyterlab_extension_version
```
---------------------
cell outputs 13af8845-c7f8-440b-a80f-097c7cf1a541 39
---------------------
<result>
<data>
{'text/plain': "'0.0.11'"}
</data>
</result>
---------------------
---------------------
cell 2906d67c-0764-4816-92c6-4063f17621aa code
---------------------
```python
notebook.path
```
---------------------
cell outputs 2906d67c-0764-4816-92c6-4063f17621aa 40
---------------------
<result>
<data>
{'text/plain': "'wordslab-notebooks-lib/nbs/02_jupyterlab.ipynb'"}
</data>
</result>
---------------------
---------------------
cell 45a764c9-e0ab-415c-947c-bdb12d26a2dd code
---------------------
```python
notebook.content.metadata
```
---------------------
cell outputs 45a764c9-e0ab-415c-947c-bdb12d26a2dd 41
---------------------
<result>
<data>
{'text/plain': "{'kernelspec': {'display_name': 'wordslab-notebooks-lib',\n 'language': 'python',\n 'name': 'wordslab-notebooks-lib'},\n 'language_info': {'codemirror_mode': {'name': 'ipython', 'version': 3},\n 'file_extension': '.py',\n 'mimetype': 'text/x-python',\n 'name': 'python',\n 'nbconvert_exporter': 'python',\n 'pygments_lexer': 'ipython3',\n 'version': '3.12.12'}}"}
</data>
</result>
---------------------
---------------------
cell d16ad869-d651-40bd-af2c-623d82b4edf0 code
---------------------
```python
notebook.cell_id
```
---------------------
cell outputs d16ad869-d651-40bd-af2c-623d82b4edf0 42
---------------------
<result>
<data>
{'text/plain': "'d16ad869-d651-40bd-af2c-623d82b4edf0'"}
</data>
</result>
---------------------
---------------------
cell 59347d6e-d1f7-4d23-b041-143e42887f6d code
---------------------
```python
notebook.cell_id
```
---------------------
cell outputs 59347d6e-d1f7-4d23-b041-143e42887f6d 43
---------------------
<result>
<data>
{'text/plain': "'59347d6e-d1f7-4d23-b041-143e42887f6d'"}
</data>
</result>
---------------------
---------------------
cell 9843c2c4-ac54-46d6-9725-0e957e944e3a markdown
---------------------
## Develop a Jupyterlab frontend extension
---------------------
cell 4178ac20-4612-4c8d-8d48-0fd2d2605aa9 markdown
---------------------
### Understand Jupyterlab kernels and frontend extensions
Jupyter kernels technical implementation
---------------------
cell da7ecd61-80f6-4a00-a795-6866d62b32bb markdown
---------------------
### Intialize the components of a frontend extension
The source code of the Jupyterlab frontend ext
---------------------
cell 7a4146ce-96e0-4c13-9296-67374c833560 markdown
---------------------
### Install the Jupyterlab frontend extension in development mode
Open a Terminal
```bash
cd $WORD
---------------------
cell 6a891df5-84b9-4579-a1ec-18fd7a13ebc2 markdown
---------------------
### Test the Jupyterlab frontend extension
After installing the extension in development mode once
---------------------
cell 61a8b5d6-7821-4c76-acde-9080fb8ad95b markdown
---------------------
## Explore the notebook format
---------------------
cell bfe108e4-6b27-403d-b5d7-fde736c1f01c markdown
---------------------
https://nbformat.readthedocs.io/en/latest/format_description.html