shidoshi
An opinionated way to augment Jupyter Lab for iterative work.
shidoshi adds %ask / %%ask magics to Jupyter that let you talk to an LLM
from inside a notebook — using the notebook itself, in order, as the
conversation history. No separate chat pane, no copy-pasting context: your
code cells, their outputs, and your notes are the context.
Install
Requires Python ≥3.13 and JupyterLab. Set an API key before use:
export OPENAI_API_KEY=sk-... # for the openai provider (default)
export OPENAI_BASE_URL=... # optional, e.g. to point at a proxy
export OPENROUTER_API_KEY=... # for the openrouter provider
Installing across Jupyter environments
%load_ext shidoshi runs import shidoshi inside the running kernel
process. That means shidoshi has to be installed into whichever Python
environment the kernel you're using actually runs in. It ships a small
shidoshi command for setup, but the library itself is not a standalone
tool — so uvx / uv tool install (which run a tool in an isolated
subprocess, separate from any kernel) don't apply here.
-
Per-project venv with its own JupyterLab (e.g. a
uv-managed project): add shidoshi as a normal dependency of that project.uv add shidoshi # or: pip install shidoshi
-
One shared JupyterLab, many kernels (each notebook's kernel points at a different project venv registered via
ipykernel install): install shidoshi into each kernel's venv. Installing it only where JupyterLab itself lives will not make it importable from other kernels.# inside the venv backing a given kernel uv add shidoshi # or: pip install shidoshi
Skipping %load_ext — the shidoshi kernel
To avoid typing %load_ext shidoshi in every notebook, register a kernel that
loads it for you:
shidoshi install-kernel --sys-prefix
Pick Python 3 (shidoshi) from the Jupyter kernel list and the magics are
already there. Nothing else changes: it is a stock Python kernel running this
environment's interpreter — your imports, variables, and debugger all work
exactly as before. The generated kernel.json just appends
--IPKernelApp.extensions=shidoshi to the normal ipykernel_launcher
command, with an absolute path to this environment's Python.
Useful flags:
| flag | effect |
|---|---|
--sys-prefix |
install into the active venv (best for a project venv) |
--user |
install into your per-user kernel directory |
--prefix PATH |
install into an explicit prefix |
--name / --display-name |
override the ids — use a distinct --name per environment if you register more than one |
--env KEY=VALUE |
set an environment variable for the kernel process (repeatable) |
--force |
replace an existing kernelspec of the same name (logos in it are kept) |
Installing is refused if a kernelspec of that name already exists, so it won't
quietly replace one you made by hand. Register one per environment with a
distinct --name.
Remove it with jupyter kernelspec remove shidoshi.
With uv
Add shidoshi to the project, then register the kernel from inside it. Which
location flag you need depends on where JupyterLab itself runs from, because
Jupyter only searches its own sys.prefix, your user directory, and the system
directory:
uv add shidoshi
# A: JupyterLab in an ephemeral env (uv's default suggestion).
# Its sys.prefix is a uv cache dir, so --sys-prefix would be invisible.
uv run shidoshi install-kernel --user
uv run --with jupyter jupyter lab
# B: JupyterLab as a project dependency — sys.prefix *is* the project venv.
uv add --dev jupyterlab
uv run shidoshi install-kernel --sys-prefix
uv run jupyter lab
B keeps the kernel scoped to the project and disappears with the venv; A is
the one that works with uv run --with jupyter. If a freshly installed kernel
doesn't show up in the launcher, run jupyter kernelspec list the same way
you start Lab — that prints exactly the directories being searched.
Either way this replaces the kernel step in
uv's Jupyter guide —
you don't need uv run ipython kernel install --env VIRTUAL_ENV ... as well,
because install-kernel records VIRTUAL_ENV for you when it detects a venv.
That variable matters more than it looks. uv pip install resolves its target
from VIRTUAL_ENV (falling back to CONDA_PREFIX, then a base interpreter) —
never from the kernel that's running. So if you start Jupyter from a
conda-activated shell, a kernel without VIRTUAL_ENV will import from your
project venv while !uv pip install quietly installs into your conda base.
Pinning it keeps both views on the same environment.
Two related notes:
!uv addwas always safe — it finds the project by walking up forpyproject.toml, so it ignoresVIRTUAL_ENVand targets the project venv either way.%pip installneedsuv venv --seed; uv venvs have nopipin them by default. Prefer!uv add.
Pass --env to set anything else the kernel should launch with (repeatable),
including an override for VIRTUAL_ENV:
uv run shidoshi install-kernel --sys-prefix --env OPENAI_BASE_URL=http://127.0.0.1:18080/v1
Because the spec pins an absolute interpreter path, it can only ever start the
environment shidoshi is installed in. That is the advantage over the
ipython_config.py route below: ~/.ipython is shared by every Python
environment under your $HOME, so putting the extension there makes every
kernel on the machine try to import shidoshi, including ones that don't have
it.
Auto-loading via ipython_config.py instead
Add to ~/.ipython/profile_default/ipython_config.py (create it first with
ipython profile create):
c.InteractiveShellApp.extensions = ["shidoshi"]
Only safe if shidoshi is installed in every environment you use for
Jupyter on that machine. To scope it, create a named profile
(ipython profile create shidoshi), put the extensions line in that
profile, and add "--profile=shidoshi" to the relevant kernel's argv.
Quickstart
%load_ext shidoshi
(skip this line if you're on the Python 3 (shidoshi) kernel)
%%ask
What does the `history.build_history` function in this file do?
The response streams into the cell's output as Markdown.
Magics reference
%ask <prompt>— line magic for a one-line prompt.- Prefix with
model|orprovider:model|to override the configured default model for just this call, e.g.%ask openrouter:openai/gpt-4o|summarize this. - Add
--debuganywhere on the line to also show the full request/response payload.
- Prefix with
%%ask [model]— cell magic; the whole cell body is the prompt (multi-line is fine, and it can reference images via Markdown![]()/<img>syntax or bare local file paths — they're inlined as base64). An optional model name on the magic line overrides the default for this call. Also supports--debug.%%skip— runs the cell normally, but the cell is left out of the context sent to the model entirely. Use it for scratch or exploratory cells you don't want the model to see.%%pin— runs the cell normally; its content and output are always included in context and are exempt from the auto-trim behavior below. Use it to protect a fact, constant, or definition you don't want dropped over a long session.
How context is built
Every prior cell in the notebook — up to the one you're currently running, and accounting for kernel restarts — is turned into conversation history automatically:
- Markdown cells become background text/image context (treated as notes or reference material, not instructions).
- Regular code cells appear as fenced code plus their text/image outputs.
- Prior
%ask/%%askcells become real user/assistant turns. Their responses are reused from a per-cell cache rather than re-sent, so replaying history doesn't resend answers the model already produced. %%skipcells are dropped entirely.%%pincells are always kept.
Automatic context-length handling
If a request is rejected for exceeding the model's context window, shidoshi
automatically retries, dropping the oldest trimmable history units first
(markdown cells, then plain code cells, then whole ask+response pairs —
%%pin cells are never dropped), up to 20 times. A banner reports how many
cells were dropped so you know context shrank.
Providers & tools
openai(default) — uses the OpenAI Responses API.openrouter— uses OpenRouter's chat-completions API; select it with theprovider:modelprefix, e.g.openrouter:anthropic/claude-3.5-sonnet.
Every request currently has the built-in web_search tool attached, so the
model can search the web when it needs current information. (A web_fetch
tool also exists in the codebase but isn't wired into the magics yet — not
available today.)
Debug mode
Add --debug to %ask/%%ask to render a collapsible, syntax-highlighted
panel showing exactly what was sent (system prompt, full message history,
tools) and every raw event streamed back — useful when the model's behavior
is surprising and you want to see the actual payload.
Configuration
shidoshi reads TOML config, layered as defaults → ~/.shidoshi/config.toml
→ ./.shidoshi/config.toml (project config overrides user config):
default_model = "gpt-5.5" # model used when none is specified
reasoning_effort = "low" # OpenAI only
ask_color = "#eafbea" # highlight color for %ask/%%ask cells
skip_color = "#ececec" # highlight color for %%skip cells
Development
uv sync
uv run pytest tests/unit tests/btp -v
Integration tests under tests/integration/ require a live OPENAI_API_KEY
(or a proxy via OPENAI_BASE_URL) and are run with:
uv run pytest tests/integration/ -v -m integration
License
Apache License 2.0 — see LICENSE.
Release files for shidoshi 0.0.2
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| shidoshi-0.0.2.tar.gz | 1.6 MB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| shidoshi-0.0.2-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 1.6 MB
Release files / shidoshi-0.0.2.tar.gz
| Download URL | shidoshi-0.0.2.tar.gz |
|---|---|
| Size | 1.6 MB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
88de811c24a7a47cbfa075cacf92f5098fe0a15b4f21af9501c5261c0b4d14aa
|
|
BLAKE2b-256 checksum How to use checksums |
b02338e3e14d15d395a6fe456785b89ea7d0645676326ea66edcf4341d98625b
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Aug 11, 2026.
Transparency logRelease files / shidoshi-0.0.2-py3-none-any.whl
| Download URL | shidoshi-0.0.2-py3-none-any.whl |
|---|---|
| Size | 44.1 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
1c0f8c41e821ccb254dfd0c3e576fd7a91cddda76316be8594d46e407653a5e9
|
|
BLAKE2b-256 checksum How to use checksums |
1889b4050bb01a48be8abf873fe3c9ac9280bf4c3d2ddd438f0177d388d005e6
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Aug 11, 2026.
Transparency log