Skip to main content
Version: 2.6

LLM Calls

AI integration is a core feature of Operaide, enabling you to build sophisticated workflows that leverage Large Language Models (LLMs) for text processing, analysis, and generation. This guide covers everything you need to know about integrating AI into your Reaktors and Aktors.

Overview

Operaide provides a comprehensive set of AI-focused Aktors that work seamlessly with multiple AI providers including OpenAI, Azure OpenAI, Anthropic, Google, and many others. The AI integration is designed to be:

  • Provider-agnostic: Switch between different AI providers without changing your code
  • Type-safe: Full TypeScript support with proper type inference
  • Streaming-capable: Support for real-time streaming responses
  • Composable: Easily combine with other Aktors in complex workflows

Core AI Aktors

aktorAICall

The primary Aktor for making LLM calls. Supports both standard and streaming responses.

import { aktorAICall, aktorAISettingProviderModel } from '@operaide/ai';
import { aktorConst } from '@operaide/aktor';

const response = aktorAICall({
messages: aktorConst([
{ role: 'system', content: 'You are a helpful assistant.' },
{ role: 'user', content: 'What is the capital of France?' }
]),
providerModel: aktorAISettingProviderModel()
});

aktorCompletePrompt

Template completion for dynamic prompt generation with variable substitution.

import { aktorCompletePrompt } from '@operaide/ai';

const completedPrompt = aktorCompletePrompt({
template: aktorConst('Hello {{name}}, today is {{date}}.'),
name: args.userName,
date: aktorConst(new Date().toLocaleDateString())
});

aktorPatchMessages

Modify existing message arrays by adding system prompts.

import { aktorPatchMessages, aktorUserPromptAsHistory } from '@operaide/ai';

const messages = aktorPatchMessages({
messages: aktorUserPromptAsHistory({
content: args.userMessage
}),
system: aktorConst('You are an expert data analyst.')
});

General Usage Patterns

Simple Q&A Workflow

import {
aktorAICall,
aktorAISettingProviderModel,
aktorMessagesFromSystemAndUser
} from '@operaide/ai';
import { aktorConst, registerReaktorDefinition } from '@operaide/aktor';
import { z } from 'zod';

registerReaktorDefinition({
reaktorDefinitionId: 'simple-qa-assistant',
label: 'AI Assistant',
description: 'Simple AI-powered question answering',
inputSchema: z.object({
question: z.string().describe('The question to ask the AI')
}),
outputSchema: z.string().describe('The AI-generated answer'),
createReaktor({ question }) {
const messages = aktorMessagesFromSystemAndUser({
user: question,
system: aktorConst('You are a helpful and knowledgeable assistant.')
});

const aiResponse = aktorAICall({
messages,
providerModel: aktorAISettingProviderModel()
});

return aiResponse;
}
});

Document Analysis Workflow

import {
aktorAICall,
aktorCompletePrompt,
aktorPatchMessages,
aktorMessagesFromSystemAndUser,
aktorAISettingProviderModel
} from '@operaide/ai';
import { aktorConst } from '@operaide/aktor';
import { dedent, registerReaktorDefinition } from '@operaide/aktor';
import { z } from 'zod';

registerReaktorDefinition({
reaktorDefinitionId: 'simple-qa-assistant',
label: 'AI Assistant',
description: 'Simple AI-powered question answering',
inputSchema: z.object({
document: z.string(),
analysisType: z.enum(['summary', 'detailed']).describe('Type of analysis to perform on the document')
}),
outputSchema: z.string().describe('The AI-generated answer'),
createReaktor({ document, analysisType }) {
const analysisPrompt = aktorCompletePrompt({
template: aktorConst(dedent`
Please analyze the following document and provide a {{analysisType}} analysis:

Document:
{{document}}

Analysis Type: {{analysisType}}

Please provide a detailed analysis including:
1. Key findings
2. Main themes
3. Actionable insights
`),
document,
analysisType
});

const messages = aktorPatchMessages({
messages: aktorMessagesFromSystemAndUser({
user: analysisPrompt
}),
system: aktorConst('You are an expert document analyst with expertise in extracting meaningful insights from text.')
});

return aktorAICall({
messages,
providerModel: aktorAISettingProviderModel()
});
}
});

Best Practices

1. Prompt Engineering

// Good: Structured, clear prompts
const structuredPrompt = dedent`
You are a professional email writer.

Task: Write a follow-up email based on the meeting notes.

Meeting Notes:
{{meetingNotes}}

Requirements:
- Professional tone
- Include action items
- Keep under 200 words
- Include a clear subject line

Format your response as JSON:
{
"subject": "Email subject line",
"body": "Email body content"
}
`;

// Avoid: Vague, unstructured prompts
const vaguePRompt = "Write an email about the meeting: {{notes}}";

2. Input Validation

import { aktorAICall, aktorAISettingProviderModel, aktorMessagesFromSystemAndUser } from '@operaide/ai';
import type { Aktor } from '@operaide/aktor';
import { createAktorFunction } from '@operaide/aktor';
import { z } from 'zod';

const validateInput = createAktorFunction('aktorValidateInput', ({ text }) => {
const schema = z.string().min(1).max(10000);
return schema.parse(text);
});

// Hint: No real reaktor here
const safeAICall = ({ userInput }: { userInput: Aktor<string> }) => {
const validatedInput = validateInput({ text: userInput });

return aktorAICall({
messages: aktorMessagesFromSystemAndUser({ user: validatedInput }),
providerModel: aktorAISettingProviderModel(),
});
};

3. Response Processing

import { aktorAICall, aktorAISettingProviderModel, aktorMessagesFromSystemAndUser } from '@operaide/ai';
import type { Aktor } from '@operaide/aktor';
import { createAktorFunction } from '@operaide/aktor';

const processAIResponse = createAktorFunction('aktorProcessAIResponse', ({ response }: { response: string }) => {
try {
// Try to parse as JSON if expected
const parsed = JSON.parse(response);
return parsed;
} catch {
// Fallback to plain text
return { content: response, type: 'text' };
}
});

// Hint: No real reaktor here
const structuredAIWorkflow = ({ input }: { input: Aktor<string> }) => {
const rawResponse = aktorAICall({
messages: aktorMessagesFromSystemAndUser({ user: input }),
providerModel: aktorAISettingProviderModel(),
});

return processAIResponse({ response: rawResponse });
};

Summary

LLM integration in Operaide provides powerful capabilities for building AI-powered workflows. Key takeaways:

  • Use aktorAICall for all LLM interactions
  • Leverage aktorCompletePrompt for dynamic prompt generation
  • Structure prompts clearly for better results
  • Handle errors gracefully with fallbacks
  • Use streaming for real-time applications
  • Cache responses when appropriate
  • Test AI workflows thoroughly

For more advanced AI patterns and examples, see the Examples section and explore the Prompt Engineering guide.