You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
swarms/docs/swarms_cloud/cloudflare_workers.md

9.5 KiB

Cloudflare Workers with Swarms: Production AI Agents

Deploy AI agents on Cloudflare's edge network with automatic cron scheduling and real-time data integration. This guide shows a production-ready implementation with both HTTP endpoints and scheduled triggers.

Architecture Overview

The Cloudflare Workers pattern uses two main handlers:

  • fetch(): HTTP requests for testing and manual triggers
  • scheduled(): Cron jobs for automated execution
export default {
  // HTTP handler for manual testing
  async fetch(request, env, ctx) {
    // Handle web interface and API endpoints
  },
  
  // Cron handler for scheduled execution
  async scheduled(event, env, ctx) {
    ctx.waitUntil(handleStockAnalysis(event, env));
  }
};

Quick Setup

1. Create Worker Project

npm create cloudflare@latest stock-agent
cd stock-agent

2. Configure Cron Schedule

Edit wrangler.jsonc:

{
  "$schema": "node_modules/wrangler/config-schema.json",
  "name": "stock-agent",
  "main": "src/index.js",
  "compatibility_date": "2025-08-03",
  "observability": {
    "enabled": true
  },
  "triggers": {
    "crons": [
      "0 */3 * * *"  // Every 3 hours
    ]
  },
  "vars": {
    "SWARMS_API_KEY": "your-api-key"
  }
}

Complete Minimal Implementation

Create src/index.js:

export default {
  // HTTP handler - provides web interface and manual triggers
  async fetch(request, env, ctx) {
    const url = new URL(request.url);
    
    // Web interface for testing
    if (url.pathname === '/') {
      return new Response(`
        <!DOCTYPE html>
        <html>
          <head>
            <title>Stock Analysis Agent</title>
            <style>
              body { font-family: Arial, sans-serif; max-width: 800px; margin: 50px auto; padding: 20px; }
              .btn { padding: 10px 20px; background: #007bff; color: white; border: none; border-radius: 5px; cursor: pointer; }
              .status { background: #f0f8f0; padding: 10px; border-left: 3px solid #28a745; margin: 20px 0; }
              .result { background: #f8f9fa; padding: 15px; border-radius: 5px; margin: 20px 0; white-space: pre-wrap; }
            </style>
          </head>
          <body>
            <h1>📈 Stock Analysis Agent</h1>
            <div class="status">Status: Online ✅</div>
            
            <button class="btn" onclick="triggerAnalysis()">🔥 Start Analysis</button>
            <div id="result"></div>
            
            <script>
              async function triggerAnalysis() {
                document.getElementById('result').innerHTML = 'Running analysis...';
                try {
                  const response = await fetch('/trigger');
                  const data = await response.json();
                  document.getElementById('result').innerHTML = 
                    data.result?.success ? 
                    \`✅ Success\\n\\nAnalysis: \\${data.result.analysis}\` :
                    \`❌ Error: \\${data.result?.error || data.error}\`;
                } catch (error) {
                  document.getElementById('result').innerHTML = \`❌ Failed: \\${error.message}\`;
                }
              }
            </script>
          </body>
        </html>
      `, {
        headers: { 'Content-Type': 'text/html' }
      });
    }
    
    // Manual trigger endpoint
    if (url.pathname === '/trigger') {
      try {
        const result = await handleStockAnalysis(null, env);
        return new Response(JSON.stringify({ 
          message: 'Analysis triggered',
          timestamp: new Date().toISOString(),
          result 
        }), {
          headers: { 'Content-Type': 'application/json' }
        });
      } catch (error) {
        return new Response(JSON.stringify({ 
          error: error.message 
        }), {
          status: 500,
          headers: { 'Content-Type': 'application/json' }
        });
      }
    }
    
    return new Response('Not Found', { status: 404 });
  },

  // Cron handler - runs automatically on schedule
  async scheduled(event, env, ctx) {
    ctx.waitUntil(handleStockAnalysis(event, env));
  }
};

// Main analysis function used by both HTTP and cron triggers
async function handleStockAnalysis(event, env) {
  console.log('🚀 Starting stock analysis...');
  
  try {
    // Step 1: Fetch real market data (using Yahoo Finance - no API key needed)
    const marketData = await fetchMarketData();
    
    // Check if we got valid data
    const validSymbols = Object.keys(marketData).filter(symbol => !marketData[symbol].error);
    if (validSymbols.length === 0) {
      throw new Error('No valid market data retrieved');
    }
    
    // Step 2: Send to Swarms AI agents
    const swarmConfig = {
      name: "Stock Analysis",
      description: "Real-time market analysis",
      agents: [
        {
          agent_name: "Technical Analyst",
          system_prompt: \`Analyze the provided stock data:
            - Identify trends and key levels
            - Provide trading signals
            - Calculate technical indicators
            Format analysis professionally.\`,
          model_name: "gpt-4o-mini",
          max_tokens: 1500,
          temperature: 0.2
        }
      ],
      swarm_type: "SequentialWorkflow",
      task: \`Analyze this market data: \\${JSON.stringify(marketData, null, 2)}\`,
      max_loops: 1
    };

    const response = await fetch('https://api.swarms.world/v1/swarm/completions', {
      method: 'POST',
      headers: {
        'x-api-key': env.SWARMS_API_KEY,
        'Content-Type': 'application/json'
      },
      body: JSON.stringify(swarmConfig)
    });

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

    const result = await response.json();
    console.log('✅ Analysis completed');

    return {
      success: true,
      analysis: result.output,
      symbolsAnalyzed: validSymbols.length,
      cost: result.usage?.billing_info?.total_cost
    };

  } catch (error) {
    console.error('❌ Analysis failed:', error.message);
    return {
      success: false,
      error: error.message
    };
  }
}

// Fetch market data from Yahoo Finance (free, no API key required)
async function fetchMarketData() {
  const symbols = ['SPY', 'AAPL', 'MSFT', 'TSLA'];
  const marketData = {};

  const promises = symbols.map(async (symbol) => {
    try {
      const controller = new AbortController();
      const timeout = setTimeout(() => controller.abort(), 8000);
      
      const response = await fetch(
        \`https://query1.finance.yahoo.com/v8/finance/chart/\\${symbol}\`,
        { 
          signal: controller.signal,
          headers: { 'User-Agent': 'Mozilla/5.0' }
        }
      );
      clearTimeout(timeout);
      
      if (!response.ok) throw new Error(\`HTTP \\${response.status}\`);
      
      const data = await response.json();
      const result = data.chart.result[0];
      const meta = result.meta;
      
      const currentPrice = meta.regularMarketPrice;
      const previousClose = meta.previousClose;
      const change = currentPrice - previousClose;
      const changePercent = ((change / previousClose) * 100).toFixed(2);

      return [symbol, {
        price: currentPrice,
        change: change,
        change_percent: changePercent,
        volume: meta.regularMarketVolume,
        currency: meta.currency
      }];

    } catch (error) {
      return [symbol, { error: error.message }];
    }
  });

  const results = await Promise.allSettled(promises);
  results.forEach((result) => {
    if (result.status === 'fulfilled' && result.value) {
      const [symbol, data] = result.value;
      marketData[symbol] = data;
    }
  });

  return marketData;
}

Key Features Explained

1. Dual Handler Pattern

  • fetch(): Handles HTTP requests, provides web UI for testing
  • scheduled(): Executes on cron schedule automatically
  • Shared Logic: Both use the same handleStockAnalysis() function

2. Cron Configuration

"triggers": {
  "crons": [
    "0 */3 * * *"    // Every 3 hours
    "0 9 * * MON-FRI" // Weekdays at 9 AM
  ]
}

3. Real Data Integration

  • Yahoo Finance API: Free, no API key required
  • Error Handling: Timeout management, fallback responses
  • Parallel Processing: Fetch multiple symbols simultaneously

4. Production Features

  • Web Interface: Test manually via browser
  • Structured Responses: Consistent JSON format
  • Error Recovery: Graceful failure handling
  • Logging: Console output for debugging

Deployment

# Deploy to Cloudflare
wrangler deploy

# View logs
wrangler tail

# Test cron manually
wrangler triggers cron "0 */3 * * *"

Environment Variables

Add to wrangler.jsonc:

{
  "vars": {
    "SWARMS_API_KEY": "your-swarms-api-key",
    "MAILGUN_API_KEY": "optional-for-emails", 
    "MAILGUN_DOMAIN": "your-domain.com",
    "RECIPIENT_EMAIL": "alerts@company.com"
  }
}

Testing

  1. Deploy: wrangler deploy
  2. Visit URL: Open your worker URL to see the web interface
  3. Manual Test: Click "Start Analysis" button
  4. Cron Test: wrangler triggers cron "0 */3 * * *"

Production Tips

  • Error Handling: Always wrap API calls in try-catch
  • Timeouts: Use AbortController for external API calls
  • Logging: Use console.log for debugging in Cloudflare dashboard
  • Rate Limits: Yahoo Finance is free but has rate limits
  • Cost Control: Set appropriate max_tokens in agent config

This minimal implementation provides a solid foundation for production AI agents on Cloudflare Workers with automated scheduling and real-time data integration.