Browse the documentation

AEL 1.0.0

Serving an agent

In AEL, the Agent Engineering Language, ael-host serve runs one workflow as a server. From one command it listens for calls on the built-in gRPC-compatible transport and fires the triggers the workflow declares: its polling, schedule and calendar triggers. A request and a trigger fire go through the same admission and run to one result.

Usage

ael-host serve --root <project> --entry <name>.agent.ael \
  --tenant <tenant> --principal <principal> \
  --native-root <dir> --worker <worker> --worker-sha256 <digest> \
  --listen 127.0.0.1:7070
OptionMeaning
--root <project>, --entry <name>.agent.aelThe project and the workflow to serve.
--tenant <tenant>, --principal <principal>The identity every request and every fire runs under.
--native-root <dir>, --worker <worker>, --worker-sha256 <digest>Where the Host keeps native work, the worker that does it and that worker's digest.
--listen <ip:port>The address to listen on. Without it, a free port on the loopback address.
--auth-token-file <file>A file with a bearer token of at least 16 characters.
--allow-remoteListen on an address other than loopback. This needs --auth-token-file.
--model-services <file>The model services the workflow's nodes use.
--state-directory <dir>Where trigger claims are kept, so that they survive a restart.
--deadline-ms <n>The deadline of each request, from 1 to 3,000,000 milliseconds. The default is 60,000. A caller's own timeout can shorten it, never extend it.
--concurrency <n>How many requests run at once, from 1 to 16. The default is 4.
--max-requests <n>, --idle-timeout-ms <n>, --stop-file <file>When to stop. See Stopping.
--ready-file <file>A file the Host writes the address it listens on to, once it listens.
--source-map <file>A source map of the served build. See Failures and your source.
--isolated-image <component>=<executable>:<sha256>The image that runs an isolated component. Repeat the option for each one.

The Host prints the address it listens on to standard error.

Requests

A caller makes one unary call to /ael.host.Workflow/Run. The message is a JSON object with exactly one input port, which names an array of values, such as {"input":[3]}. The reply is the workflow's output document, or a status:

StatusWhen
RESOURCE_EXHAUSTEDThe request was refused at admission.
FAILED_PRECONDITIONThe run was refused or failed.
CANCELLEDThe run was cancelled.
UNAUTHENTICATEDThe bearer token is missing or wrong.

The identity of a run is the tenant and principal on the command line, never a value in the request. A caller that closes its connection cancels the run that was admitted for it.

Remote callers

The Host listens on the loopback address unless you say otherwise. For any other address it requires --allow-remote and a bearer token file, and each call must carry the token in its authorization: Bearer header. The transport between caller and Host is plain HTTP/2 without TLS, so a Host that listens beyond the loopback address belongs behind your own TLS terminator.

Concurrency

Every request, and every trigger fire, prepares the workflow afresh and is admitted against one shared pool. The pool, not the request, bounds the Host. Up to --concurrency requests run at once; one serving thread alone admits, fires triggers and gives each request its single reply.

Triggers

The triggers that the workflow declares fire under the same identity and admission as requests. With --state-directory, their claims are durable, so a slot never fires twice across restarts. Without it, they last as long as the process. The Host does not take triggers fed by outside event sources.

Stopping

The Host stops at --max-requests requests, after --idle-timeout-ms without activity, or when the --stop-file appears. Running requests are then answered within their deadlines, the listener drains, and the Host prints a report that counts requests and fires by how they ended.

Failures and your source

With --source-map, every failed or refused request or fire is tied to the declaration in your source that it came from: the instance, its definition and the edges that feed it, for a node; the edge; a hook; a factory callback; or the workflow's own body. Each is printed on standard error, and the report lists the first sixteen. ael agent compile writes the source map. An observation is marked stale when the source it names has changed since the build, and it is not given a line and column then.

Serving an installed application

ael-host serve --installed-application <executable> serves an installed application, one request at a time, in place of a workflow. It takes --application-sha256, --expect-sha256 and --template-sha256 to pin the application. The values of a request are the application's standard input, one JSON value a line, and the reply is {"result":[{"exit_code":0,"stdout":"..."}]}.

When the application calls functions in other languages, the Host holds their grant, given with --foreign-cgroup <dir> and the limits --foreign-memory-bytes, --foreign-processes, --foreign-cpu-nanos, --foreign-quota-us, --foreign-period-us and --foreign-max-duration-ms. The Host checks the grant before it listens. A directory that is not a delegated control group, or limits the kernel cannot hold, end the command with the reason. Every call to such a function is recorded under its request, and the reply carries the report of those calls. Requests take turns, because one control group holds the processes of one request at a time. A served workflow cannot call functions in other languages, and --root refuses the grant options.