Skip to main content

SQLsaber Notebook

Notebook-specific data-analysis subagent for SQLsaber.

Implemented components:

  • provider-neutral notebook execution contract,
  • hardened local Docker execution (default),
  • explicit local microVM execution through the optional microsandbox extra,
  • explicit remote Modal Sandbox execution through the optional modal extra,
  • explicit remote Daytona execution through the pinned daytona extra,
  • explicit remote E2B execution through the optional e2b extra,
  • fresh-kernel transactional notebook sessions,
  • bounded notebook/image rendering and history collapse,
  • list_workspace and edit_cell analyst tools,
  • a Pydantic AI notebook analyst,
  • a managed SQLsaber analyze_data capability,
  • reusable artifact publication and persisted notebook replay, and
  • the standalone sqlsaber-notebook CLI.

The saber CLI loads analyze_data when sqlsaber-notebook is installed. That tool uses prior successful query results for calculations, statistics, transformations, and plots. Bounded executed notebooks and plot previews appear in the terminal before the main agent's text response. Notebook bytes and images are not sent to the parent model. Descriptor-bearing results from any query tool are eligible for files and automatic selection. Legacy inline results are supported only for execute_sql. The manifest records the originating tool in source and sets sql only for SQL queries.

An embedded SQLSaber session includes analyze_data only when the notebook factory is listed in SQLSaberOptions.capabilities. Managed SDK applications persist the notebook, plots, and generated files through SQLSaberOptions.artifact_store. Only the store's durable references are stored in tool metadata and exposed through SQLSaberResult.artifacts.

The default balanced runtime targets larger EDA and classical ML: 4 CPUs, 8 GiB memory, and up to 100 MiB per input/250 MiB total. SQLsaber does not cap model requests, notebook cell count, the analyst loop, or the whole operation. Individual cells retain a 10-minute timeout so a stuck computation can be diagnosed without ending the overall analysis. Embedded callers can change these defaults and select a custom image through NotebookConfig on either the capability or direct API. The CLI uses the default budgets and supports SQLSABER_NOTEBOOK_IMAGE for a custom image with additional libraries.

Managed SQLsaber usage

uv tool install --with sqlsaber-notebook sqlsaber
saber

Docker is the default local backend. Microsandbox is an opt-in local backend that runs notebook code in a hardware-isolated Linux microVM without host bind mounts:

uv tool install --with 'sqlsaber-notebook[microsandbox]' sqlsaber
SQLSABER_NOTEBOOK_BACKEND=microsandbox saber

Microsandbox 0.6 is beta. It supports Apple Silicon macOS, Linux x86_64/ARM64 with usable KVM, and preview Windows x86_64/ARM64 hosts with Windows Hypervisor Platform. Intel macOS is not supported. Its OCI cache under ~/.microsandbox is separate from Docker, so the first image preparation can be large and slow. For private or overridden registry images, authenticate them with Microsandbox's registry login support or set SQLSABER_NOTEBOOK_IMAGE to an immutable digest in an accessible registry. Guest networking is disabled, the restricted security profile is requested, and notebook processes receive a process-level PID rlimit. Microsandbox runs locally; query results are not uploaded to a third-party sandbox service.

Select Modal explicitly because query results will be uploaded to a third party:

SQLSABER_NOTEBOOK_BACKEND=modal saber

Daytona is also an explicit remote backend. The deployed legacy control plane requires exactly daytona==0.143.0, which is installed by the extra:

uv tool install --with 'sqlsaber-notebook[daytona]' sqlsaber
export DAYTONA_API_KEY=...
export DAYTONA_API_URL=https://your-daytona.example/api
SQLSABER_NOTEBOOK_BACKEND=daytona saber

Query results and selected local files are uploaded to the configured Daytona service. SQLsaber derives a minimal USER root control image from the exact configured SQLSABER_NOTEBOOK_IMAGE parent, protects inputs as root, and executes notebooks as jovyan. The sandbox requests blocked outbound networking and ephemeral deletion. Daytona 0.143.0 has no hard age-based TTL: its 24-hour setting is inactivity-based. Deployments requiring a strict maximum resource age must run a label-based reaper for sandboxes labeled application=sqlsaber,purpose=notebook.

E2B is an opt-in remote backend:

uv tool install --with 'sqlsaber-notebook[e2b]' sqlsaber
export E2B_API_KEY=...
SQLSABER_NOTEBOOK_BACKEND=e2b saber

Query results and selected files are uploaded to E2B. SQLsaber builds a cached E2B template from SQLSABER_NOTEBOOK_IMAGE (the default pinned Jupyter image when unset), with a root control process and unprivileged jovyan notebook execution. The image must provide /usr/bin/python3, /usr/sbin/runuser, and /opt/conda/bin/jupyter with nbconvert and a Python kernel. The first cold build may take several minutes. Template names include a hash of the image and resource settings; templates remain in your E2B account for reuse and contain no query data. Before building, SQLsaber checks for that template using your E2B credentials. Existing templates are reused without a build request; missing templates are built automatically. Lookup errors fail rather than triggering a rebuild. Pin custom images by digest: changes behind an unchanged image tag do not refresh an existing template. Changing the image reference or resource settings selects a different template. CPU cores round up to whole cores and memory is allocated in MiB at build time.

Inputs are root-owned and read-only to the notebook. Guest internet access is disabled, and generated artifacts are validated and frozen before download. Each sandbox has a one-hour lifetime, including time between notebook runs, and is killed on close or interrupted execution. This provider lifetime applies even when the whole-notebook timeout is disabled. Template builds and sandbox runtime may incur E2B charges. PID limits are not enforced by this adapter.

Run the opt-in live contract test with credentials to verify execution, fresh kernels, read-only inputs, HTTP egress denial, artifact downloads, session publication, and sandbox termination:

SQLSABER_RUN_E2B_INTEGRATION=1 env -u FORCE_COLOR uv run pytest \
  plugins/notebook/tests/test_notebook_e2b_integration.py -q -s

Saved notebook settings

saber plugins edits persistent notebook settings declared by this plugin: the backend, image, and advanced resource and workspace budgets (saber plugins show notebook lists them). Saved values apply when saber loads analyze_data and when sqlsaber-notebook runs standalone. Environment variables override saved values, which override plugin defaults.

Daytona, Modal, and E2B credentials entered there live in the OS keyring as daytona.api_key, modal.token_id, modal.token_secret, and e2b.api_key — never in the settings file — and are passed directly to the provider SDK client without mutating environment variables. Modal tokens are saved as a pair. With no saved credentials, Modal uses its native modal setup login and Daytona reads its own environment, unchanged from previous releases. Selecting a remote backend surfaces the plugin's upload and cost notice.

Embedded SDK usage (SQLSaberOptions, analyze()) never reads these saved settings; hosts configure NotebookConfig explicitly.

Backend isolation differs by provider:

Backend Location Guest network CPU/memory units PID limit Abandonment cleanup
Docker Local Docker none Fractional CPU / MiB Enforced Per-run container removal
Microsandbox Local microVM Disabled Whole CPU / MiB Process rlimit 24-hour max duration
Modal Remote Blocked Fractional CPU / MiB Not exposed 24-hour platform lifetime
Daytona Remote Provider block requested Whole CPU / GiB Not exposed Ephemeral 24-hour inactivity stop; no hard TTL
E2B Remote Blocked by provider firewall Whole CPU / MiB (template build) Not exposed One-hour sandbox lifetime

Daytona image derivation can make the first cold start slower. Network denial, root input ownership, and deletion are verified by credentialed tests, but do not assume PID-limit or complete isolation parity across providers.

Backends never fall back automatically after selection or failure.

Configure a dedicated analyst model with:

saber plugins set notebook model openai:gpt-5

For a web backend, list the notebook factory in SQLSaberOptions.capabilities. Inject an application-owned artifact store. Pass tenant scope as run metadata:

from sqlsaber import FilesystemArtifactStore, SQLSaber, SQLSaberOptions
from sqlsaber_notebook.capability import capability as notebook

options = SQLSaberOptions(
    database="sqlite:///analytics.db",
    artifact_store=FilesystemArtifactStore("/private/artifacts"),
    capabilities=[notebook],
)

async with SQLSaber(options=options) as saber:
    result = await saber.query(
        "Analyze and plot revenue anomalies",
        conversation_id="conversation-123",
        metadata={"tenant_id": "acme"},
    )
    print(result.artifacts)

Implement the cloud-neutral ArtifactStore protocol to use a private database plus S3, GCS, Azure Blob Storage, or another bucket. Authorize get() from current run metadata and return stable private object references rather than expiring signed URLs.

When the notebook factory is listed, managed applications can also configure SQLSaberOptions.workspace_input_resolver to expose authorized private inputs to analyze_data. The model-visible argument is attachment_refs, never raw bytes, paths, URLs, bucket names, or object keys. The resolver receives only run, conversation, tool-call, and application metadata context and returns ordered WorkspaceFile values. The host owns authorization and must reject invented, expired, cross-tenant, or out-of-history references; SQLsaber validates filenames, collisions, immutable bytes, MIME/provenance metadata, and aggregate limits before starting a notebook. When no resolver is configured, attachment_refs is omitted from the tool schema and query-result selection is unchanged.

Query result files are staged first in their selected order, followed by resolver outputs in resolver order. By default, the combined workspace allows 50 files, 100 MiB per file, and 250 MiB total. Filenames are capped at 255 UTF-8 bytes. manifest.json is reserved, has a separate default 1-MiB limit, and records each resolved file's media type and structured string provenance.

Configure notebook resources and the base image

Pass the same immutable NotebookConfig to a configured capability factory or to analyze(). Configuration belongs to the host application, not model tool arguments.

from functools import partial

from sqlsaber import SQLSaberOptions
from sqlsaber_notebook import NotebookConfig, WorkspaceLimits
from sqlsaber_notebook.capability import capability as notebook

MiB = 1024**2
GiB = 1024**3
config = NotebookConfig(
    backend="docker",
    image="registry.example.com/analysis-with-ml:2026-09",
    workspace=WorkspaceLimits(
        max_files=2_000,
        max_file_bytes=512 * MiB,
        max_total_bytes=2 * GiB,
        max_manifest_bytes=4 * MiB,
    ),
    memory_mb=16_384,
    cpu_cores=8,
    cell_seconds=3_600,
    max_artifact_bytes=512 * MiB,
    max_total_artifact_bytes=GiB,
)

options = SQLSaberOptions(
    database="analytics",
    capabilities=[
        partial(
            notebook,
            config=config,
            model_name="openai:gpt-5-mini",
            api_key="application-managed-key",
        )
    ],
)
# The independent API accepts the same object:
# result = await analyze(goal, workspace, model=model,
#                        model_provider=provider, config=config)

Use an image accessible to the selected backend, preferably pinned by digest. Extend the default Jupyter scipy image to preserve its runtime layout, Python kernel, and jupyter nbconvert installation. The remote adapters also depend on the jovyan user, /opt/conda/bin/python, and /usr/sbin/runuser. This option selects the base image; it does not install dependencies or allocate GPUs.

Explicit analyze(backend=..., image=...) arguments override the corresponding config selectors. Unspecified selectors fall back to SQLSABER_NOTEBOOK_BACKEND and SQLSABER_NOTEBOOK_IMAGE, then library defaults. Empty selectors are errors. backend can also be an application-supplied NotebookBackend instance.

Workspace budgets exclude manifest.json; SQLsaber adds its allowance when staging inputs. workspace.default_results controls how many recent query results are considered when files is omitted, defaulting to 20. Automatic selection stops at the file-count or combined-byte budget. Explicit selections fail rather than being partially admitted. An individual file above max_file_bytes is always an error.

cell_seconds=None disables the cell timer. command_seconds=None, the default, disables the timer for one whole-notebook execution. Neither disables provider lifetime limits. image_prepare_seconds and open_seconds bound provisioning and transfer operations. CPU, memory, and PID enforcement remain backend-specific. Numeric budgets must be positive; only the two execution timers accept None.

max_artifacts, max_artifact_bytes, max_total_artifact_bytes, and max_notebook_bytes control generated output and transfer budgets, not scratch-disk quotas. max_log_chars bounds backend diagnostics. Model-preview, image-history, and source-text budgets remain unchanged. The analyst's list_workspace tool reports configured budgets and file totals, with 50-file pages and next_offset.

analyze() accepts resource settings only through config=. This replaces the removed execution_limits= argument. When migrating input limits, specify user-file budgets in WorkspaceLimits without adding manifest overhead. Increasing budgets does not change the bytes-based input interface or fresh-kernel execution: inputs still occupy host memory, and each edit reruns all cells.

Direct embedded usage

Analysis and publication are separate operations. This keeps the analyst independent of SQLsaber storage while giving embedded callers the same canonical publication as the managed capability:

from sqlsaber import ArtifactContext, FilesystemArtifactStore
from sqlsaber_notebook import Workspace, WorkspaceFile, analyze, publish_analysis

workspace = Workspace.from_files(
    [
        ("sales.csv", sales_csv_bytes),  # Backwards-compatible tuple form
        WorkspaceFile(
            "preview.jpeg",
            preview_bytes,
            media_type="image/jpeg",
            provenance={"attachment_id": "attachment-1"},
        ),
    ]
)
result = await analyze(
    "Plot monthly revenue and explain anomalies",
    workspace,
    model="anthropic:claude-sonnet-4-6",
    model_provider="anthropic",
    collect_files=True,
)
publication = await publish_analysis(
    result,
    store=FilesystemArtifactStore("/private/artifacts"),
    context=ArtifactContext(
        conversation_id="conversation-123",
        metadata={"tenant_id": "acme"},
    ),
)

WorkspaceFile is provider-neutral: embedded callers supply their own trusted bytes, while the standalone CLI continues to accept explicit local paths. Staged images are files, not initial multimodal child-model content; the analyst can display them and inspect the existing bounded PNG snapshots. The tuple form of Workspace.from_files remains supported.

publish_analysis writes analysis.ipynb, ordered plots/plot_<n>.png members, and bounded generated files below files/. It forwards the supplied context to the application-owned store and raises if publication fails.

Standalone usage

uv run sqlsaber-notebook \
  --model anthropic:claude-sonnet-4-6 \
  --backend docker \
  --output analysis.ipynb \
  "Compare revenue by region and explain material anomalies" data.csv

Standalone mode writes the explicit --output notebook and, when needed, a sibling <output-stem>_artifacts directory. It does not use SQLsaber conversation storage or its user-data artifact directory.

The standalone CLI shares the notebook settings saved with saber plugins, including keyring credentials. The backend comes from the explicit --backend flag, then SQLSABER_NOTEBOOK_BACKEND, then saved settings, then the docker default. The plugin's enabled flag only governs automatic loading into saber; a disabled plugin still runs standalone.

Remote backends are never selected as automatic fallbacks. Select one explicitly because local files will be uploaded to that provider:

modal setup
SQLSABER_NOTEBOOK_BACKEND=modal uv run sqlsaber-notebook \
  --model anthropic:claude-sonnet-4-6 \
  "Analyze this dataset" data.csv

DAYTONA_API_KEY=... DAYTONA_API_URL=https://your-daytona.example/api \
  SQLSABER_NOTEBOOK_BACKEND=daytona uv run sqlsaber-notebook \
  --model anthropic:claude-sonnet-4-6 \
  "Analyze this dataset" data.csv

Development

uv sync
uv run pytest plugins/notebook/tests -q

Run live backend integration tests explicitly:

SQLSABER_RUN_DOCKER_INTEGRATION=1 \
  uv run pytest plugins/notebook/tests/test_notebook_docker_integration.py -q

SQLSABER_RUN_MICROSANDBOX_INTEGRATION=1 \
  uv run --project plugins/notebook pytest \
  plugins/notebook/tests/test_notebook_microsandbox_integration.py -q

SQLSABER_RUN_MODAL_INTEGRATION=1 \
  uv run pytest plugins/notebook/tests/test_notebook_modal_integration.py -q

SQLSABER_RUN_DAYTONA_INTEGRATION=1 \
  uv run pytest plugins/notebook/tests/test_notebook_daytona_integration.py -q

Metadata

Release files for sqlsaber-notebook 0.10.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for sqlsaber-notebook 0.10.0
File Size Uploaded
sqlsaber_notebook-0.10.0.tar.gz 307.2 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for sqlsaber-notebook 0.10.0
File Interpreter ABI Platform
sqlsaber_notebook-0.10.0-py3-none-any.whl Python 3 none any Details

Total release size: 382.5 kB

Release files / sqlsaber_notebook-0.10.0.tar.gz

Download URL sqlsaber_notebook-0.10.0.tar.gz
Size 307.2 kB
Tags Source
SHA-256 checksum
How to use checksums
6f9d39a3534f08608ae02484c9b8846e03f02d294c6d3eecc666e4a0c417271b
BLAKE2b-256 checksum
How to use checksums
aa69f477be9125c09173bb230d53dee2799ee3225bca622db110e6ab1d2aaaa6
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.12.19 {"installer":{"name":"uv","version":"0.12.19","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Release files / sqlsaber_notebook-0.10.0-py3-none-any.whl

Download URL sqlsaber_notebook-0.10.0-py3-none-any.whl
Size 75.3 kB
Tags Python 3
SHA-256 checksum
How to use checksums
e8128228bfc528d1bc518b514b4cb47be0554e7dba14625956356b0fa75b5f58
BLAKE2b-256 checksum
How to use checksums
fa19417ffd8e6b604a560bf046dd4657706e2048655e963fbc5757798fa406ce
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.12.19 {"installer":{"name":"uv","version":"0.12.19","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Release history Release notifications | RSS feed

This release

0.10.0 This release

2 release files

0.9.1

2 release files

0.9.0

2 release files

0.8.0

2 release files

0.7.0

2 release files

0.6.0

2 release files

0.5.0

2 release files

0.4.1

2 release files

0.4.0

2 release files

0.3.0

2 release files

0.2.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page