@agentdao/core
Version:
Core functionality, skills, and ready-made UI components for AgentDAO - Web3 subscriptions, content generation, social media, help support, live chat, RSS fetching, web search, and agent pricing integration
312 lines (311 loc) • 12.7 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.ContentGeneratorSkill = void 0;
const content_utils_1 = require("./content-utils");
class ContentGeneratorSkill {
constructor(config) {
this.retryCount = 0;
this.maxRetries = 3;
this.validateConfig(config);
this.config = config;
}
/**
* Validate configuration on initialization
*/
validateConfig(config) {
if (!config.ai?.apiKey) {
throw new Error('AI API key is required for ContentGeneratorSkill');
}
if (!config.ai.model) {
throw new Error('AI model is required for ContentGeneratorSkill');
}
if (config.ai.temperature < 0 || config.ai.temperature > 2) {
throw new Error('Temperature must be between 0 and 2');
}
if (config.ai.maxTokens < 1 || config.ai.maxTokens > 4000) {
throw new Error('Max tokens must be between 1 and 4000');
}
if (!config.brand?.name) {
throw new Error('Brand name is required for ContentGeneratorSkill');
}
}
/**
* Generate blog post with robust error handling and retries
*/
async generateBlogPost(topic, keywords = [], options = {}) {
const { length = 'medium', tone = 'professional', includeSEO = true, retryOnFailure = true } = options;
// Input validation
this.validateInput('topic', topic, 'string', 1, 200);
this.validateInput('keywords', keywords, 'array');
this.validateInput('length', length, 'enum', undefined, undefined, ['short', 'medium', 'long']);
this.validateInput('tone', tone, 'enum', undefined, undefined, ['professional', 'casual', 'technical']);
// Merge brand keywords with provided keywords
const allKeywords = [...new Set([...keywords, ...(this.config.brand.keywords || [])])];
return this.executeWithRetry(async () => {
try {
return await content_utils_1.ContentGenerators.generateBlogPost(topic, allKeywords, {
length,
tone,
includeSEO
});
}
catch (error) {
this.handleContentGenerationError(error, 'blog post', topic);
throw error;
}
}, retryOnFailure, `Failed to generate blog post about "${topic}"`);
}
/**
* Generate social media post with robust error handling
*/
async generateSocialPost(platform, topic, options = {}) {
const { tone = 'engaging', includeHashtags = true, retryOnFailure = true } = options;
// Input validation
this.validateInput('platform', platform, 'enum', undefined, undefined, ['twitter', 'linkedin', 'facebook', 'instagram']);
this.validateInput('topic', topic, 'string', 1, 100);
return this.executeWithRetry(async () => {
try {
return await content_utils_1.ContentGenerators.generateSocialPost(platform, topic, {
tone,
includeHashtags
});
}
catch (error) {
this.handleContentGenerationError(error, `${platform} post`, topic);
throw error;
}
}, retryOnFailure, `Failed to generate ${platform} post about "${topic}"`);
}
/**
* Generate email content with robust error handling
*/
async generateEmail(purpose, recipient, options = {}) {
const { tone = 'formal', length = 'medium', retryOnFailure = true } = options;
// Input validation
this.validateInput('purpose', purpose, 'string', 1, 100);
this.validateInput('recipient', recipient, 'string', 1, 100);
return this.executeWithRetry(async () => {
try {
return await content_utils_1.ContentGenerators.generateEmail(purpose, recipient, {
tone,
length
});
}
catch (error) {
this.handleContentGenerationError(error, 'email', purpose);
throw error;
}
}, retryOnFailure, `Failed to generate email for "${purpose}"`);
}
/**
* Generate content using custom template
*/
async generateFromTemplate(templateId, variables, options = {}) {
const { retryOnFailure = true } = options;
// Validate template exists
const template = this.config.templates[templateId];
if (!template) {
throw new Error(`Template "${templateId}" not found`);
}
// Validate required variables
this.validateTemplateVariables(template, variables);
return this.executeWithRetry(async () => {
try {
// This would be implemented based on template type
return await this.generateFromTemplateInternal(template, variables);
}
catch (error) {
this.handleContentGenerationError(error, template.type, templateId);
throw error;
}
}, retryOnFailure, `Failed to generate content from template "${templateId}"`);
}
/**
* Optimize existing content for SEO
*/
async optimizeContent(content, keywords = [], options = {}) {
const { retryOnFailure = true } = options;
// Input validation
this.validateInput('content', content, 'string', 10, 10000);
this.validateInput('keywords', keywords, 'array');
// Merge with brand keywords
const allKeywords = [...new Set([...keywords, ...(this.config.brand.keywords || [])])];
return this.executeWithRetry(async () => {
try {
return await content_utils_1.ContentGenerators.optimizeContent(content, allKeywords);
}
catch (error) {
this.handleContentGenerationError(error, 'content optimization', 'content');
throw error;
}
}, retryOnFailure, 'Failed to optimize content');
}
/**
* Generate meta tags for SEO
*/
async generateMetaTags(title, content, options = {}) {
const { retryOnFailure = true } = options;
// Input validation
this.validateInput('title', title, 'string', 1, 200);
this.validateInput('content', content, 'string', 10, 10000);
return this.executeWithRetry(async () => {
try {
return await content_utils_1.ContentGenerators.generateMetaTags(title, content);
}
catch (error) {
this.handleContentGenerationError(error, 'meta tags', title);
throw error;
}
}, retryOnFailure, `Failed to generate meta tags for "${title}"`);
}
/**
* Execute function with retry logic
*/
async executeWithRetry(fn, retryOnFailure, errorMessage) {
let lastError = null;
const maxAttempts = retryOnFailure ? this.config.ai.retryAttempts || this.maxRetries : 1;
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
try {
return await fn();
}
catch (error) {
lastError = error;
// Don't retry on certain types of errors
if (this.shouldNotRetry(error)) {
throw error;
}
// If this is the last attempt, throw the error
if (attempt === maxAttempts) {
throw new content_utils_1.ContentGenerationError(`${errorMessage} after ${maxAttempts} attempts: ${lastError.message}`, lastError);
}
// Wait before retrying (exponential backoff)
const delay = Math.min(1000 * Math.pow(2, attempt - 1), 10000);
await this.sleep(delay);
}
}
throw lastError || new Error(errorMessage);
}
/**
* Determine if an error should not be retried
*/
shouldNotRetry(error) {
// Don't retry on authentication errors, invalid input, or rate limits
const nonRetryableErrors = [
'APIKeyMissingError',
'authentication',
'unauthorized',
'invalid input',
'rate limit',
'quota exceeded'
];
return nonRetryableErrors.some(keyword => error.message.toLowerCase().includes(keyword.toLowerCase()));
}
/**
* Input validation helper
*/
validateInput(fieldName, value, type, minLength, maxLength, allowedValues) {
// Type validation
if (type === 'string' && typeof value !== 'string') {
throw new Error(`${fieldName} must be a string`);
}
if (type === 'array' && !Array.isArray(value)) {
throw new Error(`${fieldName} must be an array`);
}
// Length validation for strings
if (type === 'string' && typeof value === 'string') {
if (minLength && value.length < minLength) {
throw new Error(`${fieldName} must be at least ${minLength} characters long`);
}
if (maxLength && value.length > maxLength) {
throw new Error(`${fieldName} must be no more than ${maxLength} characters long`);
}
}
// Enum validation
if (type === 'enum' && allowedValues && !allowedValues.includes(value)) {
throw new Error(`${fieldName} must be one of: ${allowedValues.join(', ')}`);
}
}
/**
* Validate template variables
*/
validateTemplateVariables(template, variables) {
const requiredVars = template.variables || [];
const missingVars = requiredVars.filter((varName) => !variables[varName]);
if (missingVars.length > 0) {
throw new Error(`Missing required template variables: ${missingVars.join(', ')}`);
}
}
/**
* Handle content generation errors with detailed logging
*/
handleContentGenerationError(error, contentType, context) {
console.error(`Content generation error for ${contentType} (${context}):`, {
error: error.message,
type: error.constructor.name,
stack: error.stack,
timestamp: new Date().toISOString(),
config: {
model: this.config.ai.model,
temperature: this.config.ai.temperature,
maxTokens: this.config.ai.maxTokens
}
});
// Log to external service if configured
if (this.config.integration.autoSave) {
this.logErrorToExternalService(error, contentType, context);
}
}
/**
* Log error to external service (placeholder for monitoring)
*/
async logErrorToExternalService(error, contentType, context) {
try {
// This would integrate with your error monitoring service
// e.g., Sentry, LogRocket, etc.
console.log('Error logged to external service:', {
error: error.message,
contentType,
context,
timestamp: new Date().toISOString()
});
}
catch (logError) {
console.error('Failed to log error to external service:', logError);
}
}
/**
* Generate content from template (internal implementation)
*/
async generateFromTemplateInternal(template, variables) {
// This is a placeholder implementation
// In a real implementation, you would:
// 1. Replace variables in the template prompt
// 2. Call the appropriate AI service
// 3. Format the response according to the template format
const prompt = this.replaceTemplateVariables(template.prompt, variables);
// For now, return a mock response
return {
id: `template_${Date.now()}`,
type: template.type,
content: `Generated ${template.type} content: ${prompt}`,
timestamp: new Date().toISOString()
};
}
/**
* Replace variables in template prompt
*/
replaceTemplateVariables(prompt, variables) {
let result = prompt;
Object.entries(variables).forEach(([key, value]) => {
result = result.replace(new RegExp(`\\{\\{${key}\\}\\}`, 'g'), value);
});
return result;
}
/**
* Sleep utility for retry delays
*/
sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
}
exports.ContentGeneratorSkill = ContentGeneratorSkill;