Building a Cogniscript App
This tutorial walks you through creating, deploying, and running a complete Cogniscript application in Maitento. We will build a Unit Converter app that accepts numeric values and converts between different measurement units.
What You Will Learn
- How to structure a Cogniscript app with inputs and outputs
- Using built-in functions for calculations and data manipulation
- Deploying your app to Maitento
- Running the app via the Maitento Shell
- Executing the app programmatically using the SDK
Prerequisites
Before starting, ensure you have:
- Access to a Maitento tenant with app creation permissions
- The Maitento SDK installed (for programmatic execution)
- Familiarity with basic Cogniscript syntax (see Cogniscript Language Guide)
Part 1: Understanding App Structure
Every Cogniscript app follows a consistent structure:
- Inputs - Parameters passed to the app when started
- Logic - Cogniscript code that processes the inputs
- Outputs - Results produced by the app for consumers
Reading Inputs
Apps receive inputs through the appState.getInput() syscall:
// Read a string input
string conversionType = appState.getInput("conversionType");
// Read a numeric input (returned as string, convert as needed)
string valueStr = appState.getInput("value");
decimal value = StringToDecimal(valueStr);
Setting Outputs
Apps produce outputs using the appState.setOutput*() syscalls:
// Set outputs of different types
appState.setOutputDecimal("result", 42.5);
appState.setOutputString("unit", "meters");
appState.setOutputBool("success", true);
Part 2: Building the Unit Converter App
Letβs build a practical Unit Converter that supports temperature, length, and weight conversions.
Complete Cogniscript Code
// Unit Converter App
// Converts values between different measurement units
// Temperature conversion functions
decimal celsiusToFahrenheit(decimal celsius) {
return((celsius * 9 / 5) + 32);
}
decimal fahrenheitToCelsius(decimal fahrenheit) {
return((fahrenheit - 32) * 5 / 9);
}
decimal celsiusToKelvin(decimal celsius) {
return(celsius + 273.15);
}
decimal kelvinToCelsius(decimal kelvin) {
return(kelvin - 273.15);
}
// Length conversion functions
decimal metersToFeet(decimal meters) {
return(meters * 3.28084);
}
decimal feetToMeters(decimal feet) {
return(feet / 3.28084);
}
decimal kilometersToMiles(decimal km) {
return(km * 0.621371);
}
decimal milesToKilometers(decimal miles) {
return(miles / 0.621371);
}
// Weight conversion functions
decimal kilogramsToPounds(decimal kg) {
return(kg * 2.20462);
}
decimal poundsToKilograms(decimal lbs) {
return(lbs / 2.20462);
}
decimal gramsToOunces(decimal grams) {
return(grams * 0.035274);
}
decimal ouncesToGrams(decimal ounces) {
return(ounces / 0.035274);
}
// Get conversion result based on type
decimal performConversion(string conversionType, decimal value) {
// Temperature conversions
if (conversionType == "celsius_to_fahrenheit") {
return(celsiusToFahrenheit(value));
} else if (conversionType == "fahrenheit_to_celsius") {
return(fahrenheitToCelsius(value));
} else if (conversionType == "celsius_to_kelvin") {
return(celsiusToKelvin(value));
} else if (conversionType == "kelvin_to_celsius") {
return(kelvinToCelsius(value));
}
// Length conversions
else if (conversionType == "meters_to_feet") {
return(metersToFeet(value));
} else if (conversionType == "feet_to_meters") {
return(feetToMeters(value));
} else if (conversionType == "km_to_miles") {
return(kilometersToMiles(value));
} else if (conversionType == "miles_to_km") {
return(milesToKilometers(value));
}
// Weight conversions
else if (conversionType == "kg_to_lbs") {
return(kilogramsToPounds(value));
} else if (conversionType == "lbs_to_kg") {
return(poundsToKilograms(value));
} else if (conversionType == "grams_to_oz") {
return(gramsToOunces(value));
} else if (conversionType == "oz_to_grams") {
return(ouncesToGrams(value));
}
// Unknown conversion type
throw("Unknown conversion type: " + conversionType);
}
// Get the output unit label
string getOutputUnit(string conversionType) {
if (conversionType == "celsius_to_fahrenheit") {
return("Fahrenheit");
} else if (conversionType == "fahrenheit_to_celsius") {
return("Celsius");
} else if (conversionType == "celsius_to_kelvin") {
return("Kelvin");
} else if (conversionType == "kelvin_to_celsius") {
return("Celsius");
} else if (conversionType == "meters_to_feet") {
return("feet");
} else if (conversionType == "feet_to_meters") {
return("meters");
} else if (conversionType == "km_to_miles") {
return("miles");
} else if (conversionType == "miles_to_km") {
return("kilometers");
} else if (conversionType == "kg_to_lbs") {
return("pounds");
} else if (conversionType == "lbs_to_kg") {
return("kilograms");
} else if (conversionType == "grams_to_oz") {
return("ounces");
} else if (conversionType == "oz_to_grams") {
return("grams");
}
return("unknown");
}
// Main entry point
void main() {
PrintLine("=== Unit Converter App ===");
PrintLine("");
// Read inputs from the app state
string conversionType = appState.getInput("conversionType");
string valueStr = appState.getInput("value");
// Validate inputs
if (StringIsEmpty(conversionType)) {
throw("Missing required input: conversionType");
}
if (StringIsEmpty(valueStr)) {
throw("Missing required input: value");
}
// Convert value string to decimal
decimal inputValue = StringToDecimal(valueStr);
PrintLine("Input: " + valueStr);
PrintLine("Conversion: " + conversionType);
PrintLine("");
// Perform the conversion
try {
decimal result = performConversion(conversionType, inputValue);
string outputUnit = getOutputUnit(conversionType);
// Round to 4 decimal places for cleaner output
result = MathRound(result, 4);
// Set the outputs
appState.setOutputDecimal("result", result);
appState.setOutputString("unit", outputUnit);
appState.setOutputBool("success", true);
// Print result to console
PrintLine("Result: " + DecimalToString(result, "F4") + " " + outputUnit);
PrintLine("");
PrintLine("Conversion completed successfully!");
} catch {
string error = GetLastError();
PrintLine("Error: " + error);
appState.setOutputDecimal("result", 0);
appState.setOutputString("unit", "error");
appState.setOutputBool("success", false);
appState.setOutputString("errorMessage", error);
}
}
Code Walkthrough
1. Conversion Functions
The app defines dedicated functions for each conversion type. This keeps the code organized and testable:
decimal celsiusToFahrenheit(decimal celsius) {
return((celsius * 9 / 5) + 32);
}
2. Input Validation
Always validate inputs before processing:
if (StringIsEmpty(conversionType)) {
throw("Missing required input: conversionType");
}
3. Error Handling
Use try/catch to handle errors gracefully and still produce meaningful outputs:
try {
decimal result = performConversion(conversionType, inputValue);
// ... success handling
} catch {
string error = GetLastError();
appState.setOutputBool("success", false);
appState.setOutputString("errorMessage", error);
}
4. Multiple Output Types
The app demonstrates setting different output types:
appState.setOutputDecimal("result", result); // Numeric result
appState.setOutputString("unit", outputUnit); // Unit label
appState.setOutputBool("success", true); // Success flag
Part 3: Deploying the App
Step 1: Create the App Resource
First, create the app in your namespace using the Shell:
# Create a new app in the 'production' namespace
app-create unit-converter --namespace production
Step 2: Upload the Cogniscript Code
Save the Cogniscript code to a file (e.g., unit-converter.cog) in your Maitento project directory, then build and deploy:
# Build the app (compiles and creates a new version)
app-build unit-converter --namespace production --source ./unit-converter.cog
The build process:
- Compiles the Cogniscript source into bytecode
- Extracts input definitions from
appState.getInput()calls - Creates a new immutable version
- Stores the compiled binary
Step 3: Verify the Deployment
# Get app details to confirm deployment
app-get unit-converter --namespace production
This shows version information, including detected inputs and creation date.
Part 4: Running the App via Shell
Basic Execution (Fire and Forget)
Start the app without waiting for completion:
app-start unit-converter --namespace production \
--input-conversionType "celsius_to_fahrenheit" \
--input-value "100"
This returns immediately with a process ID.
Synchronous Execution (Wait for Result)
Use app-run to wait for completion and see outputs:
app-run unit-converter --namespace production \
--input-conversionType "celsius_to_fahrenheit" \
--input-value "100"
Expected Output:
=== Unit Converter App ===
Input: 100
Conversion: celsius_to_fahrenheit
Result: 212.0000 Fahrenheit
Conversion completed successfully!
Process finished successfully.
Outputs:
result: 212
unit: Fahrenheit
success: true
More Example Conversions
# Temperature: Fahrenheit to Celsius
app-run unit-converter --namespace production \
--input-conversionType "fahrenheit_to_celsius" \
--input-value "32"
# Length: Kilometers to Miles
app-run unit-converter --namespace production \
--input-conversionType "km_to_miles" \
--input-value "10"
# Weight: Kilograms to Pounds
app-run unit-converter --namespace production \
--input-conversionType "kg_to_lbs" \
--input-value "75"
Monitoring Running Processes
# List active processes in the namespace
app-ps --namespace production
# Attach to a specific process to see real-time output
app-attach <process-id>
Part 5: Running the App via SDK (C#)
Setup
First, install the Maitento SDK and create a client:
using Maitento.Sdk;
using Maitento.Entities.Apps;
using Maitento.Entities.Database;
// Create the client with your API key
IMaitentoClient client = MaitentoClient.Create("your-api-key");
Starting and Monitoring the App
// Define inputs
NameValuePair[] inputs = new[]
{
new NameValuePair("conversionType", "celsius_to_fahrenheit"),
new NameValuePair("value", "100")
};
// Start the app process
AppProcess process = await client.Apps.StartAsync(
"production", // namespace
"unit-converter", // app name
inputs
);
Console.WriteLine($"Started process: {process.Id}");
Console.WriteLine($"Status: {process.Status}");
// Poll for completion
while (process.Status == AppProcessStatus.Running ||
process.Status == AppProcessStatus.New)
{
await Task.Delay(500);
process = await client.Apps.GetProcessAsync(process.Id) ?? process;
Console.WriteLine($"Status: {process.Status}");
}
// Check final status
if (process.Status == AppProcessStatus.Finished)
{
Console.WriteLine("\nProcess completed successfully!");
Console.WriteLine($"Return Value: {process.ReturnValue}");
// Access outputs
foreach (AppOutput output in process.Outputs)
{
Console.WriteLine($" {output.Name}: {output.Value} ({output.Type})");
}
}
else if (process.Status == AppProcessStatus.Error)
{
Console.WriteLine($"\nProcess failed: {process.FailureDetails}");
}
// Display console output
Console.WriteLine("\nConsole Output:");
foreach (AppConsoleOutput consoleOutput in process.ConsoleOutputs)
{
Console.WriteLine(consoleOutput.Text);
}
Complete SDK Example with Error Handling
using Maitento.Sdk;
using Maitento.Sdk.Exceptions;
using Maitento.Entities.Apps;
using Maitento.Entities.Database;
public class UnitConverterExample
{
private readonly IMaitentoClient _client;
public UnitConverterExample(string apiKey)
{
_client = MaitentoClient.Create(apiKey);
}
public async Task<ConversionResult> ConvertAsync(
string conversionType,
decimal value)
{
try
{
// Prepare inputs
NameValuePair[] inputs = new[]
{
new NameValuePair("conversionType", conversionType),
new NameValuePair("value", value.ToString())
};
// Start the conversion
AppProcess process = await _client.Apps.StartAsync(
"production",
"unit-converter",
inputs
);
// Wait for completion (with timeout)
int maxAttempts = 60; // 30 seconds max
int attempts = 0;
while ((process.Status == AppProcessStatus.Running ||
process.Status == AppProcessStatus.New) &&
attempts < maxAttempts)
{
await Task.Delay(500);
process = await _client.Apps.GetProcessAsync(process.Id)
?? process;
attempts++;
}
// Check result
if (process.Status == AppProcessStatus.Finished)
{
var resultOutput = process.Outputs
.FirstOrDefault(o => o.Name == "result");
var unitOutput = process.Outputs
.FirstOrDefault(o => o.Name == "unit");
return new ConversionResult
{
Success = true,
Result = decimal.Parse(resultOutput?.Value ?? "0"),
Unit = unitOutput?.Value ?? "unknown",
ProcessId = process.Id
};
}
else
{
return new ConversionResult
{
Success = false,
ErrorMessage = process.FailureDetails ?? "Unknown error",
ProcessId = process.Id
};
}
}
catch (MaitentoValidationException ex)
{
return new ConversionResult
{
Success = false,
ErrorMessage = string.Join("; ",
ex.ValidationErrors.SelectMany(e => e.Value))
};
}
catch (MaitentoApiException ex)
{
return new ConversionResult
{
Success = false,
ErrorMessage = $"API error ({ex.StatusCode}): {ex.ResponseBody}"
};
}
}
}
public class ConversionResult
{
public bool Success { get; set; }
public decimal Result { get; set; }
public string? Unit { get; set; }
public string? ErrorMessage { get; set; }
public Guid? ProcessId { get; set; }
}
// Usage
var converter = new UnitConverterExample("your-api-key");
var result = await converter.ConvertAsync("celsius_to_fahrenheit", 100);
if (result.Success)
{
Console.WriteLine($"100 Celsius = {result.Result} {result.Unit}");
}
else
{
Console.WriteLine($"Conversion failed: {result.ErrorMessage}");
}
Part 6: Advanced Topics
Chaining Apps with app.start()
Cogniscript apps can start other apps for complex workflows:
void main() {
// Get input to convert
string valueStr = appState.getInput("value");
// First conversion: Celsius to Fahrenheit
jsonObject inputs1 = JsonObjectCreate();
inputs1 = JsonObjectSetString(inputs1, "conversionType", "celsius_to_fahrenheit");
inputs1 = JsonObjectSetString(inputs1, "value", valueStr);
string processId1 = AppStart("unit-converter@production", inputs1);
// Wait for the first conversion
string process1 = AppProcessGet(processId1);
// ... extract result and continue with next conversion
}
Storing Results in VFS
Write conversion logs to the Virtual File System:
void main() {
// ... conversion logic ...
// Log the conversion
string timestamp = DateTimeToString(DateGetUtc(), "yyyy-MM-dd HH:mm:ss");
string logEntry = timestamp + " | " + conversionType + " | " +
valueStr + " -> " + DecimalToString(result, "F4") + " " + outputUnit;
VfsAppendText("logs", "production", "/conversions.log", logEntry + "\n");
}
Scheduling Regular Conversions
Use Automated Actions to run conversions on a schedule:
# Create an automated action that runs daily
action-create daily-conversion \
--namespace production \
--actions '[{
"$type": "app",
"version": {"id": "<app-id>", "version": 1},
"inputs": [
{"name": "conversionType", "value": "km_to_miles"},
{"name": "value", "value": "100"}
]
}]'
# Add a daily schedule at midnight
action-schedule-add --action-name daily-conversion \
--namespace production \
--minute 0 --hour 0 \
--schedule-name "midnight-run"
Supported Conversion Types Reference
| Conversion Type | From | To |
|---|---|---|
celsius_to_fahrenheit | Celsius | Fahrenheit |
fahrenheit_to_celsius | Fahrenheit | Celsius |
celsius_to_kelvin | Celsius | Kelvin |
kelvin_to_celsius | Kelvin | Celsius |
meters_to_feet | Meters | Feet |
feet_to_meters | Feet | Meters |
km_to_miles | Kilometers | Miles |
miles_to_km | Miles | Kilometers |
kg_to_lbs | Kilograms | Pounds |
lbs_to_kg | Pounds | Kilograms |
grams_to_oz | Grams | Ounces |
oz_to_grams | Ounces | Grams |
Summary
In this tutorial, you learned how to:
- Structure a Cogniscript app with inputs, processing logic, and outputs
- Use built-in functions like
StringToDecimal(),MathRound(), andDecimalToString() - Handle errors gracefully with try/catch blocks
- Deploy the app using Shell commands
- Run the app via Shell with
app-startandapp-run - Execute programmatically using the .NET SDK with proper error handling
Next Steps
- Explore the Cogniscript Language Guide for more language features
- Learn about Apps and Capsules for containerized execution
- See the SDK Reference for complete API documentation
- Check out Automated Actions for scheduling