plaita.node¶
节点定义与 NodeRegistry。包含 Node 基类、默认内置节点(Start / End / Switch / Assignment / Bool / SwitchLegacy / Loop / Map / Filter / Find / Reduce / InlineFlow / ReferenceFlow / Parallel / HTTP / EventNode)、默认注册表与插件发现。
CodeNode 不在默认注册表
0.5.0 起 CodeNode 需 register_code_node() 显式注册(默认 docker 沙箱)。见 内置节点 - code 与 迁移指南。
node
¶
plaita.node — Node definitions and the NodeRegistry.
The NodeRegistry class provides a scoped, dict-backed registry for
node types. A module-level default registry is created at import time
and populated with all built-in nodes plus any nodes discovered via
importlib.metadata entry_points (group plaita.nodes).
Backward-compatible helpers (node_register, parse_node) emit
DeprecationWarning; the nodes dict proxy is still silent.
NodeRegistry
¶
NodeRegistry(*, auto_discover: bool = False, parent: Optional[NodeRegistry] = None)
Scoped registry for Plaita node types.
参数:
| 名称 | 类型 | 描述 | 默认 |
|---|---|---|---|
auto_discover
|
bool
|
If True, scan |
False
|
parent
|
Optional[NodeRegistry]
|
If supplied, the new registry is initialised with a copy of the parent's registered nodes. |
None
|
源代码位于: plaita/node/__init__.py
register
¶
Register a node class. Returns node_cls so it can be used as a decorator.
覆盖已注册的不同类时发 warning——历史上静默顶掉(包括劫持内置
start/end 等类型),排查时完全无线索。同类型重复注册(如
importlib 重复加载场景)不告警。
源代码位于: plaita/node/__init__.py
parse_node
¶
parse_node(node_dict) -> Node
Create a Node instance from a dict (or return an existing Node).
源代码位于: plaita/node/__init__.py
copy
¶
copy() -> NodeRegistry
Return an independent shallow copy of this registry.
源代码位于: plaita/node/__init__.py
discover
¶
Lazily discover node plugins via plaita.nodes entry points.
Idempotent: only scans on the first successful call for this
registry instance. A scan that raises (entry_points() itself
failing) leaves the registry undiscovered so the next call retries —
98: a transient environment failure must not freeze the node set for¶
the lifetime of the process.
源代码位于: plaita/node/__init__.py
init_default_registry
¶
init_default_registry(*extra_nodes: Type[Node], auto_discover: bool = True) -> NodeRegistry
显式 (重新) 初始化进程级默认 NodeRegistry。
历史上默认 registry 是模块级 _default_registry = NodeRegistry() 在 import
期静默创建, 插件发现 (plaita.nodes entry points) 在首次
get_default_registry() 时隐式触发——"隐式可变单例 + import 期副作用"是
典型反模式: 测试间状态污染、插件加载顺序敏感、复现困难。本入口把它变成
显式启动步骤:
- 清空并重建默认 registry (重注册内置节点);
auto_discover=True时扫描plaita.nodesentry points 装载插件节点;- 把
extra_nodes(节点类) 逐个注册进去; - 标记为"已显式初始化", 之后
get_default_registry()不再发隐式初始化提示。
建议在应用启动脚本顶部调用一次::
from plaita.node import init_default_registry, register_code_node
init_default_registry() # 显式装载内置 + 插件节点
register_code_node(default_backend="docker") # 按需 opt-in CodeNode
参数:
| 名称 | 类型 | 描述 | 默认 |
|---|---|---|---|
*extra_nodes
|
Type[Node]
|
额外要注册的 |
()
|
auto_discover
|
bool
|
是否扫描 entry points 装载插件节点, 默认 True。 |
True
|
返回:
| 类型 | 描述 |
|---|---|
NodeRegistry
|
初始化后的默认 |
源代码位于: plaita/node/__init__.py
get_default_registry
¶
get_default_registry() -> NodeRegistry
Return the process-wide default NodeRegistry.
Plugin discovery via plaita.nodes entry points happens lazily on the
first call, not at import time, so importing plaita.node does not pull
in optional server/plugin dependencies.
若未先调 init_default_registry, 首次调用走"隐式 discover"并 debug
提示——建议在启动脚本显式调 init_default_registry() 一次, 把进程级
单例状态变成显式、可复现的启动步骤。
源代码位于: plaita/node/__init__.py
operator_configured_sandbox_allowed_backends
¶
运营者显式配置的沙箱后端白名单(不含生效默认后端)。
PLAITA_SANDBOX_ALLOWED_BACKENDS 未设置时返回
:data:DEFAULT_SANDBOX_ALLOWED_BACKENDS。与
:func:resolve_sandbox_allowed_backends 的区别:后者会把生效默认后端并入
结果("默认后端必须自身可用"),因此无法回答"运营者是否显式放行了某后端"。
启动期降级闸门必须用本函数判断(A56-01:Docker 不可用时不得因探测失败扩集)。
源代码位于: plaita/node/__init__.py
resolve_sandbox_allowed_backends
¶
resolve_sandbox_allowed_backends(default_backend: Optional[str] = None, component: str = 'runtime') -> tuple
解析部署入口生效的沙箱后端白名单(plaita#22)。
次序:显式 PLAITA_SANDBOX_ALLOWED_BACKENDS(逗号/冒号/空白分隔的后端名)
→ 未设则 DEFAULT_SANDBOX_ALLOWED_BACKENDS;再并入生效的默认后端
(default_backend 或模块默认)——运营者选定的默认后端必须自身可用,否则
不带 sandbox_backend 的流程会被自己的白名单拦下。
结果交给 register_code_node(allowed_backends=...):硬拦载体是模块级
code._ALLOWED_SANDBOX_BACKENDS,CodeNode 解析期据此拒绝流程 JSON 里的
逐节点降级。unsafe 进白名单打 CRITICAL(宿主任意代码执行);白名单未显式
配置时打 WARNING 说明当前档位。
源代码位于: plaita/node/__init__.py
resolve_sandbox_allowed_languages
¶
解析部署入口生效的 语言 白名单(plaita#29)。
js 的历史实现(PyExecJS)完全绕开 sandbox_backend 档位——无隔离 / 无
超时 / 无取消,且后端白名单只作用于 Python:流程作者写一行 language: "js"
即拿到宿主任意代码执行 + 读 worker 全量 env。故语言默认fail-closed:只放行
python,放行 js 必须是运营者的显式决定。
次序:显式 :data:SANDBOX_ALLOWED_LANGUAGES_ENV(逗号/冒号/空白分隔)→ 未设则
code.DEFAULT_SANDBOX_ALLOWED_LANGUAGES;python(CodeNode 的默认语言)
始终保留——与后端解析同理,默认值必须自身可用,否则不带 language 的流程
会被自己的白名单拦下。
结果交给 register_code_node(allowed_languages=...):硬拦载体是模块级
code._ALLOWED_LANGUAGES,CodeNode 解析期据此拒绝 js 节点。
源代码位于: plaita/node/__init__.py
register_code_node
¶
register_code_node(registry: Optional[NodeRegistry] = None, default_backend: Optional[str] = None, allowed_backends: Optional[list] = None, allowed_languages: Optional[list] = None) -> None
Register :class:CodeNode for use in flows.
CodeNode executes arbitrary user-supplied code (Python exec
and/or PyExecJS JS). It is therefore excluded from the default registry
and must be enabled explicitly.
参数:
| 名称 | 类型 | 描述 | 默认 |
|---|---|---|---|
registry
|
Optional[NodeRegistry]
|
The :class: |
None
|
default_backend
|
Optional[str]
|
Python 沙箱后端默认值, 写入 |
None
|
allowed_backends
|
Optional[list]
|
沙箱后端白名单(2026-09 安全评审 P0)。设置后,流程 JSON
里声明的 生产部署入口应经 :func: 安全 gate: 若生效后端 ( |
None
|
allowed_languages
|
Optional[list]
|
语言白名单(2026-10 安全评审 P1,plaita#29)。默认
生产部署入口应经 :func: |
None
|
Example::
from plaita.node import register_code_node
register_code_node() # 默认 docker, 需 daemon
register_code_node(default_backend="subprocess") # 半信任, 无需 docker
.. warning::
Only call this if you trust all flow definitions that will be executed
in this process. A CodeNode in a flow JSON allows any code the flow
author chooses to run; "unsafe" 后端连文件系统/网络访问都不限制
(python exec 与 js PyExecJS 都没有超时/取消),js 语言还须
allowed_languages 显式放行。
``"subprocess"`` **不是租户凭据隔离边界**(plaita#256 首版传播租户与
凭据路径,A42-06 收窄):子进程与 worker 同用户、文件系统不受限,
且所有租户共用同一把解密密钥,租户代码可以改写租户上下文或直接读取
default 凭据文件。因此非 default 租户在 spawn 前即被拒绝
(``TenantContextPropagationError``,用户代码不执行);default 租户
语义不变。多租户 / 租户级 code 节点必须用 ``"docker"``(容器内不挂载
宿主凭据文件)。
源代码位于: plaita/node/__init__.py
504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 | |
node_register
¶
node_register(node_cls: Type[Node]) -> None
Register a node class on the default registry.
.. deprecated::
Use get_default_registry().register(node_cls) instead.
源代码位于: plaita/node/__init__.py
parse_node
¶
parse_node(node_dict) -> Node
Parse a node dict using the default registry.
.. deprecated::
Use get_default_registry().parse_node(node_dict) instead.
源代码位于: plaita/node/__init__.py
basic
¶
Node
¶
Bases: BaseModel
validate
¶
execute
¶
执行节点并返回输出。
execution 满足 NodeExecutionContext(evaluate / state / child)。
运行时传入的是 FlowExecution facade,它实现该 Protocol。
resume
¶
恢复一个此前挂起的节点 (仅 is_suspending 节点需要实现)。
内核 DistributedStrategy._handle_resume 通过本方法多态分发 resume,
避免直接 isinstance 具体挂起节点类型 (如 EventNode), 让 core 层
不反向依赖 node 插件层。基类默认抛错, 挂起型节点覆写之。
源代码位于: plaita/node/basic.py
warn_unknown_keys
¶
Schema 卫生:原始 dict 里的未知键告警(不报错)。
pydantic 对未声明字段默认 extra="ignore"——JSON 流程里拼错的字段名
(如 "conditon")会被静默吞掉,流程带着错误配置"成功"运行。0.5.0 的
一贯策略是把沉默变可见:逐键对照 声明字段+alias+LEGACY_KEYS,未知即
logger.warning(带节点 id 与合法键提示),不改变解析结果。