Jupyter
/v1/jupyter 在真正的 IPython kernel 里执行 Python。它覆盖原生 Python REPL 做不到的场景,代价是进程开销更大:
- 富输出(
display_datamime bundle,比如 matplotlib 的 PNG) - IPython magic 命令
- 在已装的多个 Python 版本之间选择
aiod 自己实现这一层:启动 ipykernel,直接用 Jupyter kernel 协议与它通信。不涉及 Jupyter server,也不需要额外起任何东西。
运行要求
aiod 选用的 Python 解释器必须能 import ipykernel。如果没有安装,请先执行 pip install ipykernel,再启动 daemon。
daemon 只在启动时探测一次。安装完成后,重启 daemon,再检查 GET /v1/capabilities:code_interpreter.jupyter_backend 应为 "kernel",python_kernels 会列出可用的 kernel。
未安装时,/v1/jupyter/* 路由不可用,并会说明修复方式;/v1/code/info 也不会声明 jupyter 这一 kind。
如果设置了 AIO_JUPYTER_ENDPOINT 且可以连接,外部 Jupyter server 会优先于内嵌 kernel,daemon 会将 /v1/jupyter 原样代理到该服务。
执行代码单元
提交这一格的源码,响应中包含 notebook 输出:
请求体示例:
data 是:
code—— 实际运行的代码status——ok、error或timeoutexecution_count—— kernel 的 cell 计数器outputs—— notebook 输出对象(stream、execute_result、display_data、error)session_id—— 运行它的会话;一次性执行时是nullkernel_name—— 运行它的 kernelmsg_id—— 该请求的 Jupyter 消息 id
每个输出对象上所有键都在,用不到的是 null,所以客户端可以直接读 outputs[i].text 或 outputs[i].data,不必先按类型分支。
请求体字段:
daemon 不支持的 kernel_name 返回 422,例如:unknown kernel 'ir'; available: python3, python3.14。
cwd 不是目录不算请求错误。该 cell 返回 200,success 为 false,并带一条写明路径的 DirectoryError 输出。
查看 kernel 信息
available_kernels 是探测出来的,不是声明的:里面的每个名字都可以作为 kernel_name 传进来。返回里还有 description 和 kernel_detection 两行,给直接看原始 JSON 的人。
输出格式
一个输出对象是四种形态之一:
matplotlib 要返回 PNG,需要使用 %matplotlib inline。只使用 Agg 后端时,图会写入文件,该 cell 不返回 display_data。
image/png 是 base64,这张图大约 27 KB。cell 写入的文件也是普通文件:通过 文件 API 读取。
一次性执行与会话
不传 session_id 和 stateful: true 时,请求只执行一次。daemon 会从预热池取一个 kernel;没有可用 kernel 时,再为这次请求启动一个。执行结束后,kernel 也会关闭。
因此,一次性执行中定义的变量不会保留到下一次请求。再次使用时会得到 NameError。
传 stateful: true 后,响应会返回一个会话 id;也可以自行指定 session_id。后续请求带上同一个 id,就会在同一个命名空间中继续执行。
一个会话在整个生命周期内独占一个 kernel,execution_count 会在多次调用之间连续累加。
会话没有 interrupt 和 restart:超过 timeout 的 cell 会被自动中断,结束会话用 DELETE。这两个路径返回 501 并说明这一点。
会话由 kernel 和 id 共同标识。因此,默认 kernel 上的 s1 与另一个 kernel 上的 s1 属于两个不同的命名空间。
后者会以组合键 python3.14:s1 列出。如果该 kernel 同时支撑 Code 路由,在那里传相同的 id 也会访问该会话的命名空间。
kernel 的工作目录在启动时固定。请求的 cwd 与池里所有 kernel 都不同时,会启动一个新 kernel;池里的 kernel 不会被移动。
超时与输出上限
一个 cell 超过 timeout 后,daemon 会先通过 control 通道发送 interrupt_request,再等待 2 秒。
如果 kernel 及时停止,会话仍然保留,cell 的 status 为 "timeout"。输出中会先出现 KeyboardInterrupt,再出现 execution timed out after 2000ms and was interrupted。
忽略中断的 kernel 会被终止,会话随之丢弃。
每个 cell 最多返回 2,000,000 个字符。流式文本超过上限时,保留开头部分,并追加一条 stderr:[output truncated at 2000000 characters]。
同一个 cell 后续产生的输出仍会返回,额外保留 64 KiB。单条结果或图片超过上限时无法截断,会直接丢弃,并只保留这条提示。
最多同时运行 5 个会话(AIO_KERNEL_MAX_SESSIONS),每个会话空闲 300 秒后回收(AIO_KERNEL_SESSION_TIMEOUT_SECS)。
第 6 个会话请求返回 429,消息为 Maximum number of kernel sessions (5) reached。已有会话不会被清除。
一个 kernel 占用约 60 MB 内存。这两个上限比原生 REPL 的 20 个会话 / 1800 秒更紧。
AIO_KERNEL_PREWARM(默认 0)保持这么多个空闲 kernel 预热。启动后的第一个 cell 因此是约 90 ms 而不是 1.4 秒。池在启动时填满,每次用掉后在后台补充。
内存占用
相同限制下(0.5 CPU、0.5 GiB)对比 aiod 和 1.x 镜像:aiod 运行 nginx 和一个预热 kernel,1.x 镜像开启 Jupyter、关闭浏览器和 VNC。
kernel 本身占用相近,差异来自外围服务:空闲时 aiod 占 69.8 MB,1.x 镜像占 248.4 MB。