Join an Event Script Flow¶
Join the engines: your function as a first-class flow task.
At a glance
- What — one YAML entry on the engine makes a route remote; the flow itself does not change at all.
- Worked demo — the engine's
composable-exampleships this exact wiring and has executed the Python demo function unchanged.
The one moving part: the declarative map¶
On the engine application, enable the map and point the route at your host —
application.properties:
event-over-http.yaml:
event.http:
- route: 'hello.declarative'
target: 'http://${peer.demo.host:127.0.0.1}:${peer.demo.port}/api/event'
# optional security headers, e.g. an authorization token the host or a
# gateway validates:
# headers:
# authorization: '${DEMO_PEER_TOKEN:demo}'
That is the entire integration surface. Every Event Script task (and MiniGraph
graph.task) that names hello.declarative now calls your Python host. The full map
grammar lives in the engine's
Event over HTTP guide.
The flow does not know, and must not care¶
This is the engine's shipped demo flow — note that nothing in it says "remote" or "python":
flow:
id: 'event-over-http-declarative'
description: 'Demonstrate Event-over-Http protocol using declarative means'
ttl: 10s
exception: 'v1.hello.exception'
first.task: 'event-over-http-declarative'
tasks:
- name: 'event-over-http-declarative'
input:
- 'input.header -> header'
- 'input.body -> *'
process: 'hello.declarative'
output:
- 'text(application/json) -> output.header.content-type'
- 'result -> output.body'
execution: end
Register the route on the Python side and the flow executes it:
@preload(route="hello.declarative", instances=10)
async def declarative_echo(headers: dict[str, str], body: Body):
return {"body": body, "headers": headers, "language": "python"}
What your function sees¶
sequenceDiagram
participant C as REST client
participant E as Engine (flow)
participant H as Python host
participant F as hello.declarative
C->>E: GET /api/event/http/declarative
E->>H: POST /api/event (envelope bytes, x-ttl, trace headers)
H->>F: (headers, body) on the bus
F-->>H: reply body
H-->>E: reply envelope (status, exec_time, annotations)
E-->>C: flow output mapping
- Headers — the task's
input.header -> headermapping arrives as yourheadersdict. Reserved keys are cleaned at ingress, and the flow's business correlation id arrives as the read-onlymy_correlation_idheader. - Body — whatever the task's input mapping sends (
input.body -> *passes the whole body through). - Trace — the engine's trace id and path ride the wire;
get_trace()sees them, andannotate_trace()entries return on the reply envelope into the engine's telemetry.
Errors flow into the flow¶
AppException(400, "missing 'text'")in Python → a 400 envelope → the flow'sexception:task fires witherror.code=400anderror.messageexactly as for an engine function.- An unexpected Python exception → 500 with message and stack.
- The flow's
ttlbounds the call: on breach the engine receives the standard 408, and the exception path decides what happens next — retries and compensation stay in the flow, never in Python (Rationale).
Checklist¶
- Host running and healthy (
/livenessprobe→OK). - Route registered (
/info/routeslists it aspublic). - Engine
application.propertiesnames the map; the map names the route and target. - The flow task's
process:names the route. Nothing else changes.