• 简体中文
  • Code Interpreter

    代码 API 统一执行 Python 和 JavaScript。调用时传入语言和代码,daemon 会选择对应的运行时并返回结果。

    可用运行时包括原生 REPL、内嵌 IPython kernel 和外部 Jupyter 兼容服务。daemon 会根据语言和当前配置选择运行时,调用方不需要指定具体后端。

    代码会话(code session)在多次调用之间保持解释器进程存活:上一次调用定义的东西,下一次还在。不带会话时,每次执行都是一次性的,进程用完即弃。

    需要执行 shell 命令而不是一段代码时,用 Commands。v1 和 v2 路由的对照见 从 1.x 迁移

    运行要求

    GET /v1/capabilitiescode_interpreter 下描述该 plane:

    • status —— 任一解释器能应答就是 ready,都没有时是 absent
    • backend —— 运行 Python 的是哪一层:nativekernelendpoint
    • kinds —— codenodejs,kernel 支撑 Python 时还有 jupyter
    • default_pythondefault_node —— 原生层实际启动的二进制

    原生层需要 PATH 上有 python3node;kernel 层还需要能 import 的 ipykernel。daemon 会根据可用的运行时选择对应的执行层。

    执行代码

    curl -X POST "$BASE_URL/v2/code/execute" \
      -H "Content-Type: application/json" \
      -d '{"language": "python", "code": "print(sum([1, 2, 3]))"}'

    请求体示例:

    {
      "language": "python",
      "code": "print(sum([1, 2, 3]))"
    }
    curl -X POST "$BASE_URL/v1/code/execute" \
      -H "Content-Type: application/json" \
      -d '{"language": "python", "code": "print(sum([1, 2, 3]))"}'

    请求体示例:

    {
      "language": "python",
      "code": "print(sum([1, 2, 3]))"
    }

    请求体包含以下字段:

    字段取值含义
    languagepython, javascript, required用哪个解释器执行代码
    codestring, required要执行的代码
    session_idstring运行所在的会话;不传即一次性执行
    statefulboolean只传 true 会开一个会话并返回其 id
    timeout1–900 s (default 30)这次执行最长可以跑多久
    cwdstring工作目录;在进程启动时固定
    userstring运行身份;只有 root 才能切换

    language 也接受 python3nodenodejsjs;路由不认识的字段会被忽略。

    如果 daemon 无法切换到请求指定的 user,这不属于请求格式错误。请求仍返回 HTTP 200,但 statuserror,并在 ProcessError 输出中说明只有 root 可以切换身份。

    kernel 会忽略 user,始终使用 daemon 自己的账户运行。

    data 包含:

    • languagecode —— 解析出的语言,以及实际执行的代码
    • status —— okerrortimeout
    • outputs —— notebook 输出列表,执行产生的每样东西一条
    • stdoutstderr —— 各自独立的流;为空时 v2 是 "",v1 是 null
    • exit_code —— 正常执行完是 0;报错或超时后是 1
    • execution_count —— 会话的 cell 计数器,从 1 开始
    • session_id —— 运行它的会话;一次性执行时是 null

    status 表示本次执行结果,返回结构中的 success 与它对应。

    代码抛出异常或执行超时仍返回 HTTP 200,但 successfalse

    空的 code 字符串是 v1 和 v2 的一个差异:v2 返回 422;v1 会执行空 cell,返回 200,且没有输出。

    outputs 是 notebook 输出列表:

    output_type携带
    streamnamestdoutstderr)和 text
    execute_resulttext/plaindata:最后一个表达式的值
    display_dataimage/png 等 mime bundle 的 data
    errorenameevaluetraceback

    JavaScript 就是换一个 language 的同样请求:console.log([1, 2, 3].reduce((a, b) => a + b, 0)); 同样打印 6

    另有两个按语言拆分的路由,执行相同的代码,但会话语义不同:

    运行时信息

    curl "$BASE_URL/v2/code/info"
    curl "$BASE_URL/v1/code/info"

    两个接口返回同一份 body —— 当前生效的后端、语言和上限:

    • backend —— 这里运行 Python 的是哪一层:nativekernelendpoint
    • kinds —— 存在的运行时:pythonnodejs,有 kernel 时还有 jupyter
    • languages —— language 接受哪些值
    • pythonnode —— 各是 {available, version}
    • max_sessions —— 同时最多能开多少个会话
    • default_timeoutmax_timeout —— 30900
    • prewarmed —— 有多少个预热进程在等着;默认是 0

    在调用之间保留状态

    daemon 没见过的 session_id 会在请求时创建。只传 stateful: true 时,响应会返回一个生成的 id。

    会话状态保存在内存中:同一会话内会跨调用保留,但会在 aiod 重启后消失。

    路由作用说明
    POST /v2/code/sessions提前开一个会话language,可选 session_idcwduser
    GET /v2/code/sessions列出全部按会话 id 索引
    GET /v2/code/sessions/{id}读取一个未知 id 返回 404
    DELETE /v2/code/sessions/{id}结束一个未知 id 返回 200 并带 deleted: false
    DELETE /v2/code/sessions结束全部返回 cleaned_sessions

    会话条目包含:

    • session_idlanguagecwd —— 它是什么,在哪里运行
    • created_atlast_used —— 毫秒时间戳
    • age_seconds —— 距离上次执行过了多少秒
    • max_idle_time —— 关闭前允许空闲的毫秒数
    • state —— idleexecuting

    kernel 支撑的 Python 会话也列在这里,带的是 kernel_name 而不是 max_idle_time

    下面展示共用同一个会话的两次调用。AioExamples 中用于解析返回结构的辅助客户端:

    Python
    TypeScript
    sb = Aio(BASE_URL)
    
    # 1. Load the data once; the session keeps it.
    sb.post(
        "/v2/code/execute",
        language="python",
        session_id="analysis",
        code="import statistics\nrows = [120, 135, 150, 142, 168, 180]",
    )
    
    # 2. Ask a question about it; `rows` is still there.
    run = sb.post(
        "/v2/code/execute",
        language="python",
        session_id="analysis",
        code="print(round(statistics.mean(rows), 1))",
    )
    print(run["stdout"].strip(), run["execution_count"])   # 149.2 2
    
    # 3. Drop the session when the task is done.
    sb.delete("/v2/code/sessions/analysis")

    会话按语言分开列出和结束,各归各的路由:

    路由作用说明
    GET /v1/nodejs/sessions列出 JavaScript 会话POST 可以提前开一个
    DELETE /v1/nodejs/sessions/{id}结束一个返回 deleted
    GET /v1/jupyter/sessions列出 kernel 支撑的 Python 会话装了 ipykernel 时可用
    DELETE /v1/jupyter/sessions/{id}结束一个原生 Python 会话只能等空闲到期

    共用一个会话的两次调用,走 SDK:

    Python
    TypeScript
    client = Sandbox(base_url=BASE_URL)
    
    # 1. Load the data once; the session keeps it.
    client.code.execute_code(
        language="python",
        session_id="analysis",
        code="import statistics\nrows = [120, 135, 150, 142, 168, 180]",
    )
    
    # 2. Ask a question about it; `rows` is still there.
    run = client.code.execute_code(
        language="python",
        session_id="analysis",
        code="print(round(statistics.mean(rows), 1))",
    ).data
    print(run.stdout.strip(), run.execution_count)   # 149.2 2
    
    # 3. Drop the session when the task is done.
    client.jupyter.delete_session("analysis")

    哪些 SDK 调用可以直接访问这些路由,见 1.x SDK 兼容性

    kernel 支持 Python 时,code 会话和 Jupyter 会话可以共享命名空间。两边使用同一个 session_id,例如 {"session_id": "s1"},就会访问同一个 kernel;任一接口都可以结束该会话。

    常见用法

    大多数执行都是一次性的。

    场景调用方式然后
    print(sum(...))一次性直接从同一个响应里读 stdout
    在一份数据上反复迭代具名会话复用同一个 id,变量都还在
    while True: passtimeoutstatus: "timeout",见下面的表
    代码抛异常任意方式outputs[].traceback 回喂给模型
    Ruby、Go 或别的语言当成命令跑扩展更多语言

    超时与上限

    执行超过 timeout 时,返回结果中的 status"timeout"。超时后的处理方式和资源开销取决于后端:

    超时后上限
    原生 REPLexecution timed out after 2000ms; session state was reset20 个会话,空闲 1800 秒后关闭
    内嵌 kernel先一个 KeyboardInterrupt,再 execution timed out after 2000ms and was interrupted5 个会话,空闲 300 秒后关闭

    原生层会在超时后再等待 5 秒,然后终止解释器。

    如果执行在这段时间内结束,仍然可以读取输出;会话会保留,但命名空间会重置。

    kernel 层只中断当前 cell,kernel 和此前定义的内容都会保留。

    两组上限分别由以下配置控制:

    • 原生 REPL:AIO_CODE_MAX_SESSIONSAIO_CODE_SESSION_TIMEOUT_SECS
    • kernel:AIO_KERNEL_MAX_SESSIONSAIO_KERNEL_SESSION_TIMEOUT_SECS

    超过对应上限时返回 429,已有会话不会被清除。返回消息分别是 Maximum number of sessions (20) reachedMaximum number of kernel sessions (5) reached

    选择后端

    Python 按以下顺序选择后端:

    1. AIO_JUPYTER_ENDPOINT 指向的外部 Jupyter 兼容端点,能连通时用它
    2. 内嵌 kernel,装了 ipykernel 时用它
    3. daemon 原生的 Python REPL

    JavaScript 始终使用原生 REPL,只要 PATH 中存在 node 即可。

    默认不预热任何后端。AIO_CODE_PREWARMAIO_KERNEL_PREWARM 的默认值都是 0

    AIO_CODE_BACKENDautonativekernel)可以固定 Python 后端,跳过上述顺序。

    扩展更多语言

    language 接受 python(也接受 python3)和 javascript(也接受 nodenodejsjs),其他值返回 422。要运行其他语言,可以按实现成本选择以下三种方式:

    • 任意解释器,不保留状态。 通过 Commands 当成命令来跑,比如 ruby -e "puts 1"。不需要任何配置,只是没有会话状态,输出也不是 notebook 的格式。
    • 换一个 Python 或 Node 版本。 PYTHON_VERSION / NODE_VERSION 决定原生层从 PATH 上选择哪个 python3 / node/v1/jupyter 使用 kernel_name 从已安装的 kernel 中选择。见 Jupyter
    • 为 code plane 增加语言支持。 原生层每种语言只有一个很小的、只用标准库的 harness,编译进二进制(harness.pyharness.js)。

    daemon 向 harness 的 stdin 逐行写入 {"code", "timeout_ms"},再从 stdout 逐行读取 {"stdout", "stderr", "result", "error"}

    每个会话对应一个进程。

    新增语言时,需要为对应解释器编写这样的 harness,并在 daemon 的 Language 列表中登记。

    HTTP 接口、会话、超时和上限都可以复用现有实现。

    错误处理

    代码异常、执行超时和解释器进程崩溃都会返回 HTTP 200,并将 success 设为 falsestatuserrortimeout

    只有请求本身无效时才返回错误状态码:

    情况结果
    language 不支持、缺 codetimeout 不在 1–900 内422
    cwd 不是一个目录422
    空的 code 字符串v2 返回 422,v1 返回 200
    找不到对应语言的解释器503,并说明它找的是什么
    会话数达到上限429
    GET .../sessions/{id} 的会话 id 未知404

    v2 拒绝请求时,原因写在 message 中,例如 Unsupported language 'ruby'. Supported: python, javascript

    v1 将同样的原因放在 errors 列表中。每个被拒绝的字段占一项,并带有 location: ["body", "language"]type: "enum"

    跨接口的通用约定见 错误处理