⚡ Actions
Actions are core building blocks in OKai that define how agents respond to and interact with messages. They allow agents to interact with external systems, modify their behavior, and perform tasks beyond simple message responses.
Overview
Each Action consists of:
name
: Unique identifier for the actionsimiles
: Array of alternative names/variationsdescription
: Detailed explanation of the action's purposevalidate
: Function that checks if action is appropriatehandler
: Implementation of the action's behaviorexamples
: Array of example usage patterns
Implementation
interface Action {
name: string;
similes: string[];
description: string;
examples: ActionExample[][];
handler: Handler;
validate: Validator;
}
Source: https://github.com/okcashpro/okai/packages/core/src/types.ts
Built-in Actions
Conversation Flow
CONTINUE
- Maintains conversation when more context is needed
- Manages natural dialogue progression
- Limited to 3 consecutive continues
IGNORE
- Gracefully disengages from conversations
- Handles:
- Inappropriate interactions
- Natural conversation endings
- Post-closing responses
NONE
- Default response action
- Used for standard conversational replies
External Integrations
TAKE_ORDER
- Records trading/purchase orders
- Processes user conviction levels
- Validates ticker symbols and contract addresses
const take_order: Action = {
name: "TAKE_ORDER",
similes: ["BUY_ORDER", "PLACE_ORDER"],
description: "Records a buy order based on the user's conviction level.",
validate: async (runtime: IAgentRuntime, message: Memory) => {
const text = (message.content as Content).text;
const tickerRegex = /\b[A-Z]{1,5}\b/g;
return tickerRegex.test(text);
},
// ... rest of implementation
};
Source: https://github.com/okcashpro/okai/packages/plugin-solana/src/actions/takeOrder.ts
Creating Custom Actions
- Implement the Action interface
- Define validation logic
- Implement handler functionality
- Provide usage examples
Example:
const customAction: Action = {
name: "CUSTOM_ACTION",
similes: ["SIMILAR_ACTION"],
description: "Action purpose",
validate: async (runtime: IAgentRuntime, message: Memory) => {
// Validation logic
return true;
},
handler: async (runtime: IAgentRuntime, message: Memory) => {
// Implementation
},
examples: [],
};
Testing Actions
Use the built-in testing framework:
test("Validate action behavior", async () => {
const message: Memory = {
userId: user.id,
content: { text: "Test message" },
roomId,
};
const response = await handleMessage(runtime, message);
// Verify response
});
Core Concepts
Action Structure
interface Action {
name: string;
similes: string[];
description: string;
validate: (runtime: IAgentRuntime, message: Memory) => Promise<boolean>;
handler: (
runtime: IAgentRuntime,
message: Memory,
state?: State,
) => Promise<void>;
examples: ActionExample[][];
}
Key Components
- name: Unique identifier for the action
- similes: Alternative names/triggers for the action
- description: Explains when and how the action should be used
- validate: Determines if the action can be executed
- handler: Implements the action's behavior
- examples: Demonstrates proper usage patterns
Built-in Actions
CONTINUE
Continues the conversation when appropriate:
const continueAction: Action = {
name: "CONTINUE",
similes: ["ELABORATE", "KEEP_TALKING"],
description:
"Used when the message requires a follow-up. Don't use when conversation is finished.",
validate: async (runtime, message) => {
// Validation logic
return true;
},
handler: async (runtime, message, state) => {
// Continuation logic
},
};
IGNORE
Stops responding to irrelevant or completed conversations:
const ignoreAction: Action = {
name: "IGNORE",
similes: ["STOP_TALKING", "STOP_CHATTING"],
description:
"Used when ignoring the user is appropriate (conversation ended, user is aggressive, etc.)",
handler: async (runtime, message) => {
return true;
},
};
FOLLOW_ROOM
Actively participates in a conversation:
const followRoomAction: Action = {
name: "FOLLOW_ROOM",
similes: ["FOLLOW_CHAT", "FOLLOW_CONVERSATION"],
description:
"Start following channel with interest, responding without explicit mentions.",
handler: async (runtime, message) => {
// Room following logic
},
};
Creating Custom Actions
Basic Action Template
const customAction: Action = {
name: "CUSTOM_ACTION",
similes: ["ALTERNATE_NAME", "OTHER_TRIGGER"],
description: "Detailed description of when and how to use this action",
validate: async (runtime: IAgentRuntime, message: Memory) => {
// Validation logic
return true;
},
handler: async (runtime: IAgentRuntime, message: Memory) => {
// Implementation logic
return true;
},
examples: [
[
{
user: "{{user1}}",
content: { text: "Trigger message" },
},
{
user: "{{user2}}",
content: { text: "Response", action: "CUSTOM_ACTION" },
},
],
],
};
Advanced Action Example
const complexAction: Action = {
name: "PROCESS_DOCUMENT",
similes: ["READ_DOCUMENT", "ANALYZE_DOCUMENT"],
description: "Process and analyze uploaded documents",
validate: async (runtime, message) => {
const hasAttachment = message.content.attachments?.length > 0;
const supportedTypes = ["pdf", "txt", "doc"];
return (
hasAttachment &&
supportedTypes.includes(message.content.attachments[0].type)
);
},
handler: async (runtime, message, state) => {
const attachment = message.content.attachments[0];
// Process document
const content = await runtime
.getService<IDocumentService>(ServiceType.DOCUMENT)
.processDocument(attachment);
// Store in memory
await runtime.documentsManager.createMemory({
id: generateId(),
content: { text: content },
userId: message.userId,
roomId: message.roomId,
});
return true;
},
};
Implementation Patterns
State-Based Actions
const stateAction: Action = {
name: "UPDATE_STATE",
handler: async (runtime, message, state) => {
const newState = await runtime.composeState(message, {
additionalData: "new-data",
});
await runtime.updateState(newState);
return true;
},
};
Service Integration
const serviceAction: Action = {
name: "TRANSCRIBE_AUDIO",
handler: async (runtime, message) => {
const transcriptionService = runtime.getService<ITranscriptionService>(
ServiceType.TRANSCRIPTION,
);
const result = await transcriptionService.transcribe(
message.content.attachments[0],
);
return true;
},
};
Best Practices
Action Design
-
Clear Purpose
- Single responsibility principle
- Well-defined triggers
- Clear success criteria
-
Robust Validation
- Check prerequisites
- Validate input data
- Handle edge cases
-
Error Handling
- Graceful failure
- Meaningful error messages
- State recovery
Example Organization
- Comprehensive Coverage
examples: [
// Happy path
[basicUsageExample],
// Edge cases
[edgeCaseExample],
// Error cases
[errorCaseExample],
];
- Clear Context
examples: [
[
{
user: "{{user1}}",
content: {
text: "Context message showing why action is needed",
},
},
{
user: "{{user2}}",
content: {
text: "Clear response demonstrating action usage",
action: "ACTION_NAME",
},
},
],
];
Troubleshooting
Common Issues
-
Action Not Triggering
- Check validation logic
- Verify similes list
- Review example patterns
-
Handler Failures
- Validate service availability
- Check state requirements
- Review error logs
-
State Inconsistencies
- Verify state updates
- Check concurrent modifications
- Review state transitions
Advanced Features
Action Composition
const compositeAction: Action = {
name: "PROCESS_AND_RESPOND",
handler: async (runtime, message) => {
// Process first action
await runtime.processAction("ANALYZE_CONTENT", message);
// Process second action
await runtime.processAction("GENERATE_RESPONSE", message);
return true;
},
};
Action Chains
const chainedAction: Action = {
name: "WORKFLOW",
handler: async (runtime, message) => {
const actions = ["VALIDATE", "PROCESS", "RESPOND"];
for (const actionName of actions) {
await runtime.processAction(actionName, message);
}
return true;
},
};
Example: Complete Action Implementation
import { Action, IAgentRuntime, Memory, State } from "@okcashpro/okai";
const documentAnalysisAction: Action = {
name: "ANALYZE_DOCUMENT",
similes: ["READ_DOCUMENT", "PROCESS_DOCUMENT", "REVIEW_DOCUMENT"],
description: "Analyzes uploaded documents and provides insights",
validate: async (runtime: IAgentRuntime, message: Memory) => {
// Check for document attachment
if (!message.content.attachments?.length) {
return false;
}
// Verify document type
const attachment = message.content.attachments[0];
return ["pdf", "txt", "doc"].includes(attachment.type);
},
handler: async (runtime: IAgentRuntime, message: Memory, state?: State) => {
try {
// Get document service
const docService = runtime.getService<IDocumentService>(
ServiceType.DOCUMENT,
);
// Process document
const content = await docService.processDocument(
message.content.attachments[0],
);
// Store analysis
await runtime.documentsManager.createMemory({
id: generateId(),
content: {
text: content,
analysis: await docService.analyze(content),
},
userId: message.userId,
roomId: message.roomId,
createdAt: Date.now(),
});
return true;
} catch (error) {
console.error("Document analysis failed:", error);
return false;
}
},
examples: [
[
{
user: "{{user1}}",
content: {
text: "Can you analyze this document?",
attachments: [{ type: "pdf", url: "document.pdf" }],
},
},
{
user: "{{user2}}",
content: {
text: "I'll analyze that document for you",
action: "ANALYZE_DOCUMENT",
},
},
],
],
};
Best Practices
-
Validation
- Thoroughly check input parameters
- Verify runtime conditions
- Handle edge cases
-
Error Handling
- Implement comprehensive error catching
- Provide clear error messages
- Clean up resources properly
-
Documentation
- Include clear usage examples
- Document expected inputs/outputs
- Explain error scenarios