Write and publish a function#
Below, <org>/<workspace> stands for your organisation and workspace, as
astra use set them.
The handler#
A function is one file. The function in it named handler (or the name in
spec.handler) is called with the call's input, a JSON value, and what it
returns is the answer, as JSON.
import requests
def handler(city: str, days: int = 3) -> dict:
"""Get the daily maximum temperature in a city.
Args:
city: The city, as people write it.
days: How many days ahead, 1 to 7.
"""
print(f"looking up {city}")
geo = requests.get("https://geocoding-api.open-meteo.com/v1/search",
params={"name": city, "count": 1}, timeout=10).json()
place = geo["results"][0]
f = requests.get("https://api.open-meteo.com/v1/forecast",
params={"latitude": place["latitude"], "longitude": place["longitude"],
"daily": "temperature_2m_max", "forecast_days": days,
"timezone": "auto"}, timeout=10).json()
return {"city": place["name"], "dates": f["daily"]["time"],
"max_c": f["daily"]["temperature_2m_max"]}
- Named parameters: the input's fields are passed as keyword
arguments (
{"city": "Lisbon"}→handler(city="Lisbon")). A field the handler does not take is an error, unless it takes**kwargs. - The input whole: a handler whose only parameter is
eventgets the input as it is (an object, a list, a string…). - A parameter named
contextgets the call's context:context.invocation_id,context.function,context.version, andcontext.remaining_ms(). async defhandlers work. What is printed goes to the log, each line prefixed with the call's id.
/**
* Get the daily maximum temperature in a city.
* @param {object} event
* @param {string} event.city - The city, as people write it.
* @param {integer} [event.days=3] - How many days ahead, 1 to 7.
*/
export async function handler(event, context) {
console.log(`looking up ${event.city}`);
const geo = await (await fetch(`https://geocoding-api.open-meteo.com/v1/search?count=1&name=${encodeURIComponent(event.city)}`)).json();
const place = geo.results[0];
const q = new URLSearchParams({ latitude: place.latitude, longitude: place.longitude, daily: "temperature_2m_max", forecast_days: event.days ?? 3, timezone: "auto" });
const f = await (await fetch(`https://api.open-meteo.com/v1/forecast?${q}`)).json();
return { city: place.name, dates: f.daily.time, max_c: f.daily.temperature_2m_max };
}
- The handler gets the input whole, and
context(invocationId,function,version,remainingMs()). - An ES module (
export async function handler) or CommonJS (exports.handler = async (event) => …).fetchis built in. - What
console.logwrites goes to the log, each line prefixed with the call's id.
Errors. An exception is the call's answer: 500 with
{"error": {"type", "message", "trace"}}. A call that runs past the
function's timeout is answered 504 with {"error": {"type": "Timeout"}};
in Python, an instance whose call is still running after twice the timeout
is restarted.
Packages#
List them in spec.dependencies, one per entry: pip requirements for Python
(requests==2.32.3), npm packages for Node ([email protected]). They are
installed each time an instance starts, from the public registries (PyPI,
npm). Options (--index-url, -e) are refused. If they do not install, the
instance still starts, and every call answers the import error — so you see
it at once when you test.
Create it#
deploy creates the function, or changes its draft when it exists; the
runtime follows the file's extension (.py, .js/.mjs/.cjs).
Astraeus → Functions → New function: a name and a runtime. Write the code in the editor, the packages below it, and Save draft.
curl -sS https://api.astralyx.cloud/v1/functions \
-H "Authorization: Bearer $ASTRALYX_TOKEN" -H 'content-type: application/json' \
-d "$(jq -n --rawfile code weather.py '{metadata: {name: "weather"}, spec: {runtime: "python", code: $code, dependencies: ["requests==2.32.3"], timeout_seconds: 20, http: {enabled: true}}}')"
Try the draft: Test in the console, or
The first call starts an instance (and installs the packages): it takes longer than the next ones.
Settings#
| Setting | Default | Range |
|---|---|---|
timeout_seconds |
30 | 1–900 |
memory_mb |
512 | 128–65536 |
cpu_cores |
1 | 1–64 (its share; it may use idle cores beyond) |
gpu.count |
none | 1, 2, 4 or 8, with isolation: container |
isolation |
sandbox (gVisor) |
sandbox, container (weaker) |
scaling.concurrency |
8 | 1–1000 calls at once per instance |
scaling.min_instances |
0 | kept warm, for versions aliases name |
scaling.max_instances |
4 | 1–64 |
scaling.queue |
64 | 0–10000 calls waiting |
scaling.idle_seconds |
300 | 30–86400 |
http.enabled |
false | answer at its URL |
Credentials are given as variables ({"credential": "github", "env": {"token": "GITHUB_TOKEN"}})
or, with no env, as files at /credentials/<name>/<key>. Variables in
env are for what is not secret; names starting ASTRAEUS_ are the
platform's. Every field is in the function specification.
Publish and roll out#
Publishing makes the draft the next version, immutable; latest names it at
once.
astra astraeus functions publish weather --note "first" --alias prod
astra astraeus functions invoke weather@prod -d '{"city": "Lisbon"}'
Change the code, deploy again, publish v2: latest is v2, prod stays
on v1. Roll out by moving prod, and back the same way:
astra astraeus functions alias weather prod v2 # roll out
astra astraeus functions alias weather prod v1 # roll back
Each version an alias names has its own instances: moving an alias starts the new version's (if none runs) and stops those of a version no alias names any more. In the console: Versions → Move.
What a model is told it takes#
Publishing generates the schema of the input from the handler — what a model is told when a deployment uses the function as a tool:
- Python: each parameter is a property, required unless it has a
default (or is
Optional[…]/X | None);str,int,float,bool,list[…],dict,Literal[…](an enum) are understood, anything else is any value. The docstring's first paragraph is the description; itsArgs:(Google),Parameters(NumPy) or:param x:(Sphinx) entries describe the parameters. A handler takingeventwhole is described by its docstring'sevent.<field>entries. - Node: the JSDoc block before the handler: `@param {string} event.city
- …
(or a destructured({city})with@param {string} city);[event.days=3]is optional with a default;string,number,integer,boolean,object,T[],'a'|'b'` are understood.
What cannot be read is left open with a warning (astra astraeus functions
schema weather, or the editor's side panel). For the example above:
{"description": "Get the daily maximum temperature in a city.",
"parameters": {"type": "object", "additionalProperties": false, "required": ["city"],
"properties": {"city": {"type": "string", "description": "The city, as people write it."},
"days": {"type": "integer", "default": 3, "description": "How many days ahead, 1 to 7."}}}}