Files
lda-wf/docs/runbooks/python-source.md
T

187 lines
4.5 KiB
Markdown

# Python Source Runbook
This runbook shows how to expose project-local Python `NodeSpec` functions as
workflow capabilities through `wf-rpc-server`.
Python sources are trusted in-process code. Importing the configured module can
run top-level module code during `wf config validate` and server startup. Keep
module top-level work small and side-effect free; put real work inside `@node`
functions.
Prefer package-style source modules and package-relative imports. For example,
use `module: "my_source.ops"` with `from .helpers import ...` inside
`my_source/ops.py`. Bare local imports such as `import helpers` use Python's
global import cache and are only best-effort when multiple configured sources
reuse the same helper/module names.
## 1. Write `ops.py`
Create a module that exports one or more `NodeSpec` objects. The simplest path
is to decorate typed functions with `wf_authoring.node` and put them in a
`registry` list.
```python
from __future__ import annotations
from pydantic import BaseModel
from wf_authoring import node
class EchoInput(BaseModel):
text: str
class EchoOutput(BaseModel):
echoed: str
@node(name="echo")
def echo(payload: EchoInput) -> EchoOutput:
return EchoOutput(echoed=payload.text)
registry = [echo]
```
The registry can also be a mapping or a callable returning a sequence/mapping.
Every exported value must be a `NodeSpec`.
## 2. Configure The Source
Add a `kind: "python"` entry under `server.sources[]`:
```json
{
"version": 1,
"client": {
"target": {
"kind": "rpc_http",
"url": "http://127.0.0.1:8766/rpc",
"timeout_seconds": 30
}
},
"server": {
"store": {"kind": "filesystem", "root": ".wf_python_store"},
"transports": [
{"kind": "rpc_http", "host": "127.0.0.1", "port": 8766, "path": "/rpc"}
],
"sources": [
{
"kind": "python",
"id": "local.ops",
"path": ".",
"module": "ops",
"registry": "registry"
}
]
}
}
```
`path` is resolved relative to the config file and added to `sys.path` before
import. This makes the module discoverable whether you run through
`uv run python` or an installed entrypoint.
The source id prefixes local names. A node named `echo` becomes
`local.ops.echo`. If a node uses the authoring namespace, such as
`authoring.echo`, that authoring prefix is replaced by the source id, producing
`local.ops.echo`.
## 3. Validate And Start
Preflight the config:
```powershell
uv run wf config validate wf.python.config.json
```
Then start the server:
```powershell
uv run wf-rpc-server --config wf.python.config.json
```
If the config includes `client.target`, the CLI can use the config directly:
```powershell
uv run wf --config wf.python.config.json status
```
You can also pass the URL explicitly:
```powershell
uv run wf --url http://127.0.0.1:8766/rpc source list
```
## 4. Call A Capability
List and call the Python capability:
```powershell
uv run wf --url http://127.0.0.1:8766/rpc cap list --source local.ops
uv run wf --url http://127.0.0.1:8766/rpc cap call local.ops.echo --input '{"text":"hello"}'
```
Expected output includes:
```json
{
"outcome": "ok",
"output": {"echoed": "hello"}
}
```
## 5. Save And Run A Workflow
Create a draft from the Python capability:
```powershell
uv run wf --url http://127.0.0.1:8766/rpc draft create `
python_echo_ws --capability local.ops.echo --name python_echo
```
Save it as an artifact. Bind the configured Python source. Built-in platform
sources such as `wf.std` can be used by generated scaffolds without self-binding.
```powershell
uv run wf --url http://127.0.0.1:8766/rpc draft save python_echo_ws `
--artifact python_echo `
--version 1 `
--title "Python Echo" `
--outcome ok `
--binding local.ops=local.ops
```
Save a deployment with the same bindings:
```powershell
uv run wf --url http://127.0.0.1:8766/rpc deploy save python_echo.default `
--artifact python_echo `
--version 1 `
--binding local.ops=local.ops
```
Run it:
```powershell
uv run wf --url http://127.0.0.1:8766/rpc run start python_echo.default `
--input '{"text":"hello workflow"}'
```
Expected output includes:
```json
{
"outcome": "ok",
"output": {"echoed": "hello workflow"}
}
```
## Current Limits
- Python sources are static at server startup; there is no hot reload yet.
- Python sources are trusted in-process code; there is no sandbox.
- Source registry mutation/apply support for Python sources is deferred.
- Reducer exports are deferred until a real source needs them.