@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
294 lines (293 loc) • 9.76 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.trackMonetization = exports.trackPerformance = exports.trackError = exports.trackApiCall = exports.trackSkillUsage = exports.trackEvent = exports.AnalyticsService = void 0;
const apiClient_1 = require("./apiClient");
class AnalyticsService {
constructor(config) {
this.eventQueue = [];
this.processingQueue = false;
this.rateLimitMap = new Map();
this.config = {
batchSize: 10,
flushInterval: 30000, // 30 seconds
rateLimit: 10, // 10 events per minute
...config,
enabled: config.enabled !== false // Default to true unless explicitly disabled
};
// Initialize client with default values (will be overridden if endpoint is provided)
this.client = new apiClient_1.AgentBridgeClient('https://developers.agentdao.com', config.apiKey);
// Auto-flush events periodically
if (this.config.enabled) {
setInterval(() => this.flushQueue(), this.config.flushInterval);
}
}
static getInstance(config) {
if (!AnalyticsService.instance) {
AnalyticsService.instance = new AnalyticsService(config || { enabled: true });
}
return AnalyticsService.instance;
}
/**
* Track an important event
*/
async trackEvent(event) {
if (!this.config.enabled)
return;
// Only track important events to prevent bloat
if (!AnalyticsService.IMPORTANT_EVENTS.has(event.event_name)) {
return;
}
// Rate limiting
const userKey = event.user_id || event.agent_id || 'anonymous';
if (this.isRateLimited(userKey)) {
return;
}
// Add timestamp if not provided
if (!event.timestamp) {
event.timestamp = new Date().toISOString();
}
// Add to queue for batch processing
this.eventQueue.push(event);
// Flush immediately if queue is getting large
if (this.eventQueue.length >= this.config.batchSize) {
this.flushQueue();
}
}
/**
* Track skill usage
*/
async trackSkillUsage(skillName, agentId, metadata) {
await this.trackEvent({
event_type: 'skill_usage',
event_name: 'skill_used',
skill_name: skillName,
agent_id: agentId,
metadata: {
skill_name: skillName,
...metadata
}
});
}
/**
* Track API calls
*/
async trackApiCall(endpoint, success, duration, metadata) {
await this.trackEvent({
event_type: 'api_call',
event_name: 'api_request',
metadata: {
endpoint,
success,
duration,
...metadata
}
});
}
/**
* Track errors
*/
async trackError(error, context, agentId, metadata) {
await this.trackEvent({
event_type: 'error',
event_name: 'error_occurred',
agent_id: agentId,
metadata: {
error_message: error.message,
error_stack: error.stack,
context,
...metadata
}
});
}
/**
* Track performance metrics
*/
async trackPerformance(metric, value, unit, metadata) {
await this.trackEvent({
event_type: 'performance',
event_name: 'performance_metric',
metadata: {
metric,
value,
unit,
...metadata
}
});
}
/**
* Track monetization events
*/
async trackMonetization(event, amount, currency, metadata) {
await this.trackEvent({
event_type: 'monetization',
event_name: event,
metadata: {
amount,
currency,
...metadata
}
});
}
/**
* Check rate limiting
*/
isRateLimited(userKey) {
const now = Date.now();
const userLimit = this.rateLimitMap.get(userKey);
if (!userLimit || now > userLimit.resetTime) {
this.rateLimitMap.set(userKey, {
count: 1,
resetTime: now + 60000 // 1 minute
});
return false;
}
if (userLimit.count >= this.config.rateLimit) {
return true;
}
userLimit.count++;
return false;
}
/**
* Batch process queued events
*/
async flushQueue() {
if (this.processingQueue || this.eventQueue.length === 0) {
return;
}
this.processingQueue = true;
const eventsToProcess = this.eventQueue.splice(0, this.config.batchSize);
try {
// Send events to AgentDAO platform
if (this.config.endpoint) {
await this.sendToEndpoint(eventsToProcess);
}
else {
// Use AgentBridge client to send to platform
await this.sendViaAgentBridge(eventsToProcess);
}
}
catch (error) {
console.warn('Failed to flush analytics queue:', error);
// Put events back in queue for retry (but limit retries)
if (this.eventQueue.length < 100) {
this.eventQueue.unshift(...eventsToProcess);
}
}
finally {
this.processingQueue = false;
}
}
/**
* Send events to custom endpoint
*/
async sendToEndpoint(events) {
if (!this.config.endpoint)
return;
const response = await fetch(this.config.endpoint, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
...(this.config.apiKey && { 'Authorization': `Bearer ${this.config.apiKey}` })
},
body: JSON.stringify({ events })
});
if (!response.ok) {
throw new Error(`Analytics endpoint failed: ${response.status}`);
}
}
/**
* Send events via AgentBridge
*/
async sendViaAgentBridge(events) {
try {
// Send events to AgentDAO platform via AgentBridge
const response = await fetch(`${this.client['baseUrl']}/api/analytics/batch`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
...(this.client['token'] && { 'Authorization': `Bearer ${this.client['token']}` })
},
body: JSON.stringify({ events })
});
if (!response.ok) {
throw new Error(`Analytics endpoint failed: ${response.status}`);
}
}
catch (error) {
// Fallback to console logging in development
if (process.env.NODE_ENV === 'development') {
console.log('Analytics events (development):', events);
}
throw error;
}
}
/**
* Get analytics summary
*/
async getAnalyticsSummary(timeRange = '30d') {
try {
const response = await fetch(`${this.client['baseUrl']}/api/analytics/summary?timeRange=${timeRange}`, {
headers: {
...(this.client['token'] && { 'Authorization': `Bearer ${this.client['token']}` })
}
});
if (!response.ok) {
throw new Error(`Analytics summary failed: ${response.status}`);
}
return await response.json();
}
catch (error) {
console.warn('Failed to get analytics summary:', error);
return null;
}
}
/**
* Enable/disable analytics
*/
setEnabled(enabled) {
this.config.enabled = enabled;
}
/**
* Update configuration
*/
updateConfig(newConfig) {
this.config = { ...this.config, ...newConfig };
}
/**
* Get current queue size
*/
getQueueSize() {
return this.eventQueue.length;
}
/**
* Force flush queue
*/
async forceFlush() {
await this.flushQueue();
}
}
exports.AnalyticsService = AnalyticsService;
// Important events that should be tracked
AnalyticsService.IMPORTANT_EVENTS = new Set([
'agent_created',
'agent_deployed',
'skill_used',
'api_call',
'error_occurred',
'user_action',
'performance_metric',
'monetization_event'
]);
// Export convenience functions
const trackEvent = (event) => AnalyticsService.getInstance().trackEvent(event);
exports.trackEvent = trackEvent;
const trackSkillUsage = (skillName, agentId, metadata) => AnalyticsService.getInstance().trackSkillUsage(skillName, agentId, metadata);
exports.trackSkillUsage = trackSkillUsage;
const trackApiCall = (endpoint, success, duration, metadata) => AnalyticsService.getInstance().trackApiCall(endpoint, success, duration, metadata);
exports.trackApiCall = trackApiCall;
const trackError = (error, context, agentId, metadata) => AnalyticsService.getInstance().trackError(error, context, agentId, metadata);
exports.trackError = trackError;
const trackPerformance = (metric, value, unit, metadata) => AnalyticsService.getInstance().trackPerformance(metric, value, unit, metadata);
exports.trackPerformance = trackPerformance;
const trackMonetization = (event, amount, currency, metadata) => AnalyticsService.getInstance().trackMonetization(event, amount, currency, metadata);
exports.trackMonetization = trackMonetization;