Quick Start#
The example below will guide you through the basics of creating a notebook, adding content, executing code, and seeing the output. For more examples, see our Use Cases section.
Developer note: For pre-1.0 release information, see the pre-1.0 README
Installation#
For stable release:
For alpha pre-release:
API Tokens#
The Noteable API requires an authentication token. You can manage tokens at the Noteable user settings page.
- Log in to Noteable (sign up is free).
- In the User Settings tab, navigate to
API Tokens
and generate a new token. - Copy the generated token to the clipboard and save in a secure location, to be read into your Python environment later.
The token can be passed directly in to APIClient
on initialization, or set it as env var NOTEABLE_TOKEN
.
Usage#
Setting up the APIClient
#
Using the API token you created previously, load it into your notebook environment so it can be passed into the APIClient
directly. (If you're in Noteable, you can create a Secret that can be read in as an environment variable.)
import os
from origami.clients.api import APIClient
# if we have the `NOTEABLE_TOKEN` environment variable set,
# we don't need to pass it in to the APIClient directly
api_client = APIClient()
APIClient
is what we'll use to make HTTP requests to Noteable's REST API.
Checking your user information#
User(
id=UUID('f1a2b3c4-5678-4d90-ef01-23456789abcd'),
created_at=datetime.datetime(2023, 1, 1, 0, 0, 0, 0, tzinfo=datetime.timezone.utc),
updated_at=datetime.datetime(2023, 1, 1, 0, 0, 0, 0, tzinfo=datetime.timezone.utc),
deleted_at=None,
handle='ori.gami',
email='origami@noteable.io',
first_name='Ori',
last_name='Gami',
origamist_default_project_id=UUID('a1b2c3d4-e5f6-4a7b-8123-abcdef123456'),
principal_sub='pat:0a1b2c3d4e5f6g7h8i9j10k11l',
auth_type='pat:0a1b2c3d4e5f6g7h8i9j10k11l'
)
Creating a new Notebook#
For this example, we're using the origamist_default_project_id
, which is the default project designed to be used by the ChatGPT plugin. Feel free to replace it with projects you have access to in Noteable!
Provide a file path
as well as a project_id
(UUID) where the Notebook will exist.
project_id = user.origamist_default_project_id
file = await api_client.create_notebook(
project_id=project_id,
path="Origami Demo.ipynb"
)
file
File(
id=UUID('bcd12345-6789-4abc-d012-3456abcdef90'),
created_at=datetime.datetime(2023, 2, 2, 0, 0, 0, 0, tzinfo=datetime.timezone.utc),
updated_at=datetime.datetime(2023, 2, 2, 0, 0, 0, 0, tzinfo=datetime.timezone.utc),
deleted_at=None,
filename='Origami Demo.ipynb',
path=PosixPath('Origami Demo.ipynb'),
project_id=UUID('a1b2c3d4-e5f6-4a7b-8123-abcdef123456'),
space_id=UUID('7890ab12-3412-4cde-8901-2345abcdef67'),
size=0,
mimetype=None,
type='notebook',
current_version_id=None,
presigned_download_url=None,
url='https://app.noteable.io/f/abc12312-3412-4abc-8123-abc12312abc1/Origami Demo.ipynb'
)
Launching a Kernel#
At a minimum, the file_id
from the Notebook is required. Additionally, you can specify:
kernel_name
(defaultpython3
, see more about available kernels)hardware_size
(defaultsmall
, see more about hardware options).
KernelSession(
id=UUID('e1f2a345-6789-4b01-cdef-1234567890ab'),
kernel=KernelDetails(
name='python3',
last_activity=datetime.datetime(2023, 2, 2, 1, 0, 0, 0, tzinfo=datetime.timezone.utc),
execution_state='idle'
)
)
Adding Cells#
Content updates and code execution is handled through the Noteable Real-Time Update (RTU) websocket connection.
You may see messages like Received un-modeled RTU message msg.channel= ...
. This is expected as we update the Noteable backend services' messaging.
Once the RTU client is connected, we can begin adding cells, executing code, and more! First, let's add a code cell with a basic Python print
statement.
from origami.models.notebook import CodeCell
cell = CodeCell(source="print('Hello World')")
await realtime_notebook.add_cell(cell=cell)
.add_cell(source='CODE HERE')
as a shortcut.)
Running a Code Cell#
The returned value is a dictionary of asyncio.Future
s. Awaiting those futures will block until the cells have completed execution.
The return value of the Futures is the up-to-date cell. If there's output, an output collection id will be set on the cell metadata.
import asyncio
queued_execution = await realtime_notebook.queue_execution(cell.id)
cells = await asyncio.gather(*queued_execution)
cell = cells[0]
cell
CodeCell(
id='2345ab6c-de78-4901-bcde-f1234567890a',
source="print('Hello World')",
metadata={
'noteable': {'output_collection_id': UUID('d1234e5f-6789-4a0b-c123-4567890abcdef')},
'ExecuteTime': {
'start_time': '2023-02-02T01:00:00.000000+00:00',
'end_time': '2023-02-02T01:00:00.050000+00:00'
}
},
cell_type='code',
execution_count=None,
outputs=[]
)
Getting Cell Output#
We can call the .output_collection_id
property on cells directly, rather than having to parse the cell metadata.
output_collection = await api_client.get_output_collection(cell.output_collection_id)
output_collection
KernelOutputCollection(
id=UUID('d1234e5f-6789-4a0b-c123-4567890abcdef'),
created_at=datetime.datetime(2023, 2, 2, 1, 0, 1, 000000, tzinfo=datetime.timezone.utc),
updated_at=datetime.datetime(2023, 2, 2, 1, 0, 1, 000000, tzinfo=datetime.timezone.utc),
deleted_at=None,
cell_id='2345ab6c-de78-4901-bcde-f1234567890a',
widget_model_id=None,
file_id=UUID('bcd12345-6789-4abc-d012-3456abcdef90'),
outputs=[
KernelOutput(
id=UUID('abcdef90-1234-4a56-7890-abcdef123456'),
created_at=datetime.datetime(2023, 2, 2, 1, 0, 1, 000000, tzinfo=datetime.timezone.utc),
updated_at=datetime.datetime(2023, 2, 2, 1, 0, 1, 000000, tzinfo=datetime.timezone.utc),
deleted_at=None,
type='stream',
display_id=None,
available_mimetypes=['text/plain'],
content_metadata=KernelOutputContent(raw='{"name":"stdout"}', url=None, mimetype='application/json'),
content=KernelOutputContent(raw='Hello World\n', url=None, mimetype='text/plain'),
content_for_llm=KernelOutputContent(raw='Hello World\n', url=None, mimetype='text/plain'),
parent_collection_id=UUID('d1234e5f-6789-4a0b-c123-4567890abcdef')
)
]
)