Node
node
Adapt an :class:~axm.tools.base.AXMTool into a DAG python-node function.
This is the third consumer of the single axm.tools declaration: the same
tool that MCP exposes and the CLI auto-generates can be called directly from a
DAG node, with no HookAction and no subprocess. One entry point → MCP + CLI
+ node (see synchronisation_cmp_cli/README.md, révision axm-dag).
A DAG python node is a callable fn(payload) -> dict whose returned keys are
the node's writes. :func:tool_node builds such a callable around a tool:
- inputs — the node's
readsarrive inpayload; they map to the tool'sexecute(**kwargs)by name, or via an explicitargsrename map when the mem key differs from the parameter name; - outputs —
returnsmaps each write key to its source: the literal"text"(the tool'sToolResult.text) or a key insideToolResult.data; - failure — fail-fast: a tool returning
success=Falseraises :class:ToolNodeError. Guard preconditions with a conditional node (router /if_) so the tool is only invoked when it can succeed; - substitution — :func:
override_toolsswaps named tools for the dynamic extent of a block (a :mod:contextvarsscope, so it followsasynciotasks andasyncio.to_thread— the paths a DAG run executes nodes on). It is the tool-side twin of an injected agent backend: a graph's logic can run against in-memory fakes ofaudit_test/git_*/… without forking the real toolchain, while the node's own payload/output shaping stays exercised.
ToolNodeError
load_tool(name)
Resolve and instantiate the axm.tools entry point named name.
Source code in packages/axm/src/axm/tools/node.py
override_tools(tools)
Substitute tools ({entry_point_name: tool}) for the duration of a block.
Inside the block, a :func:tool_node built for one of the named tools calls
the substitute instead of resolving the axm.tools entry point — whether
the node was built before or after entering the block, and whether or not
the real tool had already been resolved and memoized. Blocks nest: an inner
block adds to (or shadows, per name) the enclosing one, and leaving it
restores exactly what was active before.
The substitution applies to every resolution by name — :func:tool_node
nodes and direct :func:_load_tool callers alike — so a graph node that
resolves a tool itself (_load_tool("echo_check") in a python node) is
covered too, not only tool_node wrappers.
The scope is a :mod:contextvars context, not a global: it propagates to
asyncio tasks and :func:asyncio.to_thread calls started inside the
block (how axm_dag executes python nodes) and is invisible to concurrent
work started outside it. Substitutes are never memoized, so the real tool
resolves again as soon as the block exits.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
tools
|
Mapping[str, AXMTool]
|
Entry-point name → substitute implementing |
required |
Source code in packages/axm/src/axm/tools/node.py
tool_node(name, *, args=None, returns=None, allow_failure_data=False)
Build a DAG python-node fn(payload) -> dict around an axm.tools tool.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
name
|
str
|
The tool's |
required |
args
|
Mapping[str, str] | None
|
Optional |
None
|
returns
|
Mapping[str, str] | None
|
Precedence / collision: the literal |
None
|
allow_failure_data
|
bool
|
Opt-in for observation tools whose |
False
|
Returns:
| Type | Description |
|---|---|
Callable[[Mapping[str, object]], dict[str, object]]
|
A callable mapping the node's |
Raises:
| Type | Description |
|---|---|
ToolNodeError
|
At call time, if the tool is unknown, returns
|
Source code in packages/axm/src/axm/tools/node.py
| Python | |
|---|---|
118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 | |