EasyBot / easy_agents.py
NitinBot001's picture
Upload 10 files
f374654 verified
# 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))