Skip to main content

Code Examples

This comprehensive collection of code examples demonstrates how to implement RunAnythingAI's APIs across multiple languages and frameworks. From basic implementation patterns to advanced production techniques, these examples provide ready-to-use code that you can adapt for your specific use cases.

Getting Started

Before diving into the examples, make sure you have:

  1. API Key: Obtained your API key from the RunAnythingAI dashboard
  2. Environment Setup: Installed necessary libraries for your chosen language
  3. Basic Understanding: Reviewed the fundamental API concepts in our Getting Started guide

All examples assume you have your API key stored in an environment variable or configuration file for security.

Advanced Examples

Streaming Character Responses

This example demonstrates how to stream character responses progressively as they're generated:

// Express.js server-side streaming implementation
const express = require('express');
const app = express();
const cors = require('cors');
const fetch = require('node-fetch');

app.use(cors());
app.use(express.json());

// Stream character responses using Server-Sent Events
app.post('/api/character-stream', async (req, res) => {
try {
const { character, messages, persona, botName } = req.body;

// Set up SSE connection
res.setHeader('Content-Type', 'text/event-stream');
res.setHeader('Cache-Control', 'no-cache');
res.setHeader('Connection', 'keep-alive');

// Send initial message
res.write(`data: ${JSON.stringify({ event: 'start', status: 'generating' })}\n\n`);

// Step 1: Request character generation
const genResponse = await fetch(`https://api.runanythingai.com/api/text/${character}`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${process.env.RUNANYTHING_API_KEY}`
},
body: JSON.stringify({
messages,
persona,
botName,
samplingParams: { max_tokens: 150 }
})
});

if (!genResponse.ok) {
const errorData = await genResponse.json();
throw new Error(`API error: ${errorData.error || genResponse.statusText}`);
}

const { id } = await genResponse.json();
res.write(`data: ${JSON.stringify({ event: 'processing', requestId: id, progress: 0 })}\n\n`);

// Step 2: Poll for completion and stream the progress
let completed = false;
let attempts = 0;
let progressEstimate = 0;

while (!completed && attempts < 30) {
attempts++;

// Artificial progress estimation (replace with actual progress if API provides it)
progressEstimate = Math.min(0.9, progressEstimate + Math.random() * 0.2);

const statusResponse = await fetch(`https://api.runanythingai.com/api/v2/status/${id}`, {
headers: { 'Authorization': `Bearer ${process.env.RUNANYTHING_API_KEY}` }
});

if (!statusResponse.ok) {
const errorText = await statusResponse.text();
throw new Error(`Status check failed: ${errorText}`);
}

const statusData = await statusResponse.json();

if (statusData.status === 'completed') {
completed = true;

// Send progress update followed by completion
res.write(`data: ${JSON.stringify({ event: 'progress', progress: 1.0 })}\n\n`);
res.write(`data: ${JSON.stringify({
event: 'completed',
reply: statusData.reply,
processingTime: statusData.processingTime || `${attempts} attempts`
})}\n\n`);

// Now send TTS progress
res.write(`data: ${JSON.stringify({ event: 'tts_start' })}\n\n`);

// Generate TTS in background
generateTTS(statusData.reply)
.then(audioUrl => {
res.write(`data: ${JSON.stringify({ event: 'tts_completed', audioUrl })}\n\n`);
res.end();
})
.catch(error => {
res.write(`data: ${JSON.stringify({ event: 'tts_error', error: error.message })}\n\n`);
res.end();
});

} else if (statusData.status === 'error') {
throw new Error(`Generation error: ${statusData.error}`);
} else {
// Still processing, send progress update
res.write(`data: ${JSON.stringify({
event: 'progress',
progress: progressEstimate,
attempt: attempts
})}\n\n`);

// Wait before next check
await new Promise(resolve => setTimeout(resolve, 1000));
}
}

if (!completed) {
throw new Error('Generation timed out after maximum attempts');
}

} catch (error) {
console.error('Streaming error:', error);
res.write(`data: ${JSON.stringify({ event: 'error', message: error.message })}\n\n`);
res.end();
}
});

// Helper function to generate TTS and return a shareable URL
async function generateTTS(text) {
try {
const response = await fetch('https://api.runanythingai.com/api/audio/full', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${process.env.RUNANYTHING_API_KEY}`
},
body: JSON.stringify({
text,
voice: "af_nicole",
speed: 1
})
});

if (!response.ok) {
throw new Error(`TTS API error: ${response.status}`);
}

// In a real implementation, store the audio file and return a URL
// This is a simplified example
const audioBlob = await response.blob();
const audioUrl = storeAndGetAudioUrl(audioBlob);
return audioUrl;
} catch (error) {
console.error('TTS generation failed:', error);
throw error;
}
}

// Simplified storage function (replace with your actual storage solution)
function storeAndGetAudioUrl(audioBlob) {
// In production, store to S3, Azure Blob Storage, etc.
// Return a publicly accessible URL
return "https://storage.example.com/audio/" + Date.now() + ".mp3";
}

app.listen(3000, () => {
console.log('Streaming server running on http://localhost:3000');
});

Frontend implementation for streaming responses:

// React component for streaming character responses
import React, { useState, useEffect, useRef } from 'react';

function StreamingCharacterChat() {
const [messages, setMessages] = useState([]);
const [input, setInput] = useState('');
const [isStreaming, setIsStreaming] = useState(false);
const [progress, setProgress] = useState(0);
const [audioUrl, setAudioUrl] = useState(null);
const eventSourceRef = useRef(null);
const audioRef = useRef(null);

const sendMessage = async () => {
if (!input.trim() || isStreaming) return;

// Add user message to chat
const userMessage = {
id: `user-${Date.now()}`,
content: input,
role: 'user'
};

setMessages(prev => [...prev, userMessage]);
setInput('');
setIsStreaming(true);
setProgress(0);
setAudioUrl(null);

// Prepare conversation history
const conversationHistory = messages.concat(userMessage).map((msg, index) => ({
role: msg.role === 'user' ? 'You' : 'Bot',
content: msg.content,
index
}));

// Close any existing stream
if (eventSourceRef.current) {
eventSourceRef.current.close();
}

// Create placeholder for bot response
const placeholderId = `bot-${Date.now()}`;
setMessages(prev => [...prev, {
id: placeholderId,
content: '',
role: 'bot',
isLoading: true
}]);

// Connect to SSE endpoint
const eventSource = new EventSource(`/api/character-stream`, {
withCredentials: true
});

eventSourceRef.current = eventSource;

// Send request data
fetch('/api/character-stream', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
character: 'Witch',
messages: conversationHistory,
persona: 'Emma is a friendly and knowledgeable assistant...',
botName: 'Emma'
})
}).catch(error => {
console.error('Failed to initiate streaming:', error);
updateBotMessage(placeholderId, 'Sorry, failed to connect to the server.');
setIsStreaming(false);
eventSource.close();
});

// Handle streaming events
eventSource.onmessage = (event) => {
const data = JSON.parse(event.data);

switch(data.event) {
case 'progress':
setProgress(data.progress);
break;

case 'completed':
updateBotMessage(placeholderId, data.reply, false);
break;

case 'tts_completed':
setAudioUrl(data.audioUrl);
// Auto-play audio if available
if (audioRef.current) {
audioRef.current.src = data.audioUrl;
audioRef.current.play().catch(e => console.log('Auto-play prevented:', e));
}
setIsStreaming(false);
eventSource.close();
break;

case 'error':
updateBotMessage(placeholderId, `Error: ${data.message}`);
setIsStreaming(false);
eventSource.close();
break;
}
};

eventSource.onerror = () => {
updateBotMessage(placeholderId, 'Connection to server lost. Please try again.');
setIsStreaming(false);
eventSource.close();
};
};

// Helper to update the bot's message
const updateBotMessage = (id, content, isLoading = false) => {
setMessages(prev => prev.map(msg =>
msg.id === id ? { ...msg, content, isLoading } : msg
));
};

// Cleanup event source on unmount
useEffect(() => {
return () => {
if (eventSourceRef.current) {
eventSourceRef.current.close();
}
};
}, []);

return (
<div className="streaming-chat">
<div className="messages">
{messages.map(msg => (
<div key={msg.id} className={`message ${msg.role}`}>
{msg.isLoading ? (
<div className="loading-container">
<div className="typing-indicator">
<span></span><span></span><span></span>
</div>
<div className="progress-bar">
<div
className="progress-fill"
style={{ width: `${progress * 100}%` }}
></div>
</div>
</div>
) : (
msg.content
)}
</div>
))}
</div>

<div className="input-area">
<input
type="text"
value={input}
onChange={(e) => setInput(e.target.value)}
onKeyPress={(e) => e.key === 'Enter' && sendMessage()}
placeholder="Type your message..."
disabled={isStreaming}
/>
<button onClick={sendMessage} disabled={isStreaming}>
{isStreaming ? 'Processing...' : 'Send'}
</button>
</div>

{audioUrl && (
<div className="audio-player">
<audio ref={audioRef} controls src={audioUrl}></audio>
</div>
)}
</div>
);
}

export default StreamingCharacterChat;

Code Examples

This page contains practical code examples for common use cases with RunAnythingAI. These examples can be copied and adapted for your own applications.

Table of Contents

Node.js Examples

Character Interaction with Endpoints

This example demonstrates a complete character interaction using the character endpoints with audio response:

const fetch = require('node-fetch');
const fs = require('fs');

async function characterInteraction() {
// Character persona definition
const characterPersona = `Dream Family's Persona: Dream Family is comprised of three women;
Nessa, Marcie, and Angel. Nessa is supportive and nurturing. Marcie is the eldest daughter,
20 years old with a sassy attitude. Angel is the youngest daughter, 18 years old, shy and
loves to cuddle.`;

// Step 1: Send the character generation request (can use Witch, Mage, Succubus, or Lightning)
const response = await fetch('https://api.runanythingai.com/api/text/Succubus', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer YOUR_API_KEY'
},
body: JSON.stringify({
"messages": [
{
"id": "message123",
"messageIndex": 0,
"chatId": "chat456",
"userId": "user789",
"content": "*you see Marcie reading a book on the couch* Hello there, what are you reading?",
"createdAt": new Date().toISOString(),
"upvote": 0,
"role": "You",
"image": null,
"index": 0
}
],
"persona": characterPersona,
"botName": "Dream Family",
"samplingParams": {
"max_tokens": 125
}
})
});

const data = await response.json();
console.log('Generation request submitted:', data);
const statusId = data.id;

// Step 2: Poll for completion
let completed = false;
let result;

while (!completed) {
console.log('Checking status...');
const statusResponse = await fetch(`https://api.runanythingai.com/api/v2/status/${statusId}`, {
headers: {
'Authorization': 'Bearer YOUR_API_KEY'
}
});

const statusData = await statusResponse.json();

if (statusData.status === 'completed') {
completed = true;
result = statusData.reply;
console.log('Character response:', result);
} else if (statusData.status === 'error') {
throw new Error(`Generation failed: ${statusData.error}`);
} else {
console.log('Still processing...');
// Wait before polling again
await new Promise(resolve => setTimeout(resolve, 1000));
}
}

// Step 3: Convert the response to speech
console.log('Generating audio response...');
const ttsResponse = await fetch('https://api.runanythingai.com/api/audio/full', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer YOUR_API_KEY'
},
body: JSON.stringify({
"text": result,
"voice": "af_nicole",
"speed": 1
})
});

if (ttsResponse.ok) {
const buffer = await ttsResponse.buffer();
fs.writeFileSync('character_response.mp3', buffer);
console.log('Audio saved to character_response.mp3');
return {
text: result,
audioPath: 'character_response.mp3'
};
} else {
console.error('TTS generation failed:', await ttsResponse.text());
return {
text: result,
audioPath: null
};
}
}

characterInteraction().catch(console.error);

Text Generation with Status Polling

const fetch = require('node-fetch');

async function generateText(prompt) {
// Step 1: Send the text generation request
const response = await fetch('https://api.runanythingai.com/api/text/default', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer YOUR_API_KEY'
},
body: JSON.stringify({
"messages": [
{
"role": "You",
"content": prompt,
"index": 0
}
],
"botName": "Assistant",
"samplingParams": {
"max_tokens": 200
}
})
});

const data = await response.json();
if (!data.id) {
throw new Error('Failed to get request ID');
}

// Step 2: Poll for the result
let result = await pollForCompletion(data.id);
return result;
}

async function pollForCompletion(requestId, maxAttempts = 30) {
let attempts = 0;

while (attempts < maxAttempts) {
const response = await fetch(`https://api.runanythingai.com/api/v2/status/${requestId}`, {
headers: {
'Authorization': 'Bearer YOUR_API_KEY'
}
});

const data = await response.json();

if (data.status === 'completed') {
return data.reply;
} else if (data.status === 'error') {
throw new Error(`API error: ${data.error || 'Unknown error'}`);
}

// Wait 1 second before the next attempt
await new Promise(resolve => setTimeout(resolve, 1000));
attempts++;
}

throw new Error('Maximum polling attempts reached');
}

// Example usage
generateText("Write a short poem about technology")
.then(result => console.log(result))
.catch(error => console.error('Error:', error));

Text-to-Speech to File

const fetch = require('node-fetch');
const fs = require('fs');
const path = require('path');

async function textToSpeechFile(text, outputPath, voice = 'af_nicole', speed = 1) {
try {
const response = await fetch('https://api.runanythingai.com/api/audio/full', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer YOUR_API_KEY'
},
body: JSON.stringify({
text,
voice,
speed
})
});

if (!response.ok) {
throw new Error(`API error: ${response.status} ${await response.text()}`);
}

const buffer = await response.buffer();
fs.writeFileSync(outputPath, buffer);
console.log(`Audio saved to ${outputPath}`);
return outputPath;
} catch (error) {
console.error('Text-to-speech conversion failed:', error);
throw error;
}
}

// Example usage
textToSpeechFile(
"Welcome to RunAnythingAI's text-to-speech service!",
path.join(__dirname, 'welcome.mp3')
);

Browser JavaScript Examples

Interactive Chat Interface

<!DOCTYPE html>
<html>
<head>
<title>RunAnythingAI Chat Example</title>
<style>
body { font-family: Arial, sans-serif; margin: 0; padding: 20px; }
#chat-container { max-width:
#messages { height: 400px; overflow-y: auto; padding: 10px; }
.message { margin-bottom: 10px; padding: 8px; border-radius: 5px; }
.user-message { background-color: #e1f5fe; text-align: right; }
.bot-message { background-color: #f1f1f1; }
#input-area { display: flex; padding: 10px; background: #f9f9f9; }
#user-input { flex-grow: 1; padding: 10px; margin-right: 10px; }
#send-button { padding: 10px 20px; background: #4caf50; color: white; border: none; cursor: pointer; }
</style>
</head>
<body>
<div id="chat-container">
<div id="messages"></div>
<div id="input-area">
<input type="text" id="user-input" placeholder="Type your message...">
<button id="send-button">Send</button>
</div>
</div>

<script>
const API_KEY = 'YOUR_API_KEY';
const messagesContainer = document.getElementById('messages');
const userInput = document.getElementById('user-input');
const sendButton = document.getElementById('send-button');

// Store conversation history
let conversation = [];

sendButton.addEventListener('click', sendMessage);
userInput.addEventListener('keypress', (e) => {
if (e.key === 'Enter') sendMessage();
});

async function sendMessage() {
const message = userInput.value.trim();
if (!message) return;

// Add user message to UI
addMessageToUI(message, 'user');
userInput.value = '';

// Add to conversation history
conversation.push({
role: 'You',
content: message,
index: conversation.length
});

try {
// Show typing indicator
const typingIndicator = addTypingIndicator();

// Send to API (can use Witch, Mage, Succubus, or Lightning)
const response = await fetch('https://api.runanythingai.com/api/text/Lightning', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${API_KEY}`
},
body: JSON.stringify({
messages: conversation,
botName: 'Assistant',
samplingParams: {
max_tokens: 200
}
})
});

const data = await response.json();

// Poll for result
let completed = false;
let botResponse = '';

while (!completed) {
await new Promise(resolve => setTimeout(resolve, 1000));

const statusResponse = await fetch(`https://api.runanythingai.com/api/v2/status/${data.id}`, {
headers: {
'Authorization': `Bearer ${API_KEY}`
}
});

const statusData = await statusResponse.json();

if (statusData.status === 'completed') {
completed = true;
botResponse = statusData.reply;
} else if (statusData.status === 'error') {
completed = true;
botResponse = 'Sorry, an error occurred while processing your request.';
}
}

// Remove typing indicator
messagesContainer.removeChild(typingIndicator);

// Add bot response to UI
addMessageToUI(botResponse, 'bot');

// Add to conversation history
conversation.push({
role: 'Bot',
content: botResponse,
index: conversation.length
});

} catch (error) {
console.error('Error:', error);
addMessageToUI('Sorry, an error occurred. Please try again.', 'bot');
}
}

function addMessageToUI(message, sender) {
const messageElement = document.createElement('div');
messageElement.textContent = message;
messageElement.classList.add('message');

if (sender === 'user') {
messageElement.classList.add('user-message');
} else {
messageElement.classList.add('bot-message');
}

messagesContainer.appendChild(messageElement);
messagesContainer.scrollTop = messagesContainer.scrollHeight;
}

function addTypingIndicator() {
const indicator = document.createElement('div');
indicator.textContent = 'Assistant is typing...';
indicator.classList.add('message', 'bot-message');
messagesContainer.appendChild(indicator);
messagesContainer.scrollTop = messagesContainer.scrollHeight;
return indicator;
}
</script>
</body>
</html>

Text-to-Speech Player

<!DOCTYPE html>
<html>
<head>
<title>RunAnythingAI TTS Example</title>
<style>
body { font-family: Arial, sans-serif; margin: 0; padding: 20px; }
.container { max-width: 800px; margin: 0 auto; }
textarea { width: 100%; height: 150px; margin-bottom: 10px; padding: 10px; }
.controls { display: flex; gap: 10px; margin-bottom: 20px; }
select, button { padding: 8px; }
button { background: #4caf50; color: white; border: none; cursor: pointer; }
</style>
</head>
<body>
<div class="container">
<h1>Text-to-Speech Demo</h1>
<textarea id="text-input" placeholder="Enter text to convert to speech...">Welcome to RunAnythingAI's text-to-speech service. This is a demonstration of our audio capabilities.</textarea>

<div class="controls">
<select id="voice-select">
<option value="af_nicole">Nicole (Female)</option>
<option value="af_james">James (Male)</option>
<option value="af_emily">Emily (Female)</option>
<option value="af_michael">Michael (Male)</option>
</select>

<select id="speed-select">
<option value="0.8">Slow (0.8x)</option>
<option value="1" selected>Normal (1x)</option>
<option value="1.2">Fast (1.2x)</option>
</select>

<button id="generate-button">Generate Speech</button>
</div>

<div id="audio-container">
<!-- Audio player will be added here -->
</div>
</div>

<script>
const API_KEY = 'YOUR_API_KEY';

document.getElementById('generate-button').addEventListener('click', generateSpeech);

async function generateSpeech() {
const text = document.getElementById('text-input').value;
const voice = document.getElementById('voice-select').value;
const speed = parseFloat(document.getElementById('speed-select').value);

if (!text.trim()) {
alert('Please enter some text');
return;
}

try {
const response = await fetch('https://api.runanythingai.com/api/audio/full', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${API_KEY}`
},
body: JSON.stringify({
text,
voice,
speed
})
});

if (!response.ok) {
throw new Error(`API error: ${response.status}`);
}

const audioBlob = await response.blob();
const audioUrl = URL.createObjectURL(audioBlob);

// Clear previous audio player if exists
const audioContainer = document.getElementById('audio-container');
audioContainer.innerHTML = '';

// Create new audio player
const audio = document.createElement('audio');
audio.controls = true;
audio.src = audioUrl;

audioContainer.appendChild(audio);

// Auto-play
audio.play();

} catch (error) {
console.error('Error generating speech:', error);
alert('Failed to generate speech. Please try again.');
}
}
</script>
</body>
</html>

Python Examples

Text Generation

import requests
import time
import json

API_KEY = "YOUR_API_KEY"

def generate_text(prompt):
"""Generate text using RunAnythingAI API"""

headers = {
"Content-Type": "application/json",
"Authorization": f"Bearer {API_KEY}"
}

payload = {
"messages": [
{
"role": "You",
"content": prompt,
"index": 0
}
],
"botName": "Assistant",
"samplingParams": {
"max_tokens": 200
}
}

# Step 1: Send the request (can use Witch, Mage, Succubus, or Lightning)
response = requests.post(
"https://api.runanythingai.com/api/text/Witch",
headers=headers,
data=json.dumps(payload)
)

response_data = response.json();
if 'id' not in response_data:
raise Exception(f"Failed to get request ID: {response_data}")

request_id = response_data['id']

# Step 2: Poll for the result
max_attempts = 30
attempts = 0

while attempts < max_attempts:
status_response = requests.get(
f"https://api.runanythingai.com/api/v2/status/{request_id}",
headers={"Authorization": f"Bearer {API_KEY}"}
)

status_data = status_response.json()

if status_data.get('status') == 'completed':
return status_data.get('reply')
else if (status_data.get('status') == 'error'):
raise Exception(f"API error: {status_data.get('error', 'Unknown error')}")

# Wait before trying again
time.sleep(1)
attempts += 1

raise Exception("Maximum polling attempts reached")

# Example usage
if __name__ == "__main__":
try:
result = generate_text("Explain the concept of artificial intelligence in simple terms.")
print(result)
except Exception as e:
print(f"Error: {e}")

Text-to-Speech

import requests
import json
import os

API_KEY = "YOUR_API_KEY"

def text_to_speech(text, output_file, voice="af_nicole", speed=1.0):
"""Convert text to speech using RunAnythingAI API"""

headers = {
"Content-Type": "application/json",
"Authorization": f"Bearer {API_KEY}"
}

payload = {
"text": text,
"voice": voice,
"speed": speed
}

response = requests.post(
"https://api.runanythingai.com/api/audio/full",
headers=headers,
data=json.dumps(payload)
)

if response.status_code != 200:
raise Exception(f"API error: {response.status_code} - {response.text}")

# Save the audio file
with open(output_file, 'wb') as f:
f.write(response.content)

print(f"Audio saved to {output_file}")
return output_file

# Example usage
if __name__ == "__main__":
try:
output_file = text_to_speech(
"Welcome to RunAnythingAI's text-to-speech service! This is a Python example.",
"output.mp3"
)

# On macOS, you can play the file directly
if os.name == 'posix':
os.system(f"afplay {output_file}")
except Exception as e:
print(f"Error: {e}")

Complete Applications

For complete application examples including source code, please visit our GitHub repository where you'll find:

  1. A complete chatbot web application
  2. A voice assistant implementation
  3. API integration examples for various frameworks
  4. Mobile app examples using React Native

These examples demonstrate best practices for error handling, rate limiting, and user experience design when working with RunAnythingAI's APIs.

Multi-Character Orchestration

This example demonstrates how to create a system where multiple AI characters can interact with each other:

// Node.js implementation for orchestrating multi-character conversations
const fetch = require('node-fetch');

/**
* Orchestrate a conversation between multiple AI characters
* @param {Array} characters - Array of character configurations
* @param {Array} scenario - Initial scenario and conversation prompt
* @param {Number} turns - Number of conversation turns to generate
* @returns {Array} Conversation history with all character interactions
*/
async function orchestrateMultiCharacterScene(characters, scenario, turns = 3) {
// Initialize conversation with the scenario
let conversation = [{
role: 'System',
content: scenario,
index: 0
}];

console.log(`🎬 Starting scene: ${scenario}`);
console.log(`🎭 Characters: ${characters.map(c => c.name).join(', ')}`);

// Generate conversation turns
for (let turn = 0; turn < turns; turn++) {
console.log(`\n📝 TURN ${turn + 1}`);

// Each character responds in sequence for this turn
for (const character of characters) {
try {
// Prepare specific instructions for this character's turn
const turnInstruction = `Now ${character.name} should respond to the conversation.`;

// Add turn instruction to conversation
conversation.push({
role: 'System',
content: turnInstruction,
index: conversation.length
});

console.log(`👤 ${character.name}'s turn...`);

// Get character's response
const response = await generateCharacterResponse(
character.endpoint,
conversation,
character.persona,
character.name
);

// Add character response to conversation
conversation.push({
role: character.name,
content: response,
index: conversation.length
});

console.log(`${character.name}: "${response}"`);
} catch (error) {
console.error(`Error with ${character.name}'s response:`, error);
// Add error note but continue with scene
conversation.push({
role: 'System',
content: `(${character.name} was unable to respond)`,
index: conversation.length
});
}
}
}

console.log("\n🎬 Scene completed!");
return conversation;
}

/**
* Generate a single character's response
*/
async function generateCharacterResponse(endpoint, conversation, persona, botName) {
// Format conversation for the API
const messages = conversation.map((msg, i) => ({
content: msg.content,
role: msg.role,
index: i
}));

// Get response ID
const genResponse = await fetch(`https://api.runanythingai.com/api/text/${endpoint}`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${process.env.RUNANYTHING_API_KEY}`
},
body: JSON.stringify({
messages,
persona,
botName,
samplingParams: {
max_tokens: 150,
temperature: 0.8
}
})
});

const { id } = await genResponse.json();

// Poll for completion
let completed = false;
let result;

while (!completed) {
const statusResponse = await fetch(`https://api.runanythingai.com/api/v2/status/${id}`, {
headers: { 'Authorization': `Bearer ${process.env.RUNANYTHING_API_KEY}` }
});

const statusData = await statusResponse.json();

if (statusData.status === 'completed') {
completed = true;
result = statusData.reply;
} else if (statusData.status === 'error') {
throw new Error(`Generation failed: ${statusData.error}`);
} else {
await new Promise(resolve => setTimeout(resolve, 1000));
}
}

return result;
}

// Example usage
async function runExampleScene() {
// Define characters
const characters = [
{
name: "Detective Morgan",
endpoint: "Witch",
persona: "Detective Morgan's Persona: Detective Morgan is a 45-year-old veteran detective with 20 years of experience. Morgan has grey hair and always wears a worn trenchcoat. Morgan is methodical, observant, and speaks in short, direct sentences. Morgan has a dry sense of humor and often makes understated jokes. Morgan is skeptical by nature and requires evidence before believing claims. Morgan has extensive knowledge of criminal psychology and investigation techniques."
},
{
name: "Dr. Chen",
endpoint: "Mage",
persona: "Dr. Chen's Persona: Dr. Chen is a 38-year-old forensic scientist with multiple PhDs. Chen has black hair in a neat bun and always wears tailored lab coats. Chen is analytical, precise, and speaks using scientific terminology. Chen values accuracy above all and will correct misconceptions. Chen has a formal demeanor but occasionally reveals excitement when discussing scientific breakthroughs. Chen has extensive knowledge of forensic techniques, chemistry, and biology."
},
{
name: "Officer Rodriguez",
endpoint: "Lightning",
persona: "Officer Rodriguez's Persona: Officer Rodriguez is a 29-year-old patrol officer. Rodriguez has brown hair and always wears a meticulously maintained uniform. Rodriguez is energetic, eager, and speaks rapidly when excited. Rodriguez is optimistic and wants to prove themselves to the senior detectives. Rodriguez often uses slang and colloquialisms. Rodriguez has recent training in modern police procedures but lacks significant field experience."
}
];

// Define scenario
const scenario = "A body has been discovered in an abandoned warehouse. The victim appears to have died from blunt force trauma, but there are unusual chemical burns on their hands. The three investigators have just arrived at the crime scene and are examining the evidence.";

// Generate the scene with 3 turns (each character speaks once per turn)
const conversationHistory = await orchestrateMultiCharacterScene(characters, scenario, 3);

// In a real application, you might save or display this conversation
return conversationHistory;
}

runExampleScene().catch(console.error);

Memory Systems Implementation

This example demonstrates how to implement a memory system for maintaining long-term context with RunAnythingAI characters:

// Node.js implementation of a memory system for RunAnythingAI characters
const fetch = require('node-fetch');
const fs = require('fs').promises;

/**
* Character Memory System
* Manages long-term conversation context for AI characters
*/
class CharacterMemorySystem {
constructor(options = {}) {
this.characterName = options.characterName || 'Assistant';
this.persona = options.persona || '';
this.userId = options.userId || 'user123';
this.endpoint = options.endpoint || 'Witch';
this.apiKey = options.apiKey || process.env.RUNANYTHING_API_KEY;
this.memoryPath = options.memoryPath || './character_memories.json';
this.maxMemories = options.maxMemories || 50;
this.maxTokens = options.maxTokens || 150;
this.importanceThreshold = options.importanceThreshold || 3;

// Initialize memory structure
this.memories = {
core: [], // Unchanging core memories (manually set)
episodic: [], // Recent interactions
consolidated: [] // Important memories extracted from episodic
};

// Load existing memories if available
this.loadMemories();
}

/**
* Load memories from storage
*/
async loadMemories() {
try {
const data = await fs.readFile(this.memoryPath, 'utf8');
const allMemories = JSON.parse(data);

// Find this character's memories or initialize
const characterMemories = allMemories[`${this.characterName}_${this.userId}`];
if (characterMemories) {
this.memories = characterMemories;
console.log(`Loaded ${this.memories.episodic.length} episodic and ${this.memories.consolidated.length} consolidated memories`);
}
} catch (error) {
console.log('No existing memories found, starting fresh');
// We'll create the file on the first save
}
}

/**
* Save memories to storage
*/
async saveMemories() {
try {
// Load existing memories for all characters
let allMemories = {};
try {
const data = await fs.readFile(this.memoryPath, 'utf8');
allMemories = JSON.parse(data);
} catch (error) {
// File doesn't exist yet, that's fine
}

// Update with this character's memories
allMemories[`${this.characterName}_${this.userId}`] = this.memories;

// Save back to file
await fs.writeFile(this.memoryPath, JSON.stringify(allMemories, null, 2), 'utf8');
console.log('Memories saved successfully');
} catch (error) {
console.error('Failed to save memories:', error);
}
}

/**
* Add a new interaction to episodic memory
*/
async addInteraction(userMessage, characterResponse) {
const timestamp = new Date().toISOString();

// Add to episodic memory
this.memories.episodic.push({
timestamp,
user: userMessage,
character: characterResponse,
importance: 1 // Default importance
});

// Limit episodic memories size
if (this.memories.episodic.length > this.maxMemories) {
this.memories.episodic.shift(); // Remove oldest
}

// Every few interactions, consolidate memories
if (this.memories.episodic.length % 5 === 0) {
await this.consolidateMemories();
}

// Save after each interaction
await this.saveMemories();
}

/**
* Add a core memory that never expires
*/
async addCoreMemory(memory) {
this.memories.core.push({
timestamp: new Date().toISOString(),
content: memory
});
await this.saveMemories();
}

/**
* Extract important memories from episodic memory
*/
async consolidateMemories() {
// Only process if we have enough episodic memories
if (this.memories.episodic.length < 3) return;

// Get the most recent episodic memories
const recentMemories = this.memories.episodic.slice(-5);

// Prepare the prompt for memory importance assessment
const memoryConsolidationPrompt = `
You are an AI memory system for a character named ${this.characterName}.
Analyze the following recent interactions and identify any important information
that should be remembered long-term. For each interaction, rate its importance
on a scale of 1-5 (5 being most important). Focus on key facts, relationship
developments, or personal details.

Recent interactions:
${recentMemories.map(m =>
`User: ${m.user}\n${this.characterName}: ${m.character}`
).join('\n\n')}

Provide your analysis as a JSON array of objects with:
1. A concise summary of the important point (memory)
2. An importance rating (1-5)
3. The index of the interaction it came from (0-${recentMemories.length-1})

Format: [{"memory": "summary text", "importance": number, "index": number}]
`;

try {
// Get memory analysis from the API
const analysisResponse = await this.generateResponse(memoryConsolidationPrompt, true);

// Extract JSON
const jsonMatch = analysisResponse.match(/\[.*?\]/s);
if (!jsonMatch) {
console.warn('Could not extract JSON from memory analysis:', analysisResponse);
return;
}

// Parse memory analysis
const analysisResults = JSON.parse(jsonMatch[0]);

// Add important memories to consolidated list
for (const analysis of analysisResults) {
if (analysis.importance >= this.importanceThreshold) {
this.memories.consolidated.push({
timestamp: new Date().toISOString(),
content: analysis.memory,
importance: analysis.importance
});

// Update importance in episodic memory
const episodicIndex = this.memories.episodic.length - (5 - analysis.index);
if (this.memories.episodic[episodicIndex]) {
this.memories.episodic[episodicIndex].importance = analysis.importance;
}
}
}

// Trim consolidated memories if needed
if (this.memories.consolidated.length > this.maxMemories) {
// Sort by importance and recency (weighted)
this.memories.consolidated.sort((a, b) => {
// Calculate recency score (0-1)
const timeA = new Date(a.timestamp).getTime();
const timeB = new Date(b.timestamp).getTime();

// Combined score of importance and recency
return (b.importance * 0.7 + timeB * 0.3) - (a.importance * 0.7 + timeA * 0.3);
});

// Keep only the top memories
this.memories.consolidated = this.memories.consolidated.slice(0, this.maxMemories);
}

await this.saveMemories();
console.log('Memories consolidated successfully');
} catch (error) {
console.error('Memory consolidation failed:', error);
}
}

/**
* Recall relevant memories for the current conversation
*/
async recallRelevantMemories(currentContext) {
// If we have few memories, return them all
if (this.memories.consolidated.length <= 3 && this.memories.core.length <= 3) {
return [
...this.memories.core.map(m => m.content),
...this.memories.consolidated.map(m => m.content)
];
}

// Prepare prompt for memory retrieval
const retrievalPrompt = `
You are an AI memory system for a character named ${this.characterName}.
Based on the current conversation context, identify the MOST relevant memories
that should be recalled to provide context for the character's next response.

Current conversation:
${currentContext}

Available memories:
Core memories:
${this.memories.core.map((m, i) => `${i+1}. ${m.content}`).join('\n')}

Consolidated memories:
${this.memories.consolidated.map((m, i) => `${i+1}. ${m.content} (Importance: ${m.importance})`).join('\n')}

Return ONLY the numbers of the most relevant memories, in order of relevance.
Format: "Core: [list of numbers], Consolidated: [list of numbers]"
Example: "Core: [1, 3], Consolidated: [2, 5, 7]"

Select AT MOST 3 core memories and 5 consolidated memories.
`;

try {
// Get memory retrieval guidance
const retrievalResponse = await this.generateResponse(retrievalPrompt, true);

// Extract memory indices
const coreMatch = retrievalResponse.match(/Core:\s*\[(.*?)\]/);
const consolidatedMatch = retrievalResponse.match(/Consolidated:\s*\[(.*?)\]/);

let relevantMemories = [];

// Add relevant core memories
if (coreMatch && coreMatch[1]) {
const coreIndices = coreMatch[1].split(',').map(i => parseInt(i.trim()) - 1).filter(i => !isNaN(i));
for (const i of coreIndices) {
if (this.memories.core[i]) {
relevantMemories.push(this.memories.core[i].content);
}
}
}

// Add relevant consolidated memories
if (consolidatedMatch && consolidatedMatch[1]) {
const consolidatedIndices = consolidatedMatch[1].split(',').map(i => parseInt(i.trim()) - 1).filter(i => !isNaN(i));
for (const i of consolidatedIndices) {
if (this.memories.consolidated[i]) {
relevantMemories.push(this.memories.consolidated[i].content);
}
}
}

return relevantMemories;
} catch (error) {
console.error('Memory retrieval failed:', error);

// Fallback to most recent/important memories
return [
...this.memories.core.slice(0, 2).map(m => m.content),
...this.memories.consolidated
.sort((a, b) => b.importance - a.importance)
.slice(0, 3)
.map(m => m.content)
];
}
}

/**
* Generate a character response with memory-enhanced context
*/
async generateMemoryEnhancedResponse(userMessage, conversationHistory = []) {
try {
// Format the conversation history for context
const formattedHistory = conversationHistory.map(msg =>
`${msg.role}: ${msg.content}`
).join('\n');

// Get relevant memories for this conversation
const relevantMemories = await this.recallRelevantMemories(
`${formattedHistory}\nUser: ${userMessage}`
);

// Create memory-enhanced messages for the API
const messages = [
// Add system message with persona and memories
{
role: 'System',
content: `${this.persona}\n\nIMPORTANT MEMORIES:\n${relevantMemories.join('\n')}`,
index: 0
},

// Add conversation history
...conversationHistory.map((msg, i) => ({
role: msg.role,
content: msg.content,
index: i + 1
})),

// Add current user message
{
role: 'You',
content: userMessage,
index: conversationHistory.length + 1
}
];

// Generate the character's response
const characterResponse = await this.generateResponse(messages);

// Add the interaction to memory
await this.addInteraction(userMessage, characterResponse);

return characterResponse;
} catch (error) {
console.error('Failed to generate response with memories:', error);
throw error;
}
}

/**
* Helper: Generate text using the RunAnythingAI API
*/
async generateResponse(input, isSystemPrompt = false) {
try {
// Prepare messages based on input type
let messages;

if (isSystemPrompt) {
// Internal system prompt for memory operations
messages = [{
role: 'System',
content: input,
index: 0
}];
} else if (Array.isArray(input)) {
// Full message array
messages = input;
} else {
// Single user message
messages = [{
role: 'You',
content: input,
index: 0
}];
}

// Step 1: Send the generation request
const response = await fetch(`https://api.runanythingai.com/api/text/${this.endpoint}`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${this.apiKey}`
},
body: JSON.stringify({
messages,
botName: isSystemPrompt ? 'Memory System' : this.characterName,
samplingParams: {
max_tokens: this.maxTokens,
temperature: isSystemPrompt ? 0.3 : 0.7
}
})
});

const data = await response.json();
if (!data.id) {
throw new Error(`Failed to get request ID: ${JSON.stringify(data)}`);
}

// Step 2: Poll for the result
let completed = false;
let attempts = 0;

while (!completed && attempts < 30) {
attempts++;

const statusResponse = await fetch(`https://api.runanythingai.com/api/v2/status/${data.id}`, {
headers: { 'Authorization': `Bearer ${this.apiKey}` }
});

const statusData = await statusResponse.json();

if (statusData.status === 'completed') {
completed = true;
return statusData.reply;
} else if (statusData.status === 'error') {
throw new Error(`API error: ${statusData.error || 'Unknown error'}`);
}

// Wait before next attempt
await new Promise(resolve => setTimeout(resolve, 1000));
}

throw new Error('Maximum polling attempts reached');
} catch (error) {
console.error('Generation failed:', error);
throw error;
}
}
}

// Example usage
async function demonstrateMemorySystem() {
// Initialize the memory system
const memorySystem = new CharacterMemorySystem({
characterName: 'Professor Thompson',
endpoint: 'Mage',
persona: `Professor Thompson's Persona: Professor Thompson is a 65-year-old history professor specializing in ancient civilizations.
Thompson has white hair and a neatly trimmed beard, and always wears tweed jackets with elbow patches.
Thompson speaks formally and often references historical events as metaphors.
Thompson is patient but can be forgetful about recent events.`,
userId: 'student123',
memoryPath: './professor_memories.json'
});

// Add some core memories
await memorySystem.addCoreMemory('You've been teaching at the university for over 30 years');
await memorySystem.addCoreMemory('Your favorite historical period is Ancient Egypt');
await memorySystem.addCoreMemory('You have a cat named Cleopatra that you mention frequently');

// Simulate a conversation
const conversation = [];

// First interaction
console.log('STUDENT: Good morning Professor! I'm excited about our discussion on ancient trade routes today.');
const response1 = await memorySystem.generateMemoryEnhancedResponse(
'Good morning Professor! I'm excited about our discussion on ancient trade routes today.'
);
conversation.push({ role: 'You', content: 'Good morning Professor! I'm excited about our discussion on ancient trade routes today.' });
conversation.push({ role: 'Professor Thompson', content: response1 });
console.log(`PROFESSOR: ${response1}\n`);

// Second interaction
console.log('STUDENT: By the way, how is Cleopatra doing? Did she recover from her cold?');
const response2 = await memorySystem.generateMemoryEnhancedResponse(
'By the way, how is Cleopatra doing? Did she recover from her cold?',
conversation
);
conversation.push({ role: 'You', content: 'By the way, how is Cleopatra doing? Did she recover from her cold?' });
conversation.push({ role: 'Professor Thompson', content: response2 });
console.log(`PROFESSOR: ${response2}\n`);

// Third interaction with new information
console.log('STUDENT: I finished that book you recommended - "The Silk Roads". The chapter on maritime trade was fascinating!');
const response3 = await memorySystem.generateMemoryEnhancedResponse(
'I finished that book you recommended - "The Silk Roads". The chapter on maritime trade was fascinating!',
conversation
);
conversation.push({ role: 'You', content: 'I finished that book you recommended - "The Silk Roads". The chapter on maritime trade was fascinating!' });
conversation.push({ role: 'Professor Thompson', content: response3 });
console.log(`PROFESSOR: ${response3}\n`);

// Later conversation (would normally happen in a different session)
console.log('\n--- A WEEK LATER ---\n');

// The professor should remember the book discussion!
console.log('STUDENT: I\'ve been thinking more about maritime trade routes since our last discussion.');
const response4 = await memorySystem.generateMemoryEnhancedResponse(
'I\'ve been thinking more about maritime trade routes since our last discussion.',
[] // New conversation, but memories should persist
);
console.log(`PROFESSOR: ${response4}\n`);

// Show memory statistics
console.log('Memory system status:');
console.log(`- Core memories: ${memorySystem.memories.core.length}`);
console.log(`- Episodic memories: ${memorySystem.memories.episodic.length}`);
console.log(`- Consolidated memories: ${memorySystem.memories.consolidated.length}`);
}

// Run the example
demonstrateMemorySystem().catch(console.error);

This memory system implementation provides:

  1. Three memory layers:

    • Core memories (unchanging facts about the character)
    • Episodic memories (recent conversations)
    • Consolidated memories (important information extracted from episodic memory)
  2. Memory management features:

    • Automatic memory consolidation based on importance
    • Context-based memory retrieval
    • Persistence between sessions
    • Memory importance rating
  3. Production-ready features:

    • Error handling and fallbacks
    • Configurable parameters
    • Efficient memory optimization
    • Multi-user support

You can adapt this implementation for your specific use case, whether you're building a chatbot, virtual assistant, or interactive character for your application.