Spaces:
Sleeping
Sleeping
| # easy_agents.py - Enhanced with eNAM Market Data Integration | |
| import json | |
| import requests | |
| import logging | |
| from typing import Optional, Dict, Any, Union, List | |
| from functools import lru_cache | |
| import time | |
| from datetime import datetime | |
| import os | |
| import tempfile | |
| # Configure logging | |
| logging.basicConfig(level=logging.INFO) | |
| logger = logging.getLogger(__name__) | |
| class EasyFarmsAgent: | |
| """ | |
| EasyFarms AI Agent optimized for function calling in AI systems. | |
| Now includes eNAM market data integration. | |
| """ | |
| def __init__(self, timeout: int = 30, max_retries: int = 3): | |
| """Initialize the agent with configuration options.""" | |
| self.timeout = timeout | |
| self.max_retries = max_retries | |
| # Create a session for connection pooling | |
| self.session = requests.Session() | |
| self.session.headers.update({ | |
| 'User-Agent': 'EasyFarms-Agent/1.0', | |
| 'Accept': 'application/json' | |
| }) | |
| # eNAM API configuration | |
| self.enam_api_key = os.environ.get('ENAM_API_KEY','579b464db66ec23bdd00000117ba747cbf6948354c9afae09a8a5087') | |
| self.enam_base_url = "https://api.data.gov.in/resource/9ef84268-d588-465a-a308-a864a43d0070" | |
| # Load mappings | |
| self._load_mappings() | |
| # Cache for market data | |
| self._market_cache = {} | |
| self._cache_timestamp = {} | |
| self._cache_ttl = 300 # 5 minutes cache | |
| def _load_mappings(self): | |
| """Pre-load all mappings for faster access.""" | |
| self.soil_mapping = { | |
| "black": 0, "clayey": 1, "loamy": 2, "red": 3, "sandy": 4 | |
| } | |
| self.fertilizer_crop_mapping = { | |
| "barley": 0, "cotton": 1, "ground nuts": 2, "maize": 3, "millets": 4, | |
| "oil seeds": 5, "paddy": 6, "pulses": 7, "sugarcane": 8, "tobacco": 9, "wheat": 10 | |
| } | |
| self.disease_crop_mapping = { | |
| "almond": "almond", "aloe": "aloe", "apple": "apple", "apricot": "apricot", | |
| "avocado": "avocado", "bamboo": "bamboo", "banana": "banana", "barley": "barley", | |
| "bean": "bean", "bitter_gourd": "bitter_gourd", "black_plum": "black_plum", | |
| "blackberry": "blackberry", "bottle_gourd": "bottle_gourd", "cabbage": "cabbage", | |
| "canola": "canola", "carrot": "carrot", "cashew": "cashew", "cauliflower": "cauliflower", | |
| "chard": "chard", "cherry": "cherry", "chickpea": "chickpea", "citrus": "citrus", | |
| "cocoa": "cocoa", "coconut": "coconut", "coffee": "coffee", "cotton": "cotton", | |
| "cucumber": "cucumber", "currant": "currant", "curry_leaf_tree": "curry_leaf_tree", | |
| "date": "date", "eggplant": "eggplant", "fig": "fig", "garlic": "garlic", | |
| "ginger": "ginger", "gram": "gram", "grape": "grape", "guava": "guava", | |
| "herb": "herb", "jackfruit": "jackfruit", "leek": "leek", "lentil": "lentil", | |
| "lettuce": "lettuce", "maize": "maize", "mango": "mango", "manioc": "manioc", | |
| "melon": "melon", "millet": "millet", "mustard": "mustard", "okra": "okra", | |
| "olive": "olive", "onion": "onion", "papaya": "papaya", "pea": "pea", | |
| "peach": "peach", "peanut": "peanut", "pear": "pear", "pepper": "pepper", | |
| "pigeonpea": "pigeonpea", "pineapple": "pineapple", "pistachio": "pistachio", | |
| "plum": "plum", "pomegranate": "pomegranate", "potato": "potato", | |
| "pumpkin": "pumpkin", "radish": "radish", "raspberry": "raspberry", | |
| "rice": "rice", "rose": "rose", "rye": "rye", "sorghum": "sorghum", | |
| "soybean": "soybean", "strawberry": "strawberry", "sugarbeet": "sugarbeet", | |
| "sugarcane": "sugarcane", "sunflower": "sunflower", "sweetpotato": "sweetpotato", | |
| "tea": "tea", "tamarind": "tamarind", "tobacco": "tobacco", "tomato": "tomato", | |
| "turmeric": "turmeric", "turnip": "turnip", "wheat": "wheat", "zucchini": "zucchini" | |
| } | |
| self.endpoints = { | |
| "crop": "https://nitinbot001-agrigo.hf.space/crop-recommendation", | |
| "fertilizer": "https://nitinbot001-agrigo.hf.space/fertilizer-recommendation", | |
| "disease": "https://api-for-disease-detection.vercel.app/analyze-plant" | |
| } | |
| def _make_request_with_retry(self, method: str, url: str, **kwargs) -> Dict[str, Any]: | |
| """Make HTTP request with retry logic and handle both JSON and HTML responses.""" | |
| last_exception = None | |
| for attempt in range(self.max_retries): | |
| try: | |
| response = self.session.request(method, url, timeout=self.timeout, **kwargs) | |
| response.raise_for_status() | |
| content_type = response.headers.get('content-type', '') | |
| if 'application/json' in content_type: | |
| try: | |
| return response.json() | |
| except ValueError as e: | |
| error_msg = f"Invalid JSON response: {response.text[:200]}..." | |
| logger.error(error_msg) | |
| raise ValueError(error_msg) from e | |
| elif 'text/html' in content_type: | |
| # Handle HTML response | |
| logger.warning(f"Received HTML response from {url}, attempting to parse...") | |
| return self._parse_html_response(response.text) | |
| else: | |
| error_msg = f"Unexpected content type: {content_type}. Response: {response.text[:200]}..." | |
| logger.error(error_msg) | |
| raise ValueError(error_msg) | |
| except requests.exceptions.RequestException as e: | |
| last_exception = e | |
| if attempt < self.max_retries - 1: | |
| wait_time = (attempt + 1) * 2 | |
| logger.warning(f"Request failed (attempt {attempt + 1}), retrying in {wait_time}s: {e}") | |
| time.sleep(wait_time) | |
| raise last_exception or Exception("Failed to get valid response after retries") | |
| def _parse_html_response(self, html_content: str) -> Dict[str, Any]: | |
| """ | |
| Parse HTML response to extract the recommendation from the specified selector path. | |
| """ | |
| try: | |
| from bs4 import BeautifulSoup | |
| except ImportError: | |
| return { | |
| 'status': 'error', | |
| 'message': 'BeautifulSoup4 is required for HTML parsing. Install it with: pip install beautifulsoup4', | |
| 'raw_html': html_content[:500] + '...' | |
| } | |
| try: | |
| soup = BeautifulSoup(html_content, 'html.parser') | |
| result = {} | |
| # Extract recommendation using the specified CSS selector | |
| recommendation = soup.select_one("body > div > div > h3 > b > span") | |
| if recommendation: | |
| result['prediction'] = recommendation.text.strip() | |
| result['status'] = 'success' | |
| else: | |
| # Try to find any error messages if the recommendation isn't found | |
| error_msg = soup.find('div', class_='error') or soup.find('p', class_='error') | |
| if error_msg: | |
| result['error'] = error_msg.text.strip() | |
| else: | |
| result['error'] = 'No recommendation found in the response' | |
| result['status'] = 'error' | |
| result['raw_html'] = html_content[:500] + '...' | |
| return result | |
| except Exception as e: | |
| return { | |
| 'status': 'error', | |
| 'message': f'Error parsing HTML response: {str(e)}', | |
| 'raw_html': html_content[:500] + '...' | |
| } | |
| def _is_cache_valid(self, cache_key: str) -> bool: | |
| """Check if cached data is still valid.""" | |
| if cache_key not in self._cache_timestamp: | |
| return False | |
| elapsed = time.time() - self._cache_timestamp[cache_key] | |
| return elapsed < self._cache_ttl | |
| def _fetch_enam_data(self, params: Dict[str, Any]) -> Dict[str, Any]: | |
| """Fetch data from eNAM API with caching.""" | |
| cache_key = json.dumps(params, sort_keys=True) | |
| # Check cache | |
| if cache_key in self._market_cache and self._is_cache_valid(cache_key): | |
| logger.info("Returning cached market data") | |
| return self._market_cache[cache_key] | |
| # Fetch fresh data | |
| headers = { | |
| 'Accept': '*/*', | |
| 'Accept-Language': 'en-IN,en-US;q=0.9,en;q=0.8,hi;q=0.7', | |
| 'Connection': 'keep-alive', | |
| 'Origin': 'https://app.easyfarms.in', | |
| 'Referer': 'https://app.easyfarms.in/', | |
| 'Sec-Fetch-Dest': 'empty', | |
| 'Sec-Fetch-Mode': 'cors', | |
| 'Sec-Fetch-Site': 'cross-site', | |
| 'User-Agent': 'Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36' | |
| } | |
| response = self._make_request_with_retry( | |
| "GET", | |
| self.enam_base_url, | |
| params=params, | |
| headers=headers | |
| ) | |
| # Cache the response | |
| self._market_cache[cache_key] = response | |
| self._cache_timestamp[cache_key] = time.time() | |
| return response | |
| # Global agent instance | |
| _easyfarms_agent = EasyFarmsAgent() | |
| # ============================================================================= | |
| # MARKET DATA FUNCTION DEFINITIONS | |
| # ============================================================================= | |
| def get_market_prices( | |
| commodity: Optional[str] = None, | |
| state: Optional[str] = None, | |
| district: Optional[str] = None, | |
| market: Optional[str] = None, | |
| limit: int = 25 | |
| ) -> Dict[str, Any]: | |
| """ | |
| Get current market prices for agricultural commodities from Indian mandis. | |
| Args: | |
| commodity: Name of the commodity (e.g., "Tomato", "Wheat", "Rice") | |
| state: State name (e.g., "Gujarat", "Maharashtra") | |
| district: District name | |
| market: Market/mandi name | |
| limit: Number of records to fetch (default: 25, max: 100) | |
| Returns: | |
| Dictionary with market price data and statistics | |
| """ | |
| try: | |
| params = { | |
| 'api-key': _easyfarms_agent.enam_api_key, | |
| 'format': 'json', | |
| 'limit': min(limit, 100), | |
| 'offset': 0 | |
| } | |
| # Add filters if provided | |
| filters = [] | |
| if commodity: | |
| filters.append(f"commodity:{commodity}") | |
| if state: | |
| filters.append(f"state:{state}") | |
| if district: | |
| filters.append(f"district:{district}") | |
| if market: | |
| filters.append(f"market:{market}") | |
| if filters: | |
| params['filters'] = ','.join(filters) | |
| # Fetch data from eNAM API | |
| response = _easyfarms_agent._fetch_enam_data(params) | |
| records = response.get('records', []) | |
| # Process and analyze the data | |
| if records: | |
| # Calculate statistics | |
| prices = { | |
| 'min_prices': [], | |
| 'max_prices': [], | |
| 'modal_prices': [] | |
| } | |
| for record in records: | |
| try: | |
| if 'min_price' in record and record['min_price']: | |
| prices['min_prices'].append(float(record['min_price'])) | |
| if 'max_price' in record and record['max_price']: | |
| prices['max_prices'].append(float(record['max_price'])) | |
| if 'modal_price' in record and record['modal_price']: | |
| prices['modal_prices'].append(float(record['modal_price'])) | |
| except (ValueError, TypeError): | |
| continue | |
| # Calculate averages | |
| stats = {} | |
| for price_type, values in prices.items(): | |
| if values: | |
| stats[price_type] = { | |
| 'average': sum(values) / len(values), | |
| 'min': min(values), | |
| 'max': max(values), | |
| 'count': len(values) | |
| } | |
| return { | |
| 'status': 'success', | |
| 'mode': 'market_prices', | |
| 'total_records': response.get('total', len(records)), | |
| 'fetched_records': len(records), | |
| 'statistics': stats, | |
| 'records': records[:10], # Return first 10 records for preview | |
| 'filters_applied': { | |
| 'commodity': commodity, | |
| 'state': state, | |
| 'district': district, | |
| 'market': market | |
| } | |
| } | |
| else: | |
| return { | |
| 'status': 'success', | |
| 'mode': 'market_prices', | |
| 'message': 'No records found for the given filters', | |
| 'filters_applied': { | |
| 'commodity': commodity, | |
| 'state': state, | |
| 'district': district, | |
| 'market': market | |
| } | |
| } | |
| except Exception as e: | |
| return { | |
| 'status': 'error', | |
| 'mode': 'market_prices', | |
| 'error': str(e) | |
| } | |
| def get_commodity_list( | |
| state: Optional[str] = None, | |
| limit: int = 100 | |
| ) -> Dict[str, Any]: | |
| """ | |
| Get list of available commodities in the market data. | |
| Args: | |
| state: Optional state filter to get commodities specific to a state | |
| limit: Number of records to analyze (default: 100) | |
| Returns: | |
| Dictionary with list of unique commodities and their varieties | |
| """ | |
| try: | |
| params = { | |
| 'api-key': _easyfarms_agent.enam_api_key, | |
| 'format': 'json', | |
| 'limit': limit, | |
| 'offset': 0 | |
| } | |
| if state: | |
| params['filters'] = f"state:{state}" | |
| response = _easyfarms_agent._fetch_enam_data(params) | |
| records = response.get('records', []) | |
| # Extract unique commodities and varieties | |
| commodities = {} | |
| for record in records: | |
| commodity = record.get('commodity', '').strip() | |
| variety = record.get('variety', '').strip() | |
| if commodity: | |
| if commodity not in commodities: | |
| commodities[commodity] = set() | |
| if variety: | |
| commodities[commodity].add(variety) | |
| # Convert sets to sorted lists | |
| commodities = {k: sorted(list(v)) for k, v in commodities.items()} | |
| return { | |
| 'status': 'success', | |
| 'mode': 'commodity_list', | |
| 'total_commodities': len(commodities), | |
| 'commodities': commodities, | |
| 'state_filter': state | |
| } | |
| except Exception as e: | |
| return { | |
| 'status': 'error', | |
| 'mode': 'commodity_list', | |
| 'error': str(e) | |
| } | |
| def get_market_locations() -> Dict[str, Any]: | |
| """ | |
| Get hierarchical list of market locations (states -> districts -> markets). | |
| Returns: | |
| Dictionary with nested structure of market locations | |
| """ | |
| try: | |
| params = { | |
| 'api-key': _easyfarms_agent.enam_api_key, | |
| 'format': 'json', | |
| 'limit': 100, | |
| 'offset': 0 | |
| } | |
| response = _easyfarms_agent._fetch_enam_data(params) | |
| records = response.get('records', []) | |
| # Build hierarchical structure | |
| locations = {} | |
| for record in records: | |
| state = record.get('state', '').strip() | |
| district = record.get('district', '').strip() | |
| market = record.get('market', '').strip() | |
| if state: | |
| if state not in locations: | |
| locations[state] = {} | |
| if district: | |
| if district not in locations[state]: | |
| locations[state][district] = set() | |
| if market: | |
| locations[state][district].add(market) | |
| # Convert sets to sorted lists | |
| for state in locations: | |
| for district in locations[state]: | |
| locations[state][district] = sorted(list(locations[state][district])) | |
| # Calculate totals | |
| total_states = len(locations) | |
| total_districts = sum(len(districts) for districts in locations.values()) | |
| total_markets = sum( | |
| len(markets) | |
| for state_data in locations.values() | |
| for markets in state_data.values() | |
| ) | |
| return { | |
| 'status': 'success', | |
| 'mode': 'market_locations', | |
| 'total_states': total_states, | |
| 'total_districts': total_districts, | |
| 'total_markets': total_markets, | |
| 'locations': locations | |
| } | |
| except Exception as e: | |
| return { | |
| 'status': 'error', | |
| 'mode': 'market_locations', | |
| 'error': str(e) | |
| } | |
| def compare_commodity_prices( | |
| commodity: str, | |
| states: Optional[List[str]] = None, | |
| limit: int = 50 | |
| ) -> Dict[str, Any]: | |
| """ | |
| Compare prices of a commodity across different states or markets. | |
| Args: | |
| commodity: Name of the commodity to compare | |
| states: List of states to compare (optional, if not provided compares all) | |
| limit: Number of records per state | |
| Returns: | |
| Dictionary with price comparison data | |
| """ | |
| try: | |
| comparison_data = {} | |
| if states: | |
| # Compare specific states | |
| for state in states: | |
| params = { | |
| 'api-key': _easyfarms_agent.enam_api_key, | |
| 'format': 'json', | |
| 'limit': limit, | |
| 'offset': 0, | |
| 'filters': f"commodity:{commodity},state:{state}" | |
| } | |
| response = _easyfarms_agent._fetch_enam_data(params) | |
| records = response.get('records', []) | |
| if records: | |
| prices = [] | |
| for r in records: | |
| modal_price = r.get('modal_price') | |
| if modal_price: | |
| try: | |
| price = float(modal_price) | |
| if price > 0: | |
| prices.append(price) | |
| except (ValueError, TypeError): | |
| continue | |
| if prices: | |
| comparison_data[state] = { | |
| 'average_price': sum(prices) / len(prices), | |
| 'min_price': min(prices), | |
| 'max_price': max(prices), | |
| 'sample_count': len(prices), | |
| 'sample_markets': list(set(r.get('market', '') for r in records[:5] if r.get('market'))) | |
| } | |
| else: | |
| # Get data for all states | |
| params = { | |
| 'api-key': _easyfarms_agent.enam_api_key, | |
| 'format': 'json', | |
| 'limit': 100, | |
| 'offset': 0, | |
| 'filters': f"commodity:{commodity}" | |
| } | |
| response = _easyfarms_agent._fetch_enam_data(params) | |
| records = response.get('records', []) | |
| # Group by state | |
| state_data = {} | |
| for record in records: | |
| state = record.get('state', '') | |
| if state: | |
| if state not in state_data: | |
| state_data[state] = [] | |
| modal_price = record.get('modal_price') | |
| if modal_price: | |
| try: | |
| price = float(modal_price) | |
| if price > 0: | |
| state_data[state].append(price) | |
| except (ValueError, TypeError): | |
| continue | |
| # Calculate statistics per state | |
| for state, prices in state_data.items(): | |
| if prices: | |
| comparison_data[state] = { | |
| 'average_price': sum(prices) / len(prices), | |
| 'min_price': min(prices), | |
| 'max_price': max(prices), | |
| 'sample_count': len(prices) | |
| } | |
| # Find best and worst prices | |
| if comparison_data: | |
| avg_prices = {state: data['average_price'] for state, data in comparison_data.items()} | |
| best_state = min(avg_prices, key=avg_prices.get) | |
| worst_state = max(avg_prices, key=avg_prices.get) | |
| return { | |
| 'status': 'success', | |
| 'mode': 'price_comparison', | |
| 'commodity': commodity, | |
| 'comparison_data': comparison_data, | |
| 'summary': { | |
| 'best_price_state': best_state, | |
| 'best_average_price': avg_prices[best_state], | |
| 'worst_price_state': worst_state, | |
| 'worst_average_price': avg_prices[worst_state], | |
| 'price_range': avg_prices[worst_state] - avg_prices[best_state], | |
| 'states_compared': len(comparison_data) | |
| } | |
| } | |
| else: | |
| return { | |
| 'status': 'success', | |
| 'mode': 'price_comparison', | |
| 'commodity': commodity, | |
| 'message': 'No price data found for the specified commodity' | |
| } | |
| except Exception as e: | |
| return { | |
| 'status': 'error', | |
| 'mode': 'price_comparison', | |
| 'error': str(e) | |
| } | |
| def get_crop_recommendation( | |
| N: int, | |
| P: int, | |
| K: int, | |
| temperature: float, | |
| humidity: float, | |
| ph: float = None, | |
| rainfall: float = 100 | |
| ) -> Dict[str, Any]: | |
| try: | |
| data = { | |
| "N": N, "P": P, "K": K, | |
| "temperature": temperature, | |
| "humidity": humidity, | |
| "ph": ph if ph is not None else 6.5, | |
| "rainfall": rainfall | |
| } | |
| response = _easyfarms_agent._make_request_with_retry( | |
| "POST", | |
| _easyfarms_agent.endpoints["crop"], | |
| data=data | |
| ) | |
| if isinstance(response, dict): | |
| response['input_parameters'] = data | |
| if 'mode' not in response: | |
| response['mode'] = 'crop_recommendation' | |
| logger.info(f"Crop recommendation response: {response}") | |
| return response | |
| except Exception as e: | |
| error_result = { | |
| "error": str(e), | |
| "status": "error", | |
| "mode": "crop_recommendation" | |
| } | |
| logger.error(f"Crop recommendation failed: {e}") | |
| return error_result | |
| def get_fertilizer_recommendation( | |
| crop: str, | |
| soil: str, | |
| temperature: float, | |
| humidity: float, | |
| moisture: float, | |
| N: int, | |
| P: int, | |
| K: int | |
| ) -> Dict[str, Any]: | |
| try: | |
| soil_code = _easyfarms_agent.soil_mapping.get(soil.lower(), soil) | |
| crop_code = _easyfarms_agent.fertilizer_crop_mapping.get(crop.lower(), crop) | |
| data = { | |
| "temperature": temperature, | |
| "humidity": humidity, | |
| "moisture": moisture, | |
| "N": N, "P": P, "K": K, | |
| "soil": soil_code, | |
| "crop": crop_code | |
| } | |
| response = _easyfarms_agent._make_request_with_retry( | |
| "POST", | |
| _easyfarms_agent.endpoints["fertilizer"], | |
| data=data | |
| ) | |
| if isinstance(response, dict): | |
| response['input_parameters'] = { | |
| **data, | |
| "original_soil": soil, | |
| "original_crop": crop | |
| } | |
| if 'mode' not in response: | |
| response['mode'] = 'fertilizer_recommendation' | |
| logger.info(f"Fertilizer recommendation response: {response}") | |
| return response | |
| except Exception as e: | |
| error_result = { | |
| "status": "error", | |
| "error": str(e), | |
| "mode": "fertilizer_recommendation" | |
| } | |
| logger.error(f"Fertilizer recommendation failed: {e}") | |
| return error_result | |
| def detect_plant_disease( | |
| crop: str, | |
| image_path: str, # Can now be a URL or local path | |
| language: str = "en" | |
| ) -> Dict[str, Any]: | |
| """ | |
| Detect plant diseases from an image URL or local path. | |
| """ | |
| temp_image_path = None | |
| try: | |
| # Check if the image_path is a URL | |
| if image_path.startswith(('http://', 'https://')): | |
| logger.info(f"Downloading image from URL: {image_path}") | |
| response = requests.get(image_path, stream=True, timeout=30) | |
| response.raise_for_status() | |
| # Create a temporary file to store the downloaded image | |
| with tempfile.NamedTemporaryFile(delete=False, suffix=".jpg") as temp_file: | |
| for chunk in response.iter_content(chunk_size=8192): | |
| temp_file.write(chunk) | |
| temp_image_path = temp_file.name | |
| # The actual path to use is now the temporary file's path | |
| path_to_process = temp_image_path | |
| else: | |
| # If not a URL, assume it's a local file path | |
| path_to_process = image_path | |
| # Check if the final file path exists | |
| if not os.path.exists(path_to_process): | |
| return { | |
| "error": f"Image file not found at path: {path_to_process}", | |
| "status": "error", "mode": "disease_detection" | |
| } | |
| crop_key = _easyfarms_agent.disease_crop_mapping.get(crop.lower(), crop.lower()) | |
| headers = { 'accept': '*/*' } | |
| with open(path_to_process, "rb") as f: | |
| files = { 'image': ('blob', f, 'image/jpeg') } | |
| data = { 'crop': crop_key, 'language': language } | |
| response = _easyfarms_agent.session.post( | |
| _easyfarms_agent.endpoints["disease"], | |
| files=files, data=data, headers=headers, | |
| timeout=_easyfarms_agent.timeout | |
| ) | |
| response.raise_for_status() | |
| result = response.json() | |
| result.update({ "status": "success", "mode": "disease_detection" }) | |
| return result | |
| except Exception as e: | |
| logger.error(f"Disease detection failed: {e}") | |
| return { "error": str(e), "status": "error", "mode": "disease_detection" } | |
| finally: | |
| # Clean up the temporary file if it was created | |
| if temp_image_path and os.path.exists(temp_image_path): | |
| try: | |
| os.remove(temp_image_path) | |
| logger.info(f"Removed temporary image file: {temp_image_path}") | |
| except OSError: | |
| logger.warning(f"Failed to remove temporary file: {temp_image_path}") | |
| def get_supported_options(mode: str) -> Dict[str, Any]: | |
| try: | |
| result = {"status": "success", "mode": "supported_options"} | |
| if mode == "fertilizer_crops": | |
| result["fertilizer_crops"] = list(_easyfarms_agent.fertilizer_crop_mapping.keys()) | |
| elif mode == "disease_crops": | |
| result["disease_crops"] = list(_easyfarms_agent.disease_crop_mapping.keys()) | |
| elif mode == "soil_types": | |
| result["soil_types"] = list(_easyfarms_agent.soil_mapping.keys()) | |
| elif mode == "all": | |
| result.update({ | |
| "fertilizer_crops": list(_easyfarms_agent.fertilizer_crop_mapping.keys()), | |
| "disease_crops": list(_easyfarms_agent.disease_crop_mapping.keys()), | |
| "soil_types": list(_easyfarms_agent.soil_mapping.keys()) | |
| }) | |
| else: | |
| return { | |
| "error": f"Invalid mode: {mode}. Use 'fertilizer_crops', 'disease_crops', 'soil_types', or 'all'", | |
| "status": "error" | |
| } | |
| return result | |
| except Exception as e: | |
| return { | |
| "error": str(e), | |
| "status": "error", | |
| "mode": "supported_options" | |
| } | |
| # ============================================================================= | |
| # ENHANCED FUNCTION SCHEMAS WITH MARKET DATA | |
| # ============================================================================= | |
| EASYFARMS_FUNCTION_SCHEMAS = [ | |
| { | |
| "name": "get_crop_recommendation", | |
| "description": "Get crop recommendation based on soil nutrients (N, P, K) and environmental conditions. Returns the best crop to grow given current soil and weather conditions.", | |
| "parameters": { | |
| "type": "object", | |
| "properties": { | |
| "N": { | |
| "type": "integer", | |
| "description": "Nitrogen content in soil (ppm)", | |
| "minimum": 0, | |
| "maximum": 200 | |
| }, | |
| "P": { | |
| "type": "integer", | |
| "description": "Phosphorous content in soil (ppm)", | |
| "minimum": 0, | |
| "maximum": 150 | |
| }, | |
| "K": { | |
| "type": "integer", | |
| "description": "Potassium content in soil (ppm)", | |
| "minimum": 0, | |
| "maximum": 300 | |
| }, | |
| "temperature": { | |
| "type": "number", | |
| "description": "Temperature in Celsius", | |
| "minimum": -10, | |
| "maximum": 50 | |
| }, | |
| "humidity": { | |
| "type": "number", | |
| "description": "Relative humidity percentage", | |
| "minimum": 0, | |
| "maximum": 100 | |
| }, | |
| "ph": { | |
| "type": "number", | |
| "description": "Soil pH level", | |
| "minimum": 0, | |
| "maximum": 14 | |
| }, | |
| "rainfall": { | |
| "type": "number", | |
| "description": "Average rainfall in mm (default: 100)", | |
| "minimum": 0, | |
| "maximum": 500, | |
| "default": 100 | |
| } | |
| }, | |
| "required": ["N", "P", "K", "temperature", "humidity"] | |
| } | |
| }, | |
| { | |
| "name": "get_fertilizer_recommendation", | |
| "description": "Get fertilizer recommendation for a specific crop based on soil conditions and nutrient levels. Returns the best fertilizer type to use.", | |
| "parameters": { | |
| "type": "object", | |
| "properties": { | |
| "crop": { | |
| "type": "string", | |
| "description": "Crop type", | |
| "enum": ["barley", "cotton", "ground nuts", "maize", "millets", "oil seeds", "paddy", "pulses", "sugarcane", "tobacco", "wheat"] | |
| }, | |
| "soil": { | |
| "type": "string", | |
| "description": "Soil type", | |
| "enum": ["black", "clayey", "loamy", "red", "sandy"] | |
| }, | |
| "temperature": { | |
| "type": "number", | |
| "description": "Temperature in Celsius", | |
| "minimum": -10, | |
| "maximum": 50 | |
| }, | |
| "humidity": { | |
| "type": "number", | |
| "description": "Relative humidity percentage", | |
| "minimum": 0, | |
| "maximum": 100 | |
| }, | |
| "moisture": { | |
| "type": "number", | |
| "description": "Soil moisture percentage", | |
| "minimum": 0, | |
| "maximum": 100 | |
| }, | |
| "N": { | |
| "type": "integer", | |
| "description": "Nitrogen content in soil (ppm)", | |
| "minimum": 0, | |
| "maximum": 200 | |
| }, | |
| "P": { | |
| "type": "integer", | |
| "description": "Phosphorous content in soil (ppm)", | |
| "minimum": 0, | |
| "maximum": 150 | |
| }, | |
| "K": { | |
| "type": "integer", | |
| "description": "Potassium content in soil (ppm)", | |
| "minimum": 0, | |
| "maximum": 300 | |
| } | |
| }, | |
| "required": ["crop", "soil", "temperature", "humidity", "moisture", "N", "P", "K"] | |
| } | |
| }, | |
| { | |
| "name": "detect_plant_disease", | |
| "description": "Detect plant diseases from an image of a plant leaf or crop. Analyzes the image to identify potential diseases and provides treatment recommendations.", | |
| "parameters": { | |
| "type": "object", | |
| "properties": { | |
| "crop": { | |
| "type": "string", | |
| "description": "Type of crop/plant in the image", | |
| "enum": ["almond", "aloe", "apple", "apricot", "avocado", "bamboo", "banana", "barley", "bean", "bitter_gourd", "black_plum", "blackberry", "bottle_gourd", "cabbage", "canola", "carrot", "cashew", "cauliflower", "chard", "cherry", "chickpea", "citrus", "cocoa", "coconut", "coffee", "cotton", "cucumber", "currant", "curry_leaf_tree", "date", "eggplant", "fig", "garlic", "ginger", "gram", "grape", "guava", "herb", "jackfruit", "leek", "lentil", "lettuce", "maize", "mango", "manioc", "melon", "millet", "mustard", "okra", "olive", "onion", "papaya", "pea", "peach", "peanut", "pear", "pepper", "pigeonpea", "pineapple", "pistachio", "plum", "pomegranate", "potato", "pumpkin", "radish", "raspberry", "rice", "rose", "rye", "sorghum", "soybean", "strawberry", "sugarbeet", "sugarcane", "sunflower", "sweetpotato", "tea", "tamarind", "tobacco", "tomato", "turmeric", "turnip", "wheat", "zucchini"] | |
| }, | |
| "image_path": { | |
| "type": "string", | |
| "description": "Path to the plant/leaf image file or image URL" | |
| }, | |
| "language": { | |
| "type": "string", | |
| "description": "Response language code (default: 'en')", | |
| "default": "en", | |
| "enum": ["en", "hi", "es", "fr", "de"] | |
| } | |
| }, | |
| "required": ["crop", "image_path"] | |
| } | |
| }, | |
| { | |
| "name": "get_market_prices", | |
| "description": "Get current market prices for agricultural commodities from Indian mandis (markets). Returns price data with statistics.", | |
| "parameters": { | |
| "type": "object", | |
| "properties": { | |
| "commodity": { | |
| "type": "string", | |
| "description": "Name of the commodity (e.g., 'Tomato', 'Wheat', 'Rice')" | |
| }, | |
| "state": { | |
| "type": "string", | |
| "description": "State name (e.g., 'Gujarat', 'Maharashtra')" | |
| }, | |
| "district": { | |
| "type": "string", | |
| "description": "District name" | |
| }, | |
| "market": { | |
| "type": "string", | |
| "description": "Market/mandi name" | |
| }, | |
| "limit": { | |
| "type": "integer", | |
| "description": "Number of records to fetch (default: 25, max: 100)", | |
| "minimum": 1, | |
| "maximum": 100, | |
| "default": 25 | |
| } | |
| } | |
| } | |
| }, | |
| { | |
| "name": "get_commodity_list", | |
| "description": "Get list of available agricultural commodities with their varieties in the market data.", | |
| "parameters": { | |
| "type": "object", | |
| "properties": { | |
| "state": { | |
| "type": "string", | |
| "description": "Optional state filter to get commodities specific to a state" | |
| }, | |
| "limit": { | |
| "type": "integer", | |
| "description": "Number of records to analyze (default: 100)", | |
| "minimum": 1, | |
| "maximum": 500, | |
| "default": 100 | |
| } | |
| } | |
| } | |
| }, | |
| { | |
| "name": "get_market_locations", | |
| "description": "Get hierarchical list of market locations organized by states, districts, and markets.", | |
| "parameters": { | |
| "type": "object", | |
| "properties": {} | |
| } | |
| }, | |
| { | |
| "name": "compare_commodity_prices", | |
| "description": "Compare prices of a commodity across different states or markets to find the best deals.", | |
| "parameters": { | |
| "type": "object", | |
| "properties": { | |
| "commodity": { | |
| "type": "string", | |
| "description": "Name of the commodity to compare" | |
| }, | |
| "states": { | |
| "type": "array", | |
| "items": { | |
| "type": "string" | |
| }, | |
| "description": "List of states to compare (optional, if not provided compares all)" | |
| }, | |
| "limit": { | |
| "type": "integer", | |
| "description": "Number of records per state (default: 50)", | |
| "minimum": 1, | |
| "maximum": 100, | |
| "default": 50 | |
| } | |
| }, | |
| "required": ["commodity"] | |
| } | |
| }, | |
| { | |
| "name": "get_supported_options", | |
| "description": "Get lists of supported crops and soil types for different analysis modes. Useful for showing available options to users.", | |
| "parameters": { | |
| "type": "object", | |
| "properties": { | |
| "mode": { | |
| "type": "string", | |
| "description": "Mode to get supported options for", | |
| "enum": ["fertilizer_crops", "disease_crops", "soil_types", "all"] | |
| } | |
| }, | |
| "required": ["mode"] | |
| } | |
| } | |
| ] | |
| # ============================================================================= | |
| # FUNCTION MAPPING FOR AI AGENTS | |
| # ============================================================================= | |
| EASYFARMS_FUNCTIONS = { | |
| "get_crop_recommendation": get_crop_recommendation, | |
| "get_fertilizer_recommendation": get_fertilizer_recommendation, | |
| "detect_plant_disease": detect_plant_disease, | |
| "get_supported_options": get_supported_options, | |
| "get_market_prices": get_market_prices, | |
| "get_commodity_list": get_commodity_list, | |
| "get_market_locations": get_market_locations, | |
| "compare_commodity_prices": compare_commodity_prices | |
| } | |
| # ============================================================================= | |
| # UTILITY FUNCTIONS FOR AI AGENT INTEGRATION | |
| # ============================================================================= | |
| def execute_easyfarms_function(function_name: str, **kwargs) -> Dict[str, Any]: | |
| """ | |
| Execute an EasyFarms function by name with given parameters. | |
| Args: | |
| function_name: Name of the function to execute | |
| **kwargs: Function parameters | |
| Returns: | |
| Function execution result | |
| """ | |
| if function_name not in EASYFARMS_FUNCTIONS: | |
| return { | |
| "error": f"Unknown function: {function_name}. Available: {list(EASYFARMS_FUNCTIONS.keys())}", | |
| "status": "error" | |
| } | |
| try: | |
| return EASYFARMS_FUNCTIONS[function_name](**kwargs) | |
| except TypeError as e: | |
| return { | |
| "error": f"Invalid parameters for {function_name}: {str(e)}", | |
| "status": "error" | |
| } | |
| except Exception as e: | |
| return { | |
| "error": f"Execution failed for {function_name}: {str(e)}", | |
| "status": "error" | |
| } | |
| def get_function_schemas() -> List[Dict]: | |
| """Get all function schemas for AI agent registration.""" | |
| return EASYFARMS_FUNCTION_SCHEMAS | |
| def get_function_names() -> List[str]: | |
| """Get list of available function names.""" | |
| return list(EASYFARMS_FUNCTIONS.keys()) | |
| # ============================================================================= | |
| # EXAMPLE USAGE FOR AI AGENTS | |
| # ============================================================================= | |
| if __name__ == "__main__": | |
| print("=== Enhanced EasyFarms Agent with Market Data ===\n") | |
| print("Available Functions:") | |
| for name in get_function_names(): | |
| print(f" - {name}") | |
| print() | |
| # Example 1: Get market prices | |
| print("1. Getting tomato prices in Gujarat:") | |
| market_result = execute_easyfarms_function( | |
| "get_market_prices", | |
| commodity="Tomato", | |
| state="Gujarat", | |
| limit=10 | |
| ) | |
| print(json.dumps(market_result, indent=2)) | |
| print() | |
| # Example 2: Compare commodity prices | |
| print("2. Comparing wheat prices across states:") | |
| comparison_result = execute_easyfarms_function( | |
| "compare_commodity_prices", | |
| commodity="Wheat", | |
| states=["Punjab", "Haryana", "Uttar Pradesh"] | |
| ) | |
| print(json.dumps(comparison_result, indent=2)) | |
| print() | |
| # Example 3: Get commodity list | |
| print("3. Getting available commodities:") | |
| commodity_result = execute_easyfarms_function( | |
| "get_commodity_list", | |
| limit=20 | |
| ) | |
| print(f"Total commodities: {commodity_result.get('total_commodities', 0)}") | |
| print() | |
| # Example 4: Crop recommendation (existing function) | |
| print("4. Getting crop recommendation:") | |
| crop_result = execute_easyfarms_function( | |
| "get_crop_recommendation", | |
| N=90, P=42, K=43, | |
| temperature=20.87, | |
| humidity=82.0, | |
| ph=6.5, | |
| rainfall=202.9 | |
| ) | |
| print(json.dumps(crop_result, indent=2)) |