Skip to main content
Version: 2.6

AktorFunction

An AktorFunction is a lightweight wrapper that converts any plain TypeScript function into an Aktor. It's the most straightforward way to create an Aktor when you have a pure function and want to integrate it into your Reaktor workflow without complex scaffolding.

When to Use AktorFunction

Use AktorFunction when you:

  • Have a simple, pure function that performs a specific task
  • Want to quickly integrate existing utility functions into your Reaktor
  • Need lightweight, reusable components for common operations
  • Don't require complex streaming or advanced Aktor features

Creating AktorFunctions

Synchronous Functions

For functions that return values immediately, use createAktorFunction:

import { createAktorFunction } from '@operaide/aktor';

// A simple pure function
function addNumbers(args: { x: number; y: number }): number {
return args.x + args.y;
}

// Convert it to an AktorFunction factory
export const aktorAddNumbers = createAktorFunction('aktorAddNumbers', addNumbers);

// Usage in a Reaktor
const result = aktorAddNumbers({
x: aktorConst(10),
y: aktorConst(20)
});
// result will be an Aktor<number> that resolves to 30

Asynchronous Functions

For functions that return promises, use createAktorFunctionAsync:

import { createAktorFunctionAsync } from '@operaide/aktor';

// An async function that fetches data
async function fetchWeatherData(args: { city: string }): Promise<string> {
// Simulate API call
await new Promise(resolve => setTimeout(resolve, 1000));
return `Weather in ${args.city}: 22°C, sunny`;
}

// Convert it to an async AktorFunction factory
export const aktorFetchWeather = createAktorFunctionAsync('aktorFetchWeather', fetchWeatherData);

// Usage in a Reaktor
const weatherInfo = aktorFetchWeather({
city: aktorConst('New York')
});
// weatherInfo will be an Aktor<string> that resolves asynchronously

Real-World Examples

String Processing

import { createAktorFunction } from '@operaide/aktor';

function formatUserName(args: { firstName: string; lastName: string }): string {
return `${args.firstName} ${args.lastName}`.trim();
}

export const aktorFormatUserName = createAktorFunction('aktorFormatUserName', formatUserName);

// Usage
const fullName = aktorFormatUserName({
firstName: args.firstName,
lastName: args.lastName
});

Data Validation

import { createAktorFunction } from '@operaide/aktor';

function validateEmail(args: { email: string }): boolean {
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
return emailRegex.test(args.email);
}

export const aktorValidateEmail = createAktorFunction('aktorValidateEmail', validateEmail);

// Usage in conditional logic
const isValidEmail = aktorValidateEmail({ email: userEmail });

API Integration

import axios from 'axios';
import { createAktorFunctionAsync } from '@operaide/aktor';

async function callExternalAPI(args: { endpoint: string; params: Record<string, any> }): Promise<any> {
const url = new URL(args.endpoint);
Object.entries(args.params).forEach(([key, value]) => {
url.searchParams.append(key, String(value));
});

const response = await axios.get(url.toString());
return response.data;
}

export const aktorAPICall = createAktorFunctionAsync('aktorAPICall', callExternalAPI);

// Usage
const apiResponse = aktorAPICall({
endpoint: aktorConst('https://api.example.com/data'),
params: aktorConst({ limit: 10, offset: 0 })
});

How AktorFunctions Work

When you use createAktorFunction or createAktorFunctionAsync, the system:

  1. Wraps your function in an Aktor interface
  2. Handles input resolution - converts Aktor inputs to plain values
  3. Manages caching - results are cached until dependencies change
  4. Provides type safety - maintains TypeScript types throughout the chain
  5. Enables composition - allows chaining with other Aktors

Type Safety

AktorFunctions maintain full type safety:

// Function with typed parameters
function processOrder(args: { orderId: string; quantity: number }): { total: number; status: string } {
return {
total: args.quantity * 29.99,
status: 'processed'
};
}

const aktorProcessOrder = createAktorFunction('aktorProcessOrder', processOrder);

// TypeScript ensures correct types
const order = aktorProcessOrder({
orderId: aktorConst('ORD-123'), // Must be Aktor<string>
quantity: aktorConst(2) // Must be Aktor<number>
});
// order is Aktor<{ total: number; status: string }>

Best Practices

1. Keep Functions Pure

// Good: Pure function
function calculateDiscount(args: { price: number; discountPercent: number }): number {
return args.price * (args.discountPercent / 100);
}

// Avoid: Functions with side effects
function saveAndCalculateDiscount(args: { price: number; discountPercent: number }): number {
// Don't do database operations or other side effects here
database.save(args); // ❌ Side effect
return args.price * (args.discountPercent / 100);
}

2. Use Descriptive Names

// Good: Clear, descriptive names
export const aktorCalculateShippingCost = createAktorFunction('aktorCalculateShippingCost', calculateShippingCost);
export const aktorValidatePostalCode = createAktorFunction('aktorValidatePostalCode', validatePostalCode);

// Avoid: Generic or unclear names
export const aktorProcess = createAktorFunction('aktorProcess', process); // ❌ Too generic

3. Handle Errors Appropriately

async function fetchUserProfile(args: { userId: string }): Promise<UserProfile> {
try {
const response = await fetch(`/api/users/${args.userId}`);
if (!response.ok) {
throw new Error(`Failed to fetch user: ${response.statusText}`);
}
return await response.json();
} catch (error) {
// Provide meaningful error messages
throw new Error(`User profile fetch failed: ${error.message}`);
}
}

export const aktorFetchUserProfile = createAktorFunctionAsync('aktorFetchUserProfile', fetchUserProfile);
info

If you want to make requests to external URLs, you would normally use fetch, which is a built-in web API for making HTTP requests. However, to ensure full compatibility with Operaide's environment and features, it's recommended to use axios instead. Axios provides a more robust interface, better error handling, and works seamlessly within Operaide’s ecosystem.

4. Document Complex Functions

/**
* Calculates the optimal shipping method based on weight, distance, and urgency.
*
* @param args.weight - Package weight in kilograms
* @param args.distance - Shipping distance in kilometers
* @param args.isUrgent - Whether urgent delivery is required
* @returns Shipping method recommendation with cost and estimated delivery time
*/
function calculateOptimalShipping(args: {
weight: number;
distance: number;
isUrgent: boolean
}): ShippingRecommendation {
// Implementation details...
}

export const aktorCalculateOptimalShipping = createAktorFunction('aktorCalculateOptimalShipping', calculateOptimalShipping);

Integration with Other Aktors

AktorFunctions work seamlessly with other Aktor types:

// Combine with conditional logic
const processedData = aktorIf({
if_: aktorValidateInput({ data: inputData }),
then_: aktorProcessData({ data: inputData }),
else_: aktorConst({ error: 'Invalid input data' })
});

// Chain multiple AktorFunctions
const pipeline = aktorProcessOrder({
orderId: aktorFormatOrderId({ rawId: userInput }),
quantity: aktorValidateQuantity({ quantity: userQuantity })
});

// Use with AI integration
const enhancedDescription = aktorAICall({
messages: aktorPatchMessages({
messages: aktorUserPromptAsHistory({
content: aktorCompletePrompt({
template: aktorConst('Enhance this product description: {{description}}'),
description: args.productDescription
})
}),
system: aktorConst('You are a helpful product description writer.')
}),
providerModel: aktorAISettingProviderModel()
});

Summary

AktorFunctions provide a simple, type-safe way to integrate existing functions into your Operaide workflows. They're perfect for:

  • Converting utility functions to Aktors
  • Creating reusable, composable components
  • Maintaining clean, testable code architecture
  • Building complex workflows from simple building blocks

For more complex scenarios requiring streaming, custom caching, or advanced lifecycle management, consider using the full defineAktor approach described in the Building Reaktors and Aktors guide.