# 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))