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:
- Wraps your function in an Aktor interface
- Handles input resolution - converts Aktor inputs to plain values
- Manages caching - results are cached until dependencies change
- Provides type safety - maintains TypeScript types throughout the chain
- 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);
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.