Dexter Edep commited on
Commit
0fb5f65
·
1 Parent(s): 38e0dee

Implement risk assessment agent

Browse files
risk-assessment-agent/.blaxel/philippines-disaster-risk-mcp.yaml CHANGED
@@ -1,10 +1,47 @@
1
  # Philippines Disaster Risk MCP Server Configuration
2
- # MCP Server URL: https://mcp-1st-birthday-philippines-disaster-risk-mcp.hf.space/gradio_api/mcp/
3
 
4
- mcp_server:
5
- name: philippines-disaster-risk-mcp
6
- url: https://mcp-1st-birthday-philippines-disaster-risk-mcp.hf.space/gradio_api/mcp/
 
 
7
  type: gradio
8
- tools:
9
- - philippines_disaster_risk_mcp_get_risk_data
10
- description: "Provides disaster risk assessment data for Philippine coordinates"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  # Philippines Disaster Risk MCP Server Configuration
2
+ # This MCP server provides disaster risk assessment data for Philippine coordinates
3
 
4
+ name: philippines-disaster-risk-mcp
5
+ description: Gradio-based MCP server for Philippines disaster risk data
6
+
7
+ # MCP Server Connection
8
+ server:
9
  type: gradio
10
+ url: https://mcp-1st-birthday-philippines-disaster-risk-mcp.hf.space/gradio_api/mcp/
11
+
12
+ # Available Tools
13
+ tools:
14
+ - name: philippines_disaster_risk_mcp_get_risk_data
15
+ description: Get comprehensive disaster risk data for Philippine coordinates
16
+ parameters:
17
+ latitude:
18
+ type: number
19
+ description: Latitude in decimal degrees (4°N to 21°N)
20
+ required: true
21
+ minimum: 4.0
22
+ maximum: 21.0
23
+ longitude:
24
+ type: number
25
+ description: Longitude in decimal degrees (116°E to 127°E)
26
+ required: true
27
+ minimum: 116.0
28
+ maximum: 127.0
29
+ returns:
30
+ type: object
31
+ description: Comprehensive disaster risk assessment data including seismic, volcanic, and hydrometeorological hazards
32
+
33
+ # Cache Configuration
34
+ cache:
35
+ enabled: true
36
+ ttl: 3600 # 1 hour cache as per MCP documentation
37
+
38
+ # Retry Configuration
39
+ retry:
40
+ max_attempts: 3
41
+ base_delay: 1.0 # seconds
42
+ exponential_backoff: true
43
+
44
+ # Timeout Configuration
45
+ timeout:
46
+ connection: 30 # seconds
47
+ read: 60 # seconds
risk-assessment-agent/agent.py CHANGED
@@ -1,57 +1,531 @@
1
  """
2
  Risk Assessment Agent for Disaster Risk Construction Planner
3
- Interfaces with Philippines Disaster Risk MCP Server
 
 
4
  """
5
 
6
  import asyncio
7
- from typing import Optional
8
- from models import RiskData, RiskLevel
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
9
 
10
 
11
  class RiskAssessmentAgent:
12
- """Agent for disaster risk assessment"""
 
 
 
 
 
 
13
 
14
  def __init__(self):
15
- """Initialize risk assessment agent"""
16
- pass
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
17
 
18
  async def get_risk_assessment(self, lat: float, lon: float) -> RiskData:
19
  """
20
  Main entry point for risk assessment
21
 
22
  Args:
23
- lat: Latitude coordinate
24
- lon: Longitude coordinate
25
 
26
  Returns:
27
- Complete risk assessment data
 
 
 
 
28
  """
29
  # Validate coordinates
30
- self.validate_coordinates(lat, lon)
 
 
 
 
 
31
 
32
- # TODO: Implement MCP connection and data retrieval
33
- raise NotImplementedError("Risk assessment not yet implemented")
 
 
 
 
 
 
 
 
 
34
 
35
- def validate_coordinates(self, lat: float, lon: float) -> bool:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
36
  """
37
- Validate coordinates are within Philippine bounds
 
 
38
 
39
  Args:
40
  lat: Latitude
41
  lon: Longitude
42
 
43
  Returns:
44
- True if valid
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
45
 
46
- Raises:
47
- ValueError: If coordinates are invalid
48
  """
49
- if not (4.0 <= lat <= 21.0):
50
- raise ValueError(
51
- f"Latitude {lat} is outside Philippine bounds (4°N-21°N)"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
52
  )
53
- if not (116.0 <= lon <= 127.0):
54
- raise ValueError(
55
- f"Longitude {lon} is outside Philippine bounds (116°E-127°E)"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
56
  )
57
- return True
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  """
2
  Risk Assessment Agent for Disaster Risk Construction Planner
3
+
4
+ This agent interfaces with the Philippines Disaster Risk MCP Server to retrieve
5
+ disaster risk data for Philippine locations.
6
  """
7
 
8
  import asyncio
9
+ import os
10
+ from typing import Optional, Dict, Any
11
+ from datetime import datetime
12
+
13
+ from models import (
14
+ RiskData,
15
+ RiskSummary,
16
+ HazardData,
17
+ SeismicHazards,
18
+ VolcanicHazards,
19
+ HydroHazards,
20
+ HazardDetail,
21
+ LocationInfo,
22
+ FacilityInfo,
23
+ Metadata,
24
+ Coordinates,
25
+ RiskLevel,
26
+ ErrorResponse,
27
+ ErrorDetail
28
+ )
29
 
30
 
31
  class RiskAssessmentAgent:
32
+ """Agent for disaster risk assessment using Philippines Disaster Risk MCP"""
33
+
34
+ # Philippine geographic boundaries
35
+ PHILIPPINES_LAT_MIN = 4.0
36
+ PHILIPPINES_LAT_MAX = 21.0
37
+ PHILIPPINES_LON_MIN = 116.0
38
+ PHILIPPINES_LON_MAX = 127.0
39
 
40
  def __init__(self):
41
+ """Initialize the Risk Assessment Agent"""
42
+ self.mcp_url = os.getenv(
43
+ 'PHILIPPINES_DISASTER_RISK_MCP_URL',
44
+ 'https://mcp-1st-birthday-philippines-disaster-risk-mcp.hf.space/gradio_api/mcp/'
45
+ )
46
+
47
+ def validate_coordinates(self, lat: float, lon: float) -> bool:
48
+ """
49
+ Validate coordinates are within Philippine geographic boundaries
50
+
51
+ Args:
52
+ lat: Latitude in decimal degrees
53
+ lon: Longitude in decimal degrees
54
+
55
+ Returns:
56
+ True if coordinates are within Philippine bounds, False otherwise
57
+ """
58
+ return (
59
+ self.PHILIPPINES_LAT_MIN <= lat <= self.PHILIPPINES_LAT_MAX and
60
+ self.PHILIPPINES_LON_MIN <= lon <= self.PHILIPPINES_LON_MAX
61
+ )
62
 
63
  async def get_risk_assessment(self, lat: float, lon: float) -> RiskData:
64
  """
65
  Main entry point for risk assessment
66
 
67
  Args:
68
+ lat: Latitude in decimal degrees
69
+ lon: Longitude in decimal degrees
70
 
71
  Returns:
72
+ RiskData object containing complete risk assessment
73
+
74
+ Raises:
75
+ ValueError: If coordinates are invalid
76
+ Exception: If MCP connection fails after retries
77
  """
78
  # Validate coordinates
79
+ if not self.validate_coordinates(lat, lon):
80
+ raise ValueError(
81
+ f"Coordinates ({lat}, {lon}) are outside Philippine boundaries. "
82
+ f"Valid ranges: Latitude {self.PHILIPPINES_LAT_MIN}°N to {self.PHILIPPINES_LAT_MAX}°N, "
83
+ f"Longitude {self.PHILIPPINES_LON_MIN}°E to {self.PHILIPPINES_LON_MAX}°E"
84
+ )
85
 
86
+ # Call MCP with retry logic
87
+ try:
88
+ mcp_response = await self.call_with_retry(lat, lon)
89
+
90
+ # Parse the response
91
+ risk_data = self._parse_mcp_response(mcp_response, lat, lon)
92
+
93
+ return risk_data
94
+
95
+ except Exception as e:
96
+ raise Exception(f"Failed to retrieve risk assessment: {str(e)}")
97
 
98
+ async def call_with_retry(
99
+ self,
100
+ lat: float,
101
+ lon: float,
102
+ max_attempts: int = 3,
103
+ base_delay: float = 1.0
104
+ ) -> Dict[str, Any]:
105
+ """
106
+ Call MCP with retry logic and exponential backoff
107
+
108
+ Args:
109
+ lat: Latitude in decimal degrees
110
+ lon: Longitude in decimal degrees
111
+ max_attempts: Maximum number of retry attempts (default: 3)
112
+ base_delay: Base delay in seconds for exponential backoff (default: 1.0)
113
+
114
+ Returns:
115
+ MCP response data
116
+
117
+ Raises:
118
+ Exception: If all retry attempts fail
119
+ """
120
+ last_error: Optional[Exception] = None
121
+
122
+ for attempt in range(1, max_attempts + 1):
123
+ try:
124
+ print(f"Attempt {attempt}/{max_attempts}: Calling Philippines Disaster Risk MCP...")
125
+
126
+ # TODO: Replace with actual Blaxel MCP client call
127
+ # This is a placeholder for the MCP integration
128
+ # from blaxel import MCPClient
129
+ # mcp_client = MCPClient('philippines-disaster-risk-mcp')
130
+ # response = await mcp_client.call_tool(
131
+ # 'philippines_disaster_risk_mcp_get_risk_data',
132
+ # latitude=lat,
133
+ # longitude=lon
134
+ # )
135
+
136
+ # For now, simulate MCP call
137
+ response = await self._call_mcp_tool(lat, lon)
138
+
139
+ print(f"Successfully retrieved risk data on attempt {attempt}")
140
+ return response
141
+
142
+ except Exception as error:
143
+ last_error = error
144
+ print(f"Attempt {attempt} failed: {str(error)}")
145
+
146
+ if attempt < max_attempts:
147
+ delay = base_delay * (2 ** (attempt - 1))
148
+ print(f"Retrying in {delay}s...")
149
+ await asyncio.sleep(delay)
150
+
151
+ raise Exception(
152
+ f"Failed to retrieve risk data after {max_attempts} attempts: {str(last_error)}"
153
+ )
154
+
155
+ async def _call_mcp_tool(self, lat: float, lon: float) -> Dict[str, Any]:
156
  """
157
+ Call the Philippines Disaster Risk MCP tool
158
+
159
+ This method will be replaced with actual Blaxel MCP client integration
160
 
161
  Args:
162
  lat: Latitude
163
  lon: Longitude
164
 
165
  Returns:
166
+ MCP response data
167
+ """
168
+ # TODO: Implement actual MCP call using Blaxel SDK
169
+ # This is a placeholder that should be replaced with:
170
+ # from blaxel import MCPClient
171
+ # mcp_client = MCPClient('philippines-disaster-risk-mcp')
172
+ # return await mcp_client.call_tool(
173
+ # 'philippines_disaster_risk_mcp_get_risk_data',
174
+ # latitude=lat,
175
+ # longitude=lon
176
+ # )
177
+
178
+ raise NotImplementedError(
179
+ "MCP integration not yet implemented. "
180
+ "This will be connected to the Philippines Disaster Risk MCP Server."
181
+ )
182
+
183
+ def _parse_mcp_response(self, response: Dict[str, Any], lat: float, lon: float) -> RiskData:
184
+ """
185
+ Parse MCP response into RiskData structure
186
+
187
+ Args:
188
+ response: Raw MCP response
189
+ lat: Latitude
190
+ lon: Longitude
191
 
192
+ Returns:
193
+ Structured RiskData object
194
  """
195
+ # Extract hazard data
196
+ hazards = HazardData(
197
+ seismic=self.parse_seismic_hazards(response),
198
+ volcanic=self.parse_volcanic_hazards(response),
199
+ hydrometeorological=self.parse_hydrometeorological_hazards(response)
200
+ )
201
+
202
+ # Extract facilities
203
+ facilities = self.parse_critical_facilities(response)
204
+
205
+ # Categorize overall risk
206
+ risk_summary = self.categorize_overall_risk(hazards)
207
+
208
+ # Extract location info
209
+ location = LocationInfo(
210
+ name=response.get('location', {}).get('name', 'Unknown Location'),
211
+ coordinates=Coordinates(latitude=lat, longitude=lon),
212
+ administrative_area=response.get('location', {}).get('administrative_area', 'Philippines')
213
+ )
214
+
215
+ # Create metadata
216
+ metadata = Metadata(
217
+ timestamp=datetime.utcnow().isoformat(),
218
+ source='Philippines Disaster Risk MCP',
219
+ cache_status=response.get('cache_status', 'unknown'),
220
+ ttl=response.get('ttl', 3600)
221
+ )
222
+
223
+ return RiskData(
224
+ success=True,
225
+ summary=risk_summary,
226
+ location=location,
227
+ hazards=hazards,
228
+ facilities=facilities,
229
+ metadata=metadata
230
+ )
231
+
232
+ def parse_seismic_hazards(self, data: Dict[str, Any]) -> SeismicHazards:
233
+ """
234
+ Parse seismic hazard data from MCP response
235
+
236
+ Extracts active faults, ground shaking, liquefaction, tsunami data
237
+
238
+ Args:
239
+ data: MCP response data
240
+
241
+ Returns:
242
+ SeismicHazards object
243
+ """
244
+ seismic_data = data.get('hazards', {}).get('seismic', {})
245
+
246
+ return SeismicHazards(
247
+ active_fault=self._parse_hazard_detail(
248
+ seismic_data.get('active_fault', {})
249
+ ),
250
+ ground_shaking=self._parse_hazard_detail(
251
+ seismic_data.get('ground_shaking', {})
252
+ ),
253
+ liquefaction=self._parse_hazard_detail(
254
+ seismic_data.get('liquefaction', {})
255
+ ),
256
+ tsunami=self._parse_hazard_detail(
257
+ seismic_data.get('tsunami', {})
258
+ ),
259
+ earthquake_induced_landslide=self._parse_hazard_detail(
260
+ seismic_data.get('earthquake_induced_landslide', {})
261
+ ),
262
+ fissure=self._parse_hazard_detail(
263
+ seismic_data.get('fissure', {})
264
+ ),
265
+ ground_rupture=self._parse_hazard_detail(
266
+ seismic_data.get('ground_rupture', {})
267
  )
268
+ )
269
+
270
+ def parse_volcanic_hazards(self, data: Dict[str, Any]) -> VolcanicHazards:
271
+ """
272
+ Parse volcanic hazard data from MCP response
273
+
274
+ Extracts volcano, ashfall, pyroclastic flow data
275
+
276
+ Args:
277
+ data: MCP response data
278
+
279
+ Returns:
280
+ VolcanicHazards object
281
+ """
282
+ volcanic_data = data.get('hazards', {}).get('volcanic', {})
283
+
284
+ return VolcanicHazards(
285
+ active_volcano=self._parse_hazard_detail(
286
+ volcanic_data.get('active_volcano', {})
287
+ ),
288
+ potentially_active_volcano=self._parse_hazard_detail(
289
+ volcanic_data.get('potentially_active_volcano', {})
290
+ ),
291
+ inactive_volcano=self._parse_hazard_detail(
292
+ volcanic_data.get('inactive_volcano', {})
293
+ ),
294
+ ashfall=self._parse_hazard_detail(
295
+ volcanic_data.get('ashfall', {})
296
+ ),
297
+ pyroclastic_flow=self._parse_hazard_detail(
298
+ volcanic_data.get('pyroclastic_flow', {})
299
+ ),
300
+ lahar=self._parse_hazard_detail(
301
+ volcanic_data.get('lahar', {})
302
+ ),
303
+ lava=self._parse_hazard_detail(
304
+ volcanic_data.get('lava', {})
305
+ ),
306
+ ballistic_projectile=self._parse_hazard_detail(
307
+ volcanic_data.get('ballistic_projectile', {})
308
+ ),
309
+ base_surge=self._parse_hazard_detail(
310
+ volcanic_data.get('base_surge', {})
311
+ ),
312
+ volcanic_tsunami=self._parse_hazard_detail(
313
+ volcanic_data.get('volcanic_tsunami', {})
314
+ )
315
+ )
316
+
317
+ def parse_hydrometeorological_hazards(self, data: Dict[str, Any]) -> HydroHazards:
318
+ """
319
+ Parse hydrometeorological hazard data from MCP response
320
+
321
+ Extracts flood, landslide, storm surge, wind data
322
+
323
+ Args:
324
+ data: MCP response data
325
+
326
+ Returns:
327
+ HydroHazards object
328
+ """
329
+ hydro_data = data.get('hazards', {}).get('hydrometeorological', {})
330
+
331
+ return HydroHazards(
332
+ flood=self._parse_hazard_detail(
333
+ hydro_data.get('flood', {})
334
+ ),
335
+ rain_induced_landslide=self._parse_hazard_detail(
336
+ hydro_data.get('rain_induced_landslide', {})
337
+ ),
338
+ storm_surge=self._parse_hazard_detail(
339
+ hydro_data.get('storm_surge', {})
340
+ ),
341
+ severe_winds=self._parse_hazard_detail(
342
+ hydro_data.get('severe_winds', {})
343
  )
344
+ )
345
+
346
+ def parse_critical_facilities(self, data: Dict[str, Any]) -> FacilityInfo:
347
+ """
348
+ Parse critical facilities data from MCP response
349
+
350
+ Extracts schools, hospitals, road networks
351
+
352
+ Args:
353
+ data: MCP response data
354
+
355
+ Returns:
356
+ FacilityInfo object
357
+ """
358
+ facilities_data = data.get('facilities', {})
359
+
360
+ return FacilityInfo(
361
+ schools=facilities_data.get('schools', []),
362
+ hospitals=facilities_data.get('hospitals', []),
363
+ road_networks=facilities_data.get('road_networks', [])
364
+ )
365
+
366
+ def categorize_overall_risk(self, hazards: HazardData) -> RiskSummary:
367
+ """
368
+ Categorize overall risk level based on hazard data
369
+
370
+ Determines CRITICAL/HIGH/MODERATE/LOW level
371
+
372
+ Args:
373
+ hazards: Complete hazard data
374
+
375
+ Returns:
376
+ RiskSummary with overall risk level and counts
377
+ """
378
+ critical_hazards = []
379
+ high_risk_count = 0
380
+ moderate_risk_count = 0
381
+ total_hazards = 0
382
+
383
+ # Analyze seismic hazards
384
+ seismic_hazards = [
385
+ ('Active Fault', hazards.seismic.active_fault),
386
+ ('Ground Shaking', hazards.seismic.ground_shaking),
387
+ ('Liquefaction', hazards.seismic.liquefaction),
388
+ ('Tsunami', hazards.seismic.tsunami),
389
+ ('Earthquake-Induced Landslide', hazards.seismic.earthquake_induced_landslide),
390
+ ('Fissure', hazards.seismic.fissure),
391
+ ('Ground Rupture', hazards.seismic.ground_rupture)
392
+ ]
393
+
394
+ # Analyze volcanic hazards
395
+ volcanic_hazards = [
396
+ ('Active Volcano', hazards.volcanic.active_volcano),
397
+ ('Potentially Active Volcano', hazards.volcanic.potentially_active_volcano),
398
+ ('Ashfall', hazards.volcanic.ashfall),
399
+ ('Pyroclastic Flow', hazards.volcanic.pyroclastic_flow),
400
+ ('Lahar', hazards.volcanic.lahar),
401
+ ('Lava', hazards.volcanic.lava),
402
+ ('Ballistic Projectile', hazards.volcanic.ballistic_projectile),
403
+ ('Base Surge', hazards.volcanic.base_surge),
404
+ ('Volcanic Tsunami', hazards.volcanic.volcanic_tsunami)
405
+ ]
406
+
407
+ # Analyze hydrometeorological hazards
408
+ hydro_hazards = [
409
+ ('Flood', hazards.hydrometeorological.flood),
410
+ ('Rain-Induced Landslide', hazards.hydrometeorological.rain_induced_landslide),
411
+ ('Storm Surge', hazards.hydrometeorological.storm_surge),
412
+ ('Severe Winds', hazards.hydrometeorological.severe_winds)
413
+ ]
414
+
415
+ all_hazards = seismic_hazards + volcanic_hazards + hydro_hazards
416
+
417
+ for hazard_name, hazard_detail in all_hazards:
418
+ total_hazards += 1
419
+
420
+ # Check severity level
421
+ severity = hazard_detail.severity
422
+ status = hazard_detail.status.lower()
423
+
424
+ if severity:
425
+ severity_lower = severity.lower()
426
+ if 'critical' in severity_lower or 'very high' in severity_lower:
427
+ critical_hazards.append(hazard_name)
428
+ high_risk_count += 1
429
+ elif 'high' in severity_lower:
430
+ high_risk_count += 1
431
+ elif 'moderate' in severity_lower or 'medium' in severity_lower:
432
+ moderate_risk_count += 1
433
+
434
+ # Check status for presence indicators
435
+ if 'present' in status or 'detected' in status or 'within' in status:
436
+ if hazard_name not in critical_hazards:
437
+ high_risk_count += 1
438
+
439
+ # Determine overall risk level
440
+ if len(critical_hazards) >= 3 or high_risk_count >= 5:
441
+ overall_risk_level: RiskLevel = "CRITICAL"
442
+ elif len(critical_hazards) >= 1 or high_risk_count >= 3:
443
+ overall_risk_level = "HIGH"
444
+ elif moderate_risk_count >= 3 or high_risk_count >= 1:
445
+ overall_risk_level = "MODERATE"
446
+ else:
447
+ overall_risk_level = "LOW"
448
+
449
+ return RiskSummary(
450
+ overall_risk_level=overall_risk_level,
451
+ total_hazards_assessed=total_hazards,
452
+ high_risk_count=high_risk_count,
453
+ moderate_risk_count=moderate_risk_count,
454
+ critical_hazards=critical_hazards
455
+ )
456
+
457
+ def _parse_hazard_detail(self, hazard_data: Dict[str, Any]) -> HazardDetail:
458
+ """
459
+ Parse individual hazard detail from MCP response
460
+
461
+ Args:
462
+ hazard_data: Hazard data dictionary
463
+
464
+ Returns:
465
+ HazardDetail object
466
+ """
467
+ return HazardDetail(
468
+ status=hazard_data.get('status', 'unknown'),
469
+ description=hazard_data.get('description', 'No data available'),
470
+ distance=hazard_data.get('distance'),
471
+ direction=hazard_data.get('direction'),
472
+ severity=hazard_data.get('severity')
473
+ )
474
+
475
+
476
+ # Main entry point for Blaxel agent
477
+ async def main(latitude: float, longitude: float) -> Dict[str, Any]:
478
+ """
479
+ Main entry point for the Risk Assessment Agent
480
+
481
+ Args:
482
+ latitude: Latitude in decimal degrees
483
+ longitude: Longitude in decimal degrees
484
+
485
+ Returns:
486
+ Risk assessment data as dictionary
487
+ """
488
+ agent = RiskAssessmentAgent()
489
+
490
+ try:
491
+ risk_data = await agent.get_risk_assessment(latitude, longitude)
492
+
493
+ # Convert to dictionary for JSON serialization
494
+ return {
495
+ 'success': True,
496
+ 'data': risk_data
497
+ }
498
+
499
+ except ValueError as e:
500
+ return {
501
+ 'success': False,
502
+ 'error': {
503
+ 'code': 'INVALID_COORDINATES',
504
+ 'message': str(e),
505
+ 'retry_possible': False
506
+ }
507
+ }
508
+ except Exception as e:
509
+ return {
510
+ 'success': False,
511
+ 'error': {
512
+ 'code': 'MCP_CONNECTION_FAILED',
513
+ 'message': str(e),
514
+ 'retry_possible': True
515
+ }
516
+ }
517
+
518
+
519
+ if __name__ == "__main__":
520
+ # Test the agent
521
+ import sys
522
+
523
+ if len(sys.argv) != 3:
524
+ print("Usage: python agent.py <latitude> <longitude>")
525
+ sys.exit(1)
526
+
527
+ lat = float(sys.argv[1])
528
+ lon = float(sys.argv[2])
529
+
530
+ result = asyncio.run(main(lat, lon))
531
+ print(result)