{"id":18508669,"url":"https://github.com/noteable-io/origami","last_synced_at":"2025-04-09T03:32:07.780Z","repository":{"id":37197173,"uuid":"487603762","full_name":"noteable-io/origami","owner":"noteable-io","description":"A library capturing message patterns and protocols speaking to Noteable's APIs","archived":false,"fork":false,"pushed_at":"2024-01-02T13:33:34.000Z","size":1569,"stargazers_count":15,"open_issues_count":14,"forks_count":6,"subscribers_count":4,"default_branch":"main","last_synced_at":"2025-03-23T22:42:19.982Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"https://noteable-origami.readthedocs.io","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"bsd-3-clause","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/noteable-io.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2022-05-01T17:49:25.000Z","updated_at":"2025-03-03T01:38:36.000Z","dependencies_parsed_at":"2024-06-21T05:45:23.533Z","dependency_job_id":"84eecbfc-a157-4085-b7a0-00e371b050e6","html_url":"https://github.com/noteable-io/origami","commit_stats":{"total_commits":84,"total_committers":6,"mean_commits":14.0,"dds":0.5952380952380952,"last_synced_commit":"ce26f14d6a8eb9b0c37c8103f117c679a585b427"},"previous_names":[],"tags_count":48,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/noteable-io%2Forigami","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/noteable-io%2Forigami/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/noteable-io%2Forigami/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/noteable-io%2Forigami/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/noteable-io","download_url":"https://codeload.github.com/noteable-io/origami/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247973867,"owners_count":21026735,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-11-06T15:15:03.113Z","updated_at":"2025-04-09T03:32:07.411Z","avatar_url":"https://github.com/noteable-io.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Origami\n\n\u003cp align=\"center\"\u003e\n\u003cimg src=\"docs/papersnake.svg\" width=\"250px\" /\u003e\n\u003cbr /\u003e\nLaunch, edit, and share Jupyter notebooks \u003ci\u003ein automation\u003c/i\u003e.\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n\u003ca href=\"https://github.com/noteable-io/origami/actions/workflows/ci.yaml\"\u003e\n    \u003cimg src=\"https://github.com/noteable-io/origami/actions/workflows/ci.yaml/badge.svg\" alt=\"CI\" /\u003e\n\u003c/a\u003e\n\u003ca href=\"https://codecov.io/gh/noteable-io/origami\" \u003e \n \u003cimg src=\"https://codecov.io/gh/noteable-io/origami/branch/main/graph/badge.svg\" alt=\"codecov code coverage\"/\u003e \n \u003c/a\u003e\n\u003cimg alt=\"PyPI - License\" src=\"https://img.shields.io/pypi/l/noteable-origami\" /\u003e\n\u003cimg alt=\"PyPI - Python Version\" src=\"https://img.shields.io/pypi/pyversions/noteable-origami\" /\u003e\n\u003cimg alt=\"PyPI\" src=\"https://img.shields.io/pypi/v/noteable-origami\"\u003e\n\u003ca href=\"https://github.com/psf/black\"\u003e\u003cimg alt=\"Code style: black\" src=\"https://img.shields.io/badge/code%20style-black-000000.svg\"\u003e\u003c/a\u003e\n\u003c/p\u003e\n\n---\n\n[Install](#installation) | [Getting Started](#getting-started) | [Documentation](https://noteable-origami.readthedocs.io) | [License](./LICENSE) | [Code of Conduct](./CODE_OF_CONDUCT.md) | [Contributing](./CONTRIBUTING.md)\n\n\u003c!-- --8\u003c-- [start:intro] --\u003e\n\n## Intro to Origami\n\nOrigami is a 🐍 Python library for talking to [Noteable notebooks](https://noteable.io/). This is the official way to access the full breadth of API calls and access patterns in async Python for rich programmatic access to notebooks. You can use [Noteable for free](https://app.noteable.io) with a quick signup.\n\n\u003c!-- --8\u003c-- [end:intro] --\u003e\n\n\u003c!-- --8\u003c-- [start:requirements] --\u003e\n\n## Requirements\n\nPython 3.8+\n\n\u003c!-- --8\u003c-- [end:requirements] --\u003e\n\n\u003c!-- --8\u003c-- [start:install] --\u003e\n\n## Installation\n\nFor stable release:\n\n```bash\npip install noteable-origami\n```\n\n```bash\npoetry add noteable-origami\n```\n\nFor alpha pre-release:\n\n```bash\npip install noteable-origami --pre\n```\n\n\u003c!-- --8\u003c-- [end:install] --\u003e\n\n## Getting Started\n\n\u003e **Note**\n\u003e Developer note: For pre-1.0 release information, see the [pre-1.0 README](https://github.com/noteable-io/origami/blob/release/0.0.35/README.md)\n\n### API Tokens\n\u003c!-- --8\u003c-- [start:api-tokens] --\u003e\nThe Noteable API requires an authentication token. You can manage tokens at the Noteable user settings page.\n\n1. Log in to [Noteable](https://app.noteable.io) (sign up is free).\n2. In the User Settings tab, navigate to `API Tokens` and generate a new token.\n![](./screenshots/user_settings__api_tokens.png)\n3. Copy the generated token to the clipboard and save in a secure location, to be read into your Python environment later.\n![](./screenshots/user_settings__api_tokens2.png)\n\nThe token can be passed directly in to `APIClient` on initialization, or set it as env var `NOTEABLE_TOKEN`.\n\n### Usage\n\u003c!-- --8\u003c-- [end:api-tokens] --\u003e\n\nThe 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](../usage) section.\n\n### Setting up the `APIClient`\n\u003c!-- --8\u003c-- [start:api-client] --\u003e\nUsing 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](https://app.noteable.io), you can create a [Secret](https://docs.noteable.io/product-docs/collaborate/access-and-visibility/secrets-permissions) that can be read in as an environment variable.)\n\n```python\nimport os\nfrom origami.clients.api import APIClient\n\n# if we have the `NOTEABLE_TOKEN` environment variable set,\n# we don't need to pass it in to the APIClient directly\napi_client = APIClient()\n```\n*The `APIClient` is what we'll use to make HTTP requests to Noteable's REST API.*\n\u003c!-- --8\u003c-- [end:api-client] --\u003e\n\n### Checking your user information\n\u003c!-- --8\u003c-- [start:user-info] --\u003e\n```python\nuser = await api_client.user_info()\nuser\n```\n``` {.python .no-copy }\nUser(\n    id=UUID('f1a2b3c4-5678-4d90-ef01-23456789abcd'),\n    created_at=datetime.datetime(2023, 1, 1, 0, 0, 0, 0, tzinfo=datetime.timezone.utc),\n    updated_at=datetime.datetime(2023, 1, 1, 0, 0, 0, 0, tzinfo=datetime.timezone.utc),\n    deleted_at=None,\n    handle='ori.gami',\n    email='origami@noteable.io',\n    first_name='Ori',\n    last_name='Gami',\n    origamist_default_project_id=UUID('a1b2c3d4-e5f6-4a7b-8123-abcdef123456'),\n    principal_sub='pat:0a1b2c3d4e5f6g7h8i9j10k11l',\n    auth_type='pat:0a1b2c3d4e5f6g7h8i9j10k11l'\n)\n```\n(The information returned should match your user account information associated with the previously-generated API token.)\n\u003c!-- --8\u003c-- [end:user-info] --\u003e\n\n### Creating a new Notebook\n\n\u003e **Note**\n\u003e 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](https://app.noteable.io/)!\n\n\u003c!-- --8\u003c-- [start:create-notebook] --\u003e\nProvide a file `path` as well as a `project_id` (UUID) where the Notebook will exist.\n```python\nproject_id = user.origamist_default_project_id\n\nfile = await api_client.create_notebook(\n    project_id=project_id,\n    path=\"Origami Demo.ipynb\"\n)\nfile\n```\n``` {.python .no-copy }\nFile(\n    id=UUID('bcd12345-6789-4abc-d012-3456abcdef90'),\n    created_at=datetime.datetime(2023, 2, 2, 0, 0, 0, 0, tzinfo=datetime.timezone.utc),\n    updated_at=datetime.datetime(2023, 2, 2, 0, 0, 0, 0, tzinfo=datetime.timezone.utc),\n    deleted_at=None,\n    filename='Origami Demo.ipynb',\n    path=PosixPath('Origami Demo.ipynb'),\n    project_id=UUID('a1b2c3d4-e5f6-4a7b-8123-abcdef123456'),\n    space_id=UUID('7890ab12-3412-4cde-8901-2345abcdef67'),\n    size=0,\n    mimetype=None,\n    type='notebook',\n    current_version_id=None,\n    presigned_download_url=None,\n    url='https://app.noteable.io/f/abc12312-3412-4abc-8123-abc12312abc1/Origami Demo.ipynb'\n)\n```\n\u003c!-- --8\u003c-- [end:create-notebook] --\u003e\n\n### Launching a Kernel\n\u003c!-- --8\u003c-- [start:launch-kernel] --\u003e\nAt a minimum, the `file_id` from the Notebook is required. Additionally, you can specify:\n\n+ `kernel_name` (default `python3`, see more about [available kernels](https://docs.noteable.io/product-docs/work-with-notebooks/manage-kernels/noteable-provided-kernels))\n+ `hardware_size` (default `small`, see more about [hardware options](https://docs.noteable.io/product-docs/work-with-notebooks/manage-hardware)).\n\n```python\nkernel_session = await api_client.launch_kernel(file_id=file.id)\nkernel_session\n```\n```{.python .no-copy}\nKernelSession(\n    id=UUID('e1f2a345-6789-4b01-cdef-1234567890ab'),\n    kernel=KernelDetails(\n        name='python3',\n        last_activity=datetime.datetime(2023, 2, 2, 1, 0, 0, 0, tzinfo=datetime.timezone.utc),\n        execution_state='idle'\n    )\n)\n```\n\u003c!-- --8\u003c-- [end:launch-kernel] --\u003e\n\n### Adding Cells\n\u003c!-- --8\u003c-- [start:connect-rtu] --\u003e\nContent updates and code execution is handled through the Noteable Real-Time Update (RTU) websocket connection.\n```python\nrealtime_notebook = await api_client.connect_realtime(file)\n```\n\u003c!-- --8\u003c-- [end:connect-rtu] --\u003e\n\n\u003e **Warning**\n\u003e You may see messages like `Received un-modeled RTU message msg.channel= ...`. This is expected as we update the Noteable backend services' messaging.\n\n\u003c!-- --8\u003c-- [start:add-cells] --\u003e\nOnce 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.\n```python\nfrom origami.models.notebook import CodeCell\n\ncell = CodeCell(source=\"print('Hello World')\")\nawait realtime_notebook.add_cell(cell=cell)\n```\n(You can also pass code source directly into `.add_cell(source='CODE HERE')` as a shortcut.)\n\u003c!-- --8\u003c-- [end:add-cells] --\u003e\n\n### Running a Code Cell\n\u003c!-- --8\u003c-- [start:run-code-cell] --\u003e\nThe returned value is a dictionary of `asyncio.Future`s. Awaiting those futures will block until the cells have completed execution.\nThe 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.\n```python\nimport asyncio\n\nqueued_execution = await realtime_notebook.queue_execution(cell.id)\ncells = await asyncio.gather(*queued_execution)\ncell = cells[0]\ncell\n```\n```{.python .no-copy}\nCodeCell(\n    id='2345ab6c-de78-4901-bcde-f1234567890a',\n    source=\"print('Hello World')\",\n    metadata={\n        'noteable': {'output_collection_id': UUID('d1234e5f-6789-4a0b-c123-4567890abcdef')},\n        'ExecuteTime': {\n            'start_time': '2023-02-02T01:00:00.000000+00:00',\n            'end_time': '2023-02-02T01:00:00.050000+00:00'\n        }\n    },\n    cell_type='code',\n    execution_count=None,\n    outputs=[]\n)\n```\n\u003c!-- --8\u003c-- [end:run-code-cell] --\u003e\n\n### Getting Cell Output\n\u003c!-- --8\u003c-- [start:get-cell-output] --\u003e\nWe can call the `.output_collection_id` property on cells directly, rather than having to parse the cell metadata.\n```python\noutput_collection = await api_client.get_output_collection(cell.output_collection_id)\noutput_collection\n```\n```{.python .no-copy}\nKernelOutputCollection(\n    id=UUID('d1234e5f-6789-4a0b-c123-4567890abcdef'),\n    created_at=datetime.datetime(2023, 2, 2, 1, 0, 1, 000000, tzinfo=datetime.timezone.utc),\n    updated_at=datetime.datetime(2023, 2, 2, 1, 0, 1, 000000, tzinfo=datetime.timezone.utc),\n    deleted_at=None,\n    cell_id='2345ab6c-de78-4901-bcde-f1234567890a',\n    widget_model_id=None,\n    file_id=UUID('bcd12345-6789-4abc-d012-3456abcdef90'),\n    outputs=[\n        KernelOutput(\n            id=UUID('abcdef90-1234-4a56-7890-abcdef123456'),\n            created_at=datetime.datetime(2023, 2, 2, 1, 0, 1, 000000, tzinfo=datetime.timezone.utc),\n            updated_at=datetime.datetime(2023, 2, 2, 1, 0, 1, 000000, tzinfo=datetime.timezone.utc),\n            deleted_at=None,\n            type='stream',\n            display_id=None,\n            available_mimetypes=['text/plain'],\n            content_metadata=KernelOutputContent(raw='{\"name\":\"stdout\"}', url=None, mimetype='application/json'),\n            content=KernelOutputContent(raw='Hello World\\n', url=None, mimetype='text/plain'),\n            content_for_llm=KernelOutputContent(raw='Hello World\\n', url=None, mimetype='text/plain'),\n            parent_collection_id=UUID('d1234e5f-6789-4a0b-c123-4567890abcdef')\n        )\n    ]\n)\n```\n\u003c!-- --8\u003c-- [end:get-cell-output] --\u003e\n\n## CLI\n\nOrigami has a small CLI for fetching the content of a Notebook, and tailing a Notebook to see all RTU messages being emitted on the relevant RTU channels.\n\n```\npip install noteable-origami[cli]\npoetry install -E cli\n```\n\n1. Fetch the content of a Notebook and write to file: `origami fetch \u003cfile-id\u003e \u003e notebook.ipynb`\n2. Tail a Notebook, useful when debugging RTU messages: `origami tail \u003cfile-id\u003e`\n\n## Dev ENV settings\n\n- Use `NOTEABLE_API_URL` to point to non-production clusters, such as `http://localhost:8001/api` for local Gate development\n- E2E tests will use `TEST_SPACE_ID`, `TEST_PROJECT_ID`, and `TEST_USER_ID` env vars when running, useful in CI\n\n## Contributing\n\nSee [CONTRIBUTING.md](./CONTRIBUTING.md).\n\n---\n\n\u003cp align=\"center\"\u003eOpen sourced with ❤️ by \u003ca href=\"https://noteable.io\"\u003eNoteable\u003c/a\u003e for the community.\u003c/p\u003e\n\n\u003cimg href=\"https://pages.noteable.io/private-beta-access\" src=\"https://assets.noteable.io/github/2022-07-29/noteable.png\" alt=\"Boost Data Collaboration with Notebooks\"\u003e\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnoteable-io%2Forigami","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fnoteable-io%2Forigami","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnoteable-io%2Forigami/lists"}