# Project export: Autonomous AI Society

This document was generated by HackStack to give an AI agent context about a hackathon project. Sections are labeled with their provenance; content marked as truncated was cut to keep this document small.

## Project metadata

- Hackathon: Cal Hacks 11.0
- Tagline: An autonomous system of AI agents performing an intelligent disaster response from analyzing the distress calls, dispatching drones, finding humans in floods and making calls to the rescue teams.
- Devpost: https://devpost.com/software/autonomous-ai-society
- GitHub: https://github.com/rajashekarcs2023/Autonomous-AI-Society
- Video: https://www.youtube.com/embed/3K3OB0o3TD8?enablejsapi=1&hl=en_US&rel=0&start=&version=3&wmode=transparent
- Result: winner (Fetch.ai: Agentic Track Prize)
- Team: 0 GitHub contributor(s) — 

## Devpost submission (written by the team)

### Inspiration

I got this idea because of the current hurricane Milton causing devastation across Florida. The inspiration behind Autonomous AI Society stems from the need for faster, more efficient, and autonomous systems that can make critical decisions during disaster situations. With multiple sponsors like Fetch.ai, Groq, Deepgram, Hyperbolic, and Vapi providing powerful tools, I envisioned an intelligent system of AI agents capable of handling a disaster response chain—from analyzing distress calls to dispatching drones and contacting rescue teams. The goal was to build an AI-driven solution that can streamline emergency responses, save lives, and minimize risks.

### What it does

Autonomous AI Society is a fully autonomous multi-agent system that performs disaster response tasks in the following workflow: Distress Call Analysis: The system first analyzes distress calls using Deepgram for speech-to-text and Hume AI to score distress levels. Based on the analysis, the agent identifies the most urgent calls and the city. Distress Call Analysis: The system first analyzes distress calls using Deepgram for speech-to-text and Hume AI to score distress levels. Based on the analysis, the agent identifies the most urgent calls and the city. Drone Dispatch: The distress analyzer agent communicates with the drone agent (built using Fetch.ai) to dispatch drones to specific locations, assisting with flood and rescue operations. Drone Dispatch: The distress analyzer agent communicates with the drone agent (built using Fetch.ai) to dispatch drones to specific locations, assisting with flood and rescue operations. Human Detection: Drones capture aerial images, which are analyzed by the human detection agent using Hyperbolic's LLaMA Vision model to detect humans in distress. The agent provides a description and coordinates. Human Detection: Drones capture aerial images, which are analyzed by the human detection agent using Hyperbolic's LLaMA Vision model to detect humans in distress. The agent provides a description and coordinates. Priority-Based Action: The drone results are displayed on a dashboard, ranked based on priority using Groq. Higher priority areas receive faster dispatches, and this is determined dynamically. Priority-Based Action: The drone results are displayed on a dashboard, ranked based on priority using Groq. Higher priority areas receive faster dispatches, and this is determined dynamically. Rescue Call: The final agent, built using Vapi, places an emergency call to the rescue team. It uses instructions generated by Hyperbolic’s text model to give precise directions based on the detected individuals and their location. Rescue Call: The final agent, built using Vapi, places an emergency call to the rescue team. It uses instructions generated by Hyperbolic’s text model to give precise directions based on the detected individuals and their location. How I built it The system consists of five agents, all built using Fetch.ai’s framework, allowing them to interact autonomously and make real-time decisions: Request-sender agent sends the initial requests. Distress analyzer agent uses Hume AI to analyze calls and Groq to generate dramatic messages. Drone agent dispatches drones to designated areas based on the distress score. Human detection agent uses Hyperbolic’s LLaMA Vision to process images and detect humans in danger. Call rescue agent sends audio instructions using Deepgram’s TTS and Vapi for automated phone calls. Challenges I ran into Simulating a drone movement on florida map: The lat_lon_to_pixel function converts latitude and longitude coordinates to pixel positions on the screen. The drone starts at the center of Florida. Its movement is calculated using trigonometry. The angle to the target city is calculated using math.atan2. The drone moves towards the target using sin and cos functions.This allows placing cities and the drone accurately on the map. Simulating a drone movement on florida map: The lat_lon_to_pixel function converts latitude and longitude coordinates to pixel positions on the screen. The drone starts at the center of Florida. Its movement is calculated using trigonometry. The angle to the target city is calculated using math.atan2. The drone moves towards the target using sin and cos functions.This allows placing cities and the drone accurately on the map. Callibrating the map to right coordinates: I had manually experiment with increasing and decreasing the coordinates to fit them at right spots on the florida map. Callibrating the map to right coordinates: I had manually experiment with increasing and decreasing the coordinates to fit them at right spots on the florida map. Coordinating AI agents: Getting agents to communicate effectively while working autonomously was a challenge. Coordinating AI agents: Getting agents to communicate effectively while working autonomously was a challenge. Handling dynamic priorities: Ensuring real-time analysis and updating the priority of drone dispatch based on Groq's risk assessment was tricky. Handling dynamic priorities: Ensuring real-time analysis and updating the priority of drone dispatch based on Groq's risk assessment was tricky. Integration of multiple APIs: Each sponsor's tools had specific nuances, and integrating all of them smoothly, especially with Fetch.ai, required careful handling. Integration of multiple APIs: Each sponsor's tools had specific nuances, and integrating all of them smoothly, especially with Fetch.ai, required careful handling. Accomplishments that I am proud of Successfully built an end-to-end autonomous system where AI agents can make intelligent decisions during a disaster, from distress call analysis to rescue actions. Integrated cutting-edge technologies like Fetch.ai, Groq, Hyperbolic, Deepgram, and Vapi in a single project to create a highly functional and real-time response system. What I learned AI for disaster response: Building systems that leverage multimodal AI agents can significantly improve response times and decision-making in life-critical scenarios. Cross-platform integration: We learned how to seamlessly integrate various tools, from vision AI to TTS to drone dispatch, using Fetch.ai and sponsor technologies. Working with real-time data: Developing an autonomous system that processes data in real-time provided insights into handling complex workflows.

### What's next

Scaling to more disasters: Expanding the system to handle other types of natural disasters like wildfires or earthquakes. Edge deployment: Enabling drones and agents to run on the edge to reduce response times further. Improved human detection: Enhancing human detection with more precise models to handle low-light or difficult visual conditions. Expanded rescue communication: Integrating real-time communication with the victims themselves using Deepgram’s speech technology.

## README (from the GitHub repository)

# project-z

## Detected evidence (automated analysis)

Indexed codebase: 23 recognized source files, 56 KB.
- CSS (language) — detected in the code
- Express (technology) — detected in the code
- HTML (language) — detected in the code
- JavaScript (language) — detected in the code
- Python (language) — detected in the code
- React (technology) — detected in the code
- Node.js (technology) — claimed on Devpost, not found in the code

## Codebase structure (from repository index)

### Files (38 of 38)

```
.DS_Store
.gitignore
Agents/bob_drone_agent.py
Agents/call_rescue_agent.py
Agents/distress_analyzer_agent.py
Agents/human_detection_agent.py
Agents/request_sender.py
Agents/rescue_trigger.py
Agents/trigger_agent.py
descriptions/processed_results.txt
distress_details/distress_call_1.txt
distress_details/distress_call_2.txt
distress_details/distress_call_3.txt
distress_details/distress_call_4.txt
distress_details/distress_call_5.txt
distress_details/final_summary.txt
drone_simulation.py
frontend/.gitignore
frontend/package.json
frontend/public/index.html
frontend/public/manifest.json
frontend/public/robots.txt
frontend/README.md
frontend/server.js
frontend/src/App.css
frontend/src/App.js
frontend/src/App.test.js
frontend/src/components/DistressCalls.css
frontend/src/components/DistressCalls.js
frontend/src/components/DroneUpdates.css
frontend/src/components/DroneUpdates.js
frontend/src/index.css
frontend/src/index.js
frontend/src/reportWebVitals.js
frontend/src/setupTests.js
README.md
valid/.DS_Store
valid/images/.DS_Store
```

### Dependencies

- frontend/package.json: @emotion/react@^11.13.3, @emotion/styled@^11.13.0, @mui/material@^6.1.4, @testing-library/jest-dom@^5.17.0, @testing-library/react@^13.4.0, @testing-library/user-event@^13.5.0, axios@^1.7.7, cors@^2.8.5, express@^4.21.1, react@^18.3.1, react-circular-progressbar@^2.1.0, react-dom@^18.3.1, react-router-dom@^6.27.0, react-scripts@5.0.1, web-vitals@^2.1.4

### Recent commits (newest first)

- all commit
- Initial commit

## Key source files (fetched from GitHub, selected and truncated for size)

### frontend/package.json

```
{
  "name": "autonomous-ai-society",
  "version": "0.1.0",
  "private": true,
  "dependencies": {
    "@emotion/react": "^11.13.3",
    "@emotion/styled": "^11.13.0",
    "@mui/material": "^6.1.4",
    "@testing-library/jest-dom": "^5.17.0",
    "@testing-library/react": "^13.4.0",
    "@testing-library/user-event": "^13.5.0",
    "axios": "^1.7.7",
    "cors": "^2.8.5",
    "express": "^4.21.1",
    "react": "^18.3.1",
    "react-circular-progressbar": "^2.1.0",
    "react-dom": "^18.3.1",
    "react-router-dom": "^6.27.0",
    "react-scripts": "5.0.1",
    "web-vitals": "^2.1.4"
  },
  "scripts": {
    "start": "react-scripts start",
    "build": "react-scripts build",
    "test": "react-scripts test",
    "eject": "react-scripts eject"
  },
  "eslintConfig": {
    "extends": [
      "react-app",
      "react-app/jest"
    ]
  },
  "browserslist": {
    "production": [
      ">0.2%",
      "not dead",
      "not op_mini all"
    ],
    "development": [
      "last 1 chrome version",
      "last 1 firefox version",
      "last 1 safari version"
    ]
  }
}

```

### frontend/server.js

```javascript
const express = require('express');
const cors = require('cors');
const path = require('path');

const app = express();

// Enable CORSdeactiv ate
app.use(cors());

// Serve static files from the distress_details folder
const distressDetailsPath = path.join(__dirname, '../distress_details'); // Adjust the path to distress_details
app.use('/distress_details', express.static(distressDetailsPath));

// Serve static files from the descriptions folder
const descriptionsPath = path.join(__dirname, '../descriptions'); // Adjust the path to descriptions
app.use('/descriptions', express.static(descriptionsPath));

// Start the server
const PORT = process.env.PORT || 4001;
app.listen(PORT, () => {
    console.log(`Server running on port ${PORT}`);
});


```

### frontend/src/index.js

```javascript
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
  <React.StrictMode>
    <App />
  </React.StrictMode>
);

// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();

```

### frontend/src/App.js

```javascript
import React from 'react';
import './App.css';
import { BrowserRouter as Router, Route, Routes } from 'react-router-dom';
import DistressCalls from './components/DistressCalls';
import DroneUpdates from './components/DroneUpdates';  // Import your Drone Updates page

function App() {
  return (
    <Router>
      <div className="App">
        <Routes>
          <Route path="/" element={<DistressCalls />} />
          <Route path="/drone-updates" element={<DroneUpdates />} /> {/* New route for Drone Updates */}
        </Routes>
      </div>
    </Router>
  );
}

export default App;


```

### drone_simulation.py

```python
import pygame
import os
import math
import sys
import asyncio
from pygame import mixer
from deepgram import DeepgramClient, SpeakOptions

DEEPGRAM_API_KEY = "d6474c2ed5a5c94cd35f1e0616e2998a7c9d6d01"

def load_image(name):
    fullname = os.path.join(os.getcwd(), name)
    try:
        image = pygame.image.load(fullname)
    except pygame.error as message:
        print(f'Cannot load image: {name}')
        raise SystemExit(message)
    return image, image.get_rect()

def resize_image(image, max_width, max_height):
    width, height = image.get_size()
    aspect_ratio = width / height
    if width > max_width:
        width = max_width
        height = int(width / aspect_ratio)
    if height > max_height:
        height = max_height
        width = int(height * aspect_ratio)
    return pygame.transform.smoothscale(image, (width, height))

def lat_lon_to_pixel(lat, lon, min_lat, max_lat, min_lon, max_lon, width, height):
    x = (lon - min_lon) / (max_lon - min_lon) * width
    y = height - ((lat - min_lat) / (max_lat - min_lat) * height)
    return int(x), int(y)

def load_drone_image(name, size):
    fullname = os.path.join(os.getcwd(), name)
    try:
        image = pygame.image.load(fullname)
        scale_factor = 1
        new_size = (int(size[0] * scale_factor), int(size[1] * scale_factor))
        return pygame.transform.scale(image, new_size)
    except pygame.error as message:
        print(f'Cannot load image: {name}')
        raise SystemExit(message)

def rotate_drone(image, angle):
    return pygame.transform.rotate(image, math.degrees(angle))

async def generate_audio(text, filename):
    dg_client = DeepgramClient(DEEPGRAM_API_KEY)
    options = SpeakOptions(model="aura-asteria-en")
    speak_options = {"text": text}
    
    try:
        response = dg_client.speak.v("1").save(filename, speak_options, options)
        print(f"Audio saved to {filename}")
    except Exception as e:
        print(f"Error generating audio: {e}")

async def prepare_audio_files(target_city_name):
    await generate_audio(f"Dispatch started for {target_city_name}", "start.mp3")
    await generate_audio(f"Dispatch has reached {target_city_name}. Mission completed", "complete.mp3")

def drone_response_simulation(target_city_name):
    # Run the async function to generate audio files
    asyncio.run(prepare_audio_files(target_city_name))

    pygame.init()
    mixer.init()

    # Load sound effects
    start_sound = mixer.Sound('start.mp3')
    drone_sound = mixer.Sound('drone.wav')
    complete_sound = mixer.Sound('complete.mp3')

    max_width, max_height = 1000, 800
    screen = pygame.display.set_mode((max_width, max_height))
    pygame.display.set_caption("Florida Drone Response Simulation")

    try:
        original_map, _ = load_image('florida.png')
        florida_map = resize_image(original_map, max_width, max_height)
        width, height = florida_map.get_size()
        screen = pygame.display.set_mode((width, height))
    except pygame.error as e:
        return f"Simulation failed: Error loading image: {str(e)}"

    clock = pygame.time.Clock()

    min_lat, max_lat = 24.396308, 31.000888
    min_lon, max_lon = -87.634643, -79.974307

    RED = (255, 0, 0)
    BLACK = (0, 0, 0)

    font = pygame.font.Font(None, 24)

    drone_size = (30, 30)
    drone_image = load_drone_image('drone.jpg', drone_size)

    drone_lat, drone_lon = (max_lat + min_lat) / 2, (max_lon + min_lon) / 2
    drone_speed = 0.005
    drone_angle = 0

    cities = [
        {"name": "Miami", "lat": 25.6614, "lon": -80.7718},
        {"name": "Orlando", "lat": 28.0380, "lon": -81.5592},
        {"name": "Tampa", "lat": 27.4406, "lon": -82.4572},
        {"name": "Jacksonville", "lat": 29.6682, "lon": -81.6557},
        {"name": "Tallahassee", "lat": 29.7780, "lon": -83.7807},
        {"name": "Pensacola", "lat": 29.8810, "lon": -86.2169}
    ]

    target_city = next((c for c in cities if c["name"].lower() == target_city_name.lower()), None)
    if not target_city:
        pygame.quit()
        return f"City '{target_city_name}' not found. Simulation aborted."

    mission_active = True
    print(f"Distress signal received from {target_city['name']}!")

    # Play start sound
    start_sound.play()
    pygame.time.wait(int(start_sound.get_length() * 1000))  # Wait for the sound to finish

    # Start looping drone sound
    drone_channel = drone_sound.play(-1)  # -1 means loop indefinitely

    running = True
    frame_count = 0
    while running:
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                running = False

        screen.blit(florida_map, (0, 0))

        for city in cities:
            x, y = lat_lon_to_pixel(city["lat"], city["lon"], min_lat, max_lat, min_lon, max_lon, width, height)
            pygame.draw.circle(screen, RED, (x, y), 5)
            
            if city["name"] == target_city["name"]:
                # Blink the target city name and add "High distress" message
                if frame_count % 60 < 30:  # Blink every half second
                    text_color = RED
                    city_text = f"{city['name']} [High distress]"
                else:
                    text_color = BLACK
                    city_text = city["name"]
            else:
                text_color = BLACK
                city_text = city["name"]
            
            text = font.render(city_text, True, text_color)
            text_rect = text.get_rect(center=(x, y - 15))
            screen.blit(text, text_rect)

        if mission_active:
            drone_angle = math.atan2(target_city["lat"] - drone_lat, target_city["lon"] - drone_lon)
            
            drone_lat += drone_speed * math.sin(drone_angle)
            drone_lon += drone_speed * math.cos(drone_angle)

            distance = math.sqrt((target_city["lat"] - drone_lat)**2 + (target_city["lon"] - drone_lon)**2)
            if distance < 0.05:
                mission_active = False
              
[truncated — 1154 more characters]
```

### Agents/trigger_agent.py

```python
from uagents import Agent, Context, Model

# Initialize the trigger agent
trigger_agent = Agent(
    name="trigger_agent",
    port=8006,
    seed="trigger_agent_secret_seed",
    endpoint=["http://127.0.0.1:8006/submit"],
)

class HumanDetectionRequest(Model):
    folder_path: str  # Path to the folder containing images

@trigger_agent.on_event("startup")
async def startup_event(ctx: Context):
    ctx.logger.info(f"Trigger Agent started at address {ctx.address}")

    # Address of the human detection agent
    human_detection_agent_address = "agent1qdykgdgrrm5en7tzqv0v6cjy7s0ujunjlp5qwgh93qu70mfx70pxkwruza9"
    
    # Send a request to the human detection agent to start processing
    await ctx.send(
        human_detection_agent_address,
        HumanDetectionRequest(folder_path="valid/images")
    )

    # Log that the request was sent, without expecting a response
    ctx.logger.info("Request sent to Human Detection Agent to process images.")

if __name__ == "__main__":
    trigger_agent.run()

```

### Agents/request_sender.py

```python
from uagents import Agent, Context, Model

# Define the distress analysis request model
class DistressAnalysisRequest(Model):
    duration: int

# Create the request sender agent
request_sender = Agent(
    name="request_sender",
    port=8001,
    seed="request_sender_secret_seed",
    endpoint=["http://127.0.0.1:8001/submit"],
)

# Address of the distress analyzer agent (ensure this is correct)
DISTRESS_ANALYZER_ADDRESS = "agent1qg6s5cjun8kz0hz3jaslly0f3nlvmytvn6dmdc8xr2p7mjdpktpsw5ugy3g"

# Log the agent's address when it starts
@request_sender.on_event("startup")
async def introduce(ctx: Context):
    ctx.logger.info(f"Request Sender's address: {ctx.address}")
    # Sending a single distress analysis request upon startup (you can trigger it as per your workflow)
    await send_distress_analysis_request(ctx)

# Function to send a distress analysis request to the distress analyzer
async def send_distress_analysis_request(ctx: Context):
    ctx.logger.info(f"Sending distress analysis request to {DISTRESS_ANALYZER_ADDRESS}")
    request = DistressAnalysisRequest(duration=5)  # Example: request for 5 seconds of audio analysis
    await ctx.send(DISTRESS_ANALYZER_ADDRESS, request)
    ctx.logger.info("Distress analysis request sent")

if __name__ == "__main__":
    request_sender.run()

```

### Agents/rescue_trigger.py

```python
from uagents import Agent, Context, Model

# Define the agent that will trigger the call_rescue_agent
trigger_agent = Agent(
    name="trigger_agent",
    port=8007,
    seed="trigger_secret_seed",
    endpoint=["http://127.0.0.1:8007/submit"],
)

# Define the model for the message that will be sent to call_rescue_agent
class CallRescueRequest(Model):
    message: str  # The full message containing rescue details

# Address of the call_rescue_agent
CALL_RESCUE_AGENT_ADDRESS = "agent1qv306rfgyvyhpyfdwavl625lh4383kchky39rkqdcz0s7s7t93yjglrjxrr"

@trigger_agent.on_event("startup")
async def introduce(ctx: Context):
    ctx.logger.info(f"Trigger Agent is starting. Address: {ctx.address}")
    print(f"Trigger Agent Address: {ctx.address}")

    # Compose the message that will be sent to the call_rescue_agent
    rescue_message = """
    There is a child trapped on the roof of a house, surrounded by floodwaters. The rescue team needs to reach coordinates (latitude: 25.50798, longitude: -84.19412).
    The water level is rising, and the child is in immediate danger.
    """

    # Send the message to the call_rescue_agent
    call_rescue_request = CallRescueRequest(message=rescue_message)
    await ctx.send(CALL_RESCUE_AGENT_ADDRESS, call_rescue_request)

if __name__ == "__main__":
    trigger_agent.run()

```

### Agents/bob_drone_agent.py

```python
import asyncio
from uagents import Agent, Context, Model
from uagents.setup import fund_agent_if_low
import subprocess
import sys

class DroneDispatchRequest(Model):
    target_city: str

class HumanDetectionRequest(Model):
    folder_path: str  # Path to the folder containing images

bob = Agent(
    name="bob",
    port=8003,
    seed="bob's unique seed phrase",
    endpoint=["http://127.0.0.1:8003/submit"],
)

# Define the human detection agent address
HUMAN_DETECTION_AGENT_ADDRESS = "agent1qdykgdgrrm5en7tzqv0v6cjy7s0ujunjlp5qwgh93qu70mfx70pxkwruza9"  # Replace with the actual address of the human detection agent

@bob.on_event("startup")
async def introduce(ctx: Context):
    ctx.logger.info(f"Bob's address: {ctx.address}")
    await fund_agent_if_low(ctx.wallet.address())

@bob.on_message(model=DroneDispatchRequest)
async def handle_dispatch_request(ctx: Context, sender: str, msg: DroneDispatchRequest):
    ctx.logger.info(f"Received dispatch request from {sender} for location: {msg.target_city}")
    
    dispatch_message = f"Drone has been dispatched to {msg.target_city}"
    ctx.logger.info(dispatch_message)
    
    # Trigger the human detection agent after dispatch
    ctx.logger.info(f"Triggering human detection agent for image analysis in the folder.")
    
    # You can change the folder path to where your images are located
    human_detection_request = HumanDetectionRequest(folder_path="valid/images")
    await ctx.send(HUMAN_DETECTION_AGENT_ADDRESS, human_detection_request)
    
    # Run the drone dispatch simulation in a separate process
    process = subprocess.Popen([sys.executable, "drone_simulation.py", msg.target_city], 
                               stdout=subprocess.PIPE, 
                               stderr=subprocess.PIPE,
                               text=True)
    
    # Wait for the process to complete
    stdout, stderr = process.communicate()
    
    if process.returncode == 0:
        simulation_result = stdout.strip()
        ctx.logger.info(f"Drone dispatched successfully to {msg.target_city}. Simulation Result: {simulation_result}")
    else:
        error_message = f"Simulation failed: {stderr}"
        ctx.logger.error(error_message)

if __name__ == "__main__":
    bob.run()

```

### Agents/call_rescue_agent.py

```python
import requests
from uagents import Agent, Context, Model

# Define the call rescue agent
call_rescue_agent = Agent(
    name="call_rescue_agent",
    port=8009,
    seed="call_rescue_secret_seed",
    endpoint=["http://127.0.0.1:8009/submit"],
)

# Define the model for call rescue request
class CallRescueRequest(Model):
    message: str  # The full message containing rescue details

# Function to call Hyperbolic LLaMA API
def call_llama_vision_for_summary(message):
    url = "https://api.hyperbolic.xyz/v1/chat/completions"
    headers = {
        "Content-Type": "application/json",
        "Authorization": ""  # Add your API key here
    }
    
    data = {
        "messages": [
            {
                "role": "system",
                "content": "You are an assistant to disaster rescue team. From the message given, make a concise summary of the details and surroundings of the person mentioned in the message and also their coordinates.Don't begin with 'The summary is..', just directly gibve the message."
            },
            {
                "role": "user",
                "content": message
            }
        ],
        "model": "meta-llama/Llama-3.2-3B-Instruct",
        "max_tokens": 512,
        "temperature": 0.7,
        "top_p": 0.9
    }

    response = requests.post(url, headers=headers, json=data)
    if response.status_code == 200:
        return response.json().get('choices', [{}])[0].get('message', {}).get('content', 'No summary available')
    else:
        return "Error: Failed to summarize the message"

# Function to create the Vapi call
def create_vapi_call(first_message):
    # Hardcoded Vapi API Authorization token and customer number
    auth_token = ''
    
    # The Phone Number ID, and the Customer details for the call
    phone_number_id = ''
    customer_number = ""

    # Create the header with Authorization token
    headers = {
        'Authorization': f'Bearer {auth_token}',
        'Content-Type': 'application/json',
    }

    # Create the data payload for the API request
    data = {
        'assistant': {
            "firstMessage": first_message,
            "model": {
                "provider": "openai",
                "model": "gpt-3.5-turbo",
                "messages": [
                    {
                        "role": "system",
                        "content": "You are a disaster response assistant and will give instructions to the rescue team by providing the details of the people to rescue and the coordinates."
                    }
                ]
            },
            "voice": "jennifer-playht"
        },
        'phoneNumberId': phone_number_id,
        'customer': {
            'number': customer_number,
        },
    }

    # Make the POST request to Vapi to create the phone call
    response = requests.post(
        'https://api.vapi.ai/call/phone', headers=headers, json=data)

    # Check if the request was successful and return the response
    if response.status_code == 201:
        return f"Call created successfully: {response.json()}"
    else:
        return f"Failed to create call: {response.text}"

# Handle rescue call requests
@call_rescue_agent.on_message(model=CallRescueRequest)
async def handle_call_rescue_request(ctx: Context, sender: str, msg: CallRescueRequest):
    # Step 1: Send the message to Hyperbolic's LLaMA API for summarization
    ctx.logger.info("Sending message to Hyperbolic LLaMA for summarization...")
    summary = call_llama_vision_for_summary(msg.message)

    ctx.logger.info(f"Received summary from LLaMA: {summary}")
    
    # Step 2: Send the summarized message to VAPI to create the phone call
    response = create_vapi_call(summary)

    # Log the result
    ctx.logger.info(response)

# On startup, log and print the agent's address
@call_rescue_agent.on_event("startup")
async def introduce(ctx: Context):
    ctx.logger.info(f"Call Rescue Agent is starting. Address: {ctx.address}")
    print(f"Call Rescue Agent Address: {ctx.address}")

if __name__ == "__main__":
    call_rescue_agent.run()

```

[12 more indexed source files omitted to keep this export small. The full file list is in the Codebase structure section above.]