Apps
An app is code that runs on Maitento. Use one when you need logic around your agents: start an interaction, look at its answer, branch, loop, start several more in parallel, write files, run a capsule. Apps are written in Cogniscript, a small C-like language.
See it in tutorial 16, Apps, which writes the app below, and tutorial 17, which runs it every morning with an automation. Tutorial 23’s nightly digest is an app that calls a tool, saves the day’s tickets to a file system and runs a capsule that reports on them.
// The taglines app: it asks for three campaign angles, then writes a tagline for each angle, all at once.// An app starts at main. main's parameters are the app's inputs: this one has one, product.string main(string product) { // Stop early, with a clear message, when the input is missing. if (product == "") { throw("Tell me which product to write taglines for."); } PrintLine("Brainstorming angles for " + product);
// Start the brainstorm interaction and wait for it. The last three arguments are for memory, // model overrides and a response schema; this app uses none of them. jsonObject brainstormInputs = JsonObjectCreate(); brainstormInputs = JsonObjectSetString(brainstormInputs, "product", product); guid brainstormId = InteractionStart("brainstorm", brainstormInputs, "[]", "[]", ""); jsonObject brainstorm = InteractionAwait(brainstormId); if (JsonObjectGet(brainstorm, "status") != "Finished") { throw("The brainstorm failed: " + JsonObjectGet(brainstorm, "failureDetails")); }
// The brainstorm's answer is JSON that matches json/ideas.json. jsonObject result = StringToJsonObject(JsonObjectGet(brainstorm, "output")); jsonArray ideas = JsonObjectGet(result, "ideas");
// Start one tagline run per idea. They all run at the same time. jsonArray runIds = JsonArrayCreate(); int ideaCount = JsonArrayCount(ideas); for (int i = 0; i < ideaCount; i++) { jsonObject inputs = JsonObjectCreate(); inputs = JsonObjectSetString(inputs, "product", product); inputs = JsonObjectSetString(inputs, "idea", JsonArrayGetAtIndex(ideas, i)); guid runId = InteractionStart("tagline", inputs, "[]", "[]", ""); runIds = JsonArrayAdd(runIds, runId); }
// Wait for every tagline run, then collect the answers. jsonArray runs = InteractionAwaitAll(runIds); jsonArray taglines = JsonArrayCreate(); int runCount = JsonArrayCount(runs); for (int j = 0; j < runCount; j++) { jsonObject run = JsonArrayGetAtIndex(runs, j); if (JsonObjectGet(run, "status") == "Finished") { string tagline = JsonObjectGet(run, "output"); PrintLine("- " + tagline); taglines = JsonArrayAdd(taglines, tagline); } }
// A named output is what your code reads back from the finished run. return gives the run's return value. AppStateSetOutputJsonArray("taglines", taglines); return(JsonArrayCount(taglines) + " taglines");}How an app is laid out
Section titled “How an app is laid out”- Each app is a folder under
apps/, named after the app:apps/taglines/. - It holds one or more
.mtcfiles with the code, and optionally one.mtrfile with settings such asduration.max. - An app starts at
main.main’s parameters are the app’s inputs, somain(string product)makes an app with one input calledproduct. - A system call takes every argument, even the ones you don’t need:
InteractionStart’s last three are there even when they’re empty. The deploy fails (“Argument count mismatch”) when one is missing.
What an app produces
Section titled “What an app produces”| Field on the finished run | Comes from |
|---|---|
returnValue | return(...) in main |
outputs | AppStateSetOutputString(name, value), AppStateSetOutputJsonArray(...) and friends |
consoleOutputs | PrintLine(...) and TraceLine(...) |
failureDetails | an uncaught throw("...") (its message), or an error the app didn’t catch (InteractionStart failed: …, naming the system call) |
Running one
Section titled “Running one”const run = await mt.apps.run("taglines", { inputs: { product: "a reusable bamboo coffee cup" } });console.log(run.outputs);run = mt.apps.run("taglines", inputs={"product": "a reusable bamboo coffee cup"})print(run.outputs)var run = await mt.Apps.RunAsync("taglines", new Dictionary<string, string> { ["product"] = "a reusable bamboo coffee cup" });foreach (var output in run.Outputs) Console.WriteLine($"{output.Name}: {output.Value}");mt app-run taglines --input-product "a reusable bamboo coffee cup"run() waits for the run. If it ends Error or Killed, run() throws (ProcessFailedError in TypeScript and
Python, MaitentoProcessFailedException in C#) with the run’s failureDetails. To get the failed run back instead,
pass throwOnFailure: false (throw_on_failure=False in Python, ThrowOnFailure = false in C#‘s RunOptions). A
start without one of the app’s inputs is refused with api:apps:input-missing. See it in
tutorial 16’s break-it step.
Cogniscript in five minutes
Section titled “Cogniscript in five minutes”string main(string name) { string greeting = "Hello " + name; // variables must be given a value int count = 3; for (int i = 0; i < count; i++) { PrintLine(greeting); } if (count > 2) { return("many"); // return, exit and throw need brackets } return("few");}- Types:
string,int,decimal,bool,guid,date,jsonObjectandjsonArray. Every value is held as text underneath, so treat types as documentation. - Arithmetic is decimal:
7 / 2is3.5.+adds two numbers, and joins anything else. - Comparisons are exact:
==compares text exactly, including case. - JSON helpers return a new value: write
o = JsonObjectSetString(o, "key", "value");. - Errors:
try { } catch { }catches them, andGetLastError()says what went wrong. - Backslashes need doubling in strings:
"\\d".
The most useful calls
Section titled “The most useful calls”| Area | Calls |
|---|---|
| Interactions | InteractionStart, InteractionAwait, InteractionAwaitAll, InteractionAwaitAny |
| Apps | AppStart, AppProcessAwait, AppStateSetOutput… |
| Files | VfsReadText, VfsWriteText, VfsFind, FileSearch |
| Sandboxes | CapsuleStart, CapsuleExec, CapsuleCommandAwait, CapsuleTerminate: see tutorial 21 |
| Tools | ExtOpInvoke |
| JSON | JsonObjectCreate, JsonObjectGet, JsonObjectSetString, JsonArrayCreate, JsonArrayAdd, JsonArrayCount, JsonArrayGetAtIndex |
| Text | StringToLower, StringContains, StringToJsonObject, RegexIsMatch |
Good to know
Section titled “Good to know”- Child runs use your model keys. Interactions an app starts run on your tenant’s model keys, like any other run.
- Every input is required when you start the app, and arrives as text.
- Tools that need approval don’t run from apps. Approvals happen in interactions.