Spaces:
Sleeping
Sleeping
| # app.py - Production-ready Hugging Face Spaces deployment | |
| from fastapi import FastAPI, HTTPException | |
| from fastapi.middleware.cors import CORSMiddleware | |
| from fastapi.staticfiles import StaticFiles | |
| from fastapi.responses import HTMLResponse | |
| from pydantic import BaseModel, Field | |
| import torch | |
| import numpy as np | |
| import pandas as pd | |
| import json | |
| import gc | |
| import os | |
| import logging | |
| from transformers import AutoModelForCausalLM, AutoTokenizer, BitsAndBytesConfig | |
| from peft import PeftModel | |
| from huggingface_hub import hf_hub_download | |
| from typing import List, Dict, Any, Optional | |
| import uvicorn | |
| # Setup logging | |
| logging.basicConfig(level=logging.INFO) | |
| logger = logging.getLogger(__name__) | |
| # Clear cache | |
| torch.cuda.empty_cache() | |
| gc.collect() | |
| PARAMS = ["N","P","K","temperature","pH","rainfall","humidity"] | |
| # Acceptable ranges | |
| IGNORE_RANGES = { | |
| "N": (-10, 10), | |
| "P": (-10, 10), | |
| "K": (-10, 10), | |
| "temperature": (-0.2, 0.2), | |
| "pH": (-0.2, 0.2), | |
| "humidity": (-5, 5), | |
| "rainfall": (-15, 15) | |
| } | |
| def evaluate_problems_and_diffs(required: np.ndarray, given: np.ndarray): | |
| problems = [] | |
| diff_dict = {} | |
| for i, param in enumerate(PARAMS): | |
| diff = given[i] - required[i] | |
| low, high = IGNORE_RANGES[param] | |
| if not (low <= diff <= high): | |
| status = "deficiency" if diff < 0 else "excess" | |
| problems.append(f"{param}_{status}") | |
| diff_dict[param] = diff | |
| return problems, diff_dict | |
| class AgriculturalAdvisor: | |
| def __init__(self): | |
| self.model = None | |
| self.tokenizer = None | |
| self.df1 = None | |
| self.df2 = None | |
| self.template = None | |
| self.model_loaded = False | |
| self.data_loaded = False | |
| try: | |
| self.load_data() | |
| self.load_model() | |
| logger.info("✅ Agricultural Advisor initialized successfully!") | |
| except Exception as e: | |
| logger.error(f"❌ Failed to initialize: {str(e)}") | |
| def load_data(self): | |
| """Load datasets with fallback options""" | |
| try: | |
| # Try to load datasets | |
| if os.path.exists('Crop_recommendation.csv'): | |
| self.df1 = pd.read_csv('Crop_recommendation.csv') | |
| logger.info("✅ Crop_recommendation.csv loaded") | |
| else: | |
| # Create fallback dataset | |
| logger.warning("⚠️ Crop_recommendation.csv not found, creating fallback") | |
| self.df1 = self.create_fallback_dataset() | |
| if os.path.exists('sensor_Crop_Dataset.csv'): | |
| self.df2 = pd.read_csv('sensor_Crop_Dataset.csv') | |
| self.df2.rename(columns={"crop": "label"}, inplace=True) | |
| self.df2 = self.df2.drop(["soil","variety"], axis=1, errors='ignore') | |
| logger.info("✅ sensor_Crop_Dataset.csv loaded") | |
| else: | |
| logger.warning("⚠️ sensor_Crop_Dataset.csv not found") | |
| self.df2 = pd.DataFrame() | |
| # Load template | |
| if os.path.exists("crop_template.json"): | |
| with open("crop_template.json") as f: | |
| self.template = json.load(f) | |
| logger.info("✅ Template loaded") | |
| else: | |
| logger.warning("⚠️ Template not found, creating fallback") | |
| self.template = self.create_fallback_template() | |
| self.data_loaded = True | |
| except Exception as e: | |
| logger.error(f"❌ Error loading data: {str(e)}") | |
| # Create minimal fallbacks | |
| self.df1 = self.create_fallback_dataset() | |
| self.df2 = pd.DataFrame() | |
| self.template = self.create_fallback_template() | |
| self.data_loaded = True | |
| def create_fallback_dataset(self): | |
| """Create minimal dataset for demo""" | |
| return pd.DataFrame({ | |
| 'N': [80, 75, 85, 70, 90], | |
| 'P': [40, 35, 45, 30, 50], | |
| 'K': [67, 60, 70, 55, 75], | |
| 'temperature': [25, 27, 23, 30, 20], | |
| 'pH': [7.0, 6.8, 7.2, 6.5, 7.5], | |
| 'rainfall': [200, 180, 220, 150, 250], | |
| 'humidity': [60, 65, 55, 70, 50], | |
| 'label': ['rice', 'wheat', 'maize', 'cotton', 'sugarcane'] | |
| }) | |
| def create_fallback_template(self): | |
| """Create minimal template""" | |
| return { | |
| "rice": { | |
| "N_deficiency": { | |
| "Description": "Nitrogen deficiency causes yellowing of older leaves and stunted growth", | |
| "Homemade/Natural Remedies": "Apply compost, farmyard manure, or green manures", | |
| "Commercial Suggestions": "Apply urea fertilizer in split doses", | |
| "Cultural Practices": "Use alternate wetting and drying irrigation", | |
| "Crop-Specific Notes": "Critical during tillering stage" | |
| }, | |
| "P_deficiency": { | |
| "Description": "Phosphorus deficiency causes dark green to purplish leaves", | |
| "Homemade/Natural Remedies": "Apply bone meal or rock phosphate", | |
| "Commercial Suggestions": "Apply superphosphate as basal dose", | |
| "Cultural Practices": "Maintain soil pH near neutral", | |
| "Crop-Specific Notes": "Important for root and flower development" | |
| } | |
| }, | |
| "wheat": { | |
| "N_deficiency": { | |
| "Description": "Nitrogen deficiency in wheat causes chlorosis and poor tillering", | |
| "Homemade/Natural Remedies": "Apply compost and green manures", | |
| "Commercial Suggestions": "Apply urea in 2-3 splits", | |
| "Cultural Practices": "Ensure proper drainage", | |
| "Crop-Specific Notes": "Critical at tillering and grain filling" | |
| } | |
| } | |
| } | |
| def load_model(self): | |
| """Load model with error handling""" | |
| try: | |
| # Model configuration | |
| base_model = "unsloth/gemma-3-1b-it" | |
| adapter_path = "./unified_crop_model" # Local path | |
| # Check if running on CPU or GPU | |
| device = "cuda" if torch.cuda.is_available() else "cpu" | |
| logger.info(f"🖥️ Using device: {device}") | |
| # Configure quantization only for GPU | |
| if device == "cuda": | |
| bnb_config = BitsAndBytesConfig( | |
| load_in_4bit=True, | |
| bnb_4bit_quant_type="nf4", | |
| bnb_4bit_use_double_quant=True, | |
| bnb_4bit_compute_dtype="bfloat16" | |
| ) | |
| self.model = AutoModelForCausalLM.from_pretrained( | |
| base_model, | |
| quantization_config=bnb_config, | |
| device_map="auto", | |
| trust_remote_code=True | |
| ) | |
| else: | |
| # CPU inference | |
| self.model = AutoModelForCausalLM.from_pretrained( | |
| base_model, | |
| torch_dtype=torch.float32, | |
| trust_remote_code=True | |
| ) | |
| # Try to load LoRA adapter | |
| if os.path.exists(adapter_path): | |
| try: | |
| self.model = PeftModel.from_pretrained( | |
| self.model, | |
| adapter_path, | |
| device_map="auto" if device == "cuda" else None | |
| ) | |
| logger.info("✅ LoRA adapter loaded") | |
| except Exception as e: | |
| logger.warning(f"⚠️ Could not load LoRA adapter: {str(e)}") | |
| logger.info("📝 Using base model without fine-tuning") | |
| else: | |
| logger.warning("⚠️ LoRA adapter not found, using base model") | |
| # Load tokenizer | |
| tokenizer_path = adapter_path if os.path.exists(adapter_path) else base_model | |
| self.tokenizer = AutoTokenizer.from_pretrained(tokenizer_path, trust_remote_code=True) | |
| # Set pad token if not exists | |
| if self.tokenizer.pad_token is None: | |
| self.tokenizer.pad_token = self.tokenizer.eos_token | |
| self.model_loaded = True | |
| logger.info("✅ Model loaded successfully!") | |
| except Exception as e: | |
| logger.error(f"❌ Failed to load model: {str(e)}") | |
| self.model_loaded = False | |
| def analyze_crop_conditions(self, crop, N, P, K, temp, humidity, pH, rainfall): | |
| """Analyze crop conditions with comprehensive error handling""" | |
| if not self.data_loaded: | |
| return "❌ Data not loaded properly. Please refresh the page." | |
| if not self.model_loaded: | |
| return "⚠️ Model not loaded. Providing basic analysis without AI recommendations." | |
| try: | |
| given = [N, P, K, temp, pH, rainfall, humidity] | |
| # Find crop in datasets | |
| if crop in self.df1['label'].values: | |
| df = self.df1[self.df1['label']==crop] | |
| elif not self.df2.empty and crop in self.df2['label'].values: | |
| df = self.df2[self.df2['label']==crop] | |
| else: | |
| available_crops = list(self.df1['label'].unique()) | |
| return f"❌ Crop '{crop}' not found in database. Available crops: {', '.join(available_crops)}" | |
| df_values = df.drop('label', axis=1) | |
| df_array = np.array(df_values) | |
| # MSE computation | |
| mse_list = [] | |
| for row in df_array: | |
| mse = np.mean((np.array(row) - np.array(given))**2) | |
| mse_list.append(mse) | |
| best_index = np.argmin(mse_list) | |
| required = df_array[best_index].tolist() | |
| problems, diff_dict = evaluate_problems_and_diffs(required, given) | |
| if not problems: | |
| return "✅ **Great!** No significant issues detected. Current conditions are within acceptable ranges for optimal growth." | |
| # ============================== | |
| # Detailed Default Template | |
| # ============================== | |
| default_template = { | |
| "general": { | |
| "nitrogen_deficiency": { | |
| "Description": "Leaves appear pale or yellowish; growth may be slow.", | |
| "Homemade/Natural Remedies": "Apply composted manure, cow dung, or green manure.", | |
| "Commercial Suggestions": "Use balanced NPK fertilizer with higher nitrogen content.", | |
| "Cultural Practices": "Rotate crops; avoid over-harvesting nitrogen-rich leaves.", | |
| "Crop-Specific Notes": "Sensitive crops like leafy greens show symptoms faster." | |
| }, | |
| "nitrogen_excess": { | |
| "Description": "Excessive vegetative growth; flowering/fruiting may be delayed.", | |
| "Homemade/Natural Remedies": "Limit nitrogen-rich organic inputs like fresh manure.", | |
| "Commercial Suggestions": "Reduce nitrogen fertilizer; maintain balanced NPK ratios.", | |
| "Cultural Practices": "Prune excess growth; monitor soil nutrient levels.", | |
| "Crop-Specific Notes": "Fruit crops may produce fewer fruits if over-fertilized with nitrogen." | |
| }, | |
| "phosphorus_deficiency": { | |
| "Description": "Stunted growth; leaves may show dark green/purplish coloration.", | |
| "Homemade/Natural Remedies": "Use bone meal, rock phosphate, or composted organic matter.", | |
| "Commercial Suggestions": "Apply phosphorus-rich fertilizers like single superphosphate (SSP).", | |
| "Cultural Practices": "Maintain soil pH around 6–7; avoid acidic soils.", | |
| "Crop-Specific Notes": "Root crops may be most affected due to poor root development." | |
| }, | |
| "phosphorus_excess": { | |
| "Description": "Can interfere with micronutrient absorption (Zn, Fe).", | |
| "Homemade/Natural Remedies": "Avoid adding extra phosphorus-containing amendments.", | |
| "Commercial Suggestions": "Use balanced fertilizers; avoid repeated high P applications.", | |
| "Cultural Practices": "Rotate crops to prevent phosphorus build-up.", | |
| "Crop-Specific Notes": "Cereals are more sensitive to high phosphorus than legumes." | |
| }, | |
| "potassium_deficiency": { | |
| "Description": "Leaf edges turn brown, scorching; weak stems.", | |
| "Homemade/Natural Remedies": "Add wood ash or composted banana peels.", | |
| "Commercial Suggestions": "Apply potassium sulfate or muriate of potash.", | |
| "Cultural Practices": "Ensure proper irrigation; avoid water stress.", | |
| "Crop-Specific Notes": "Potato and tomato show clear leaf-edge symptoms." | |
| }, | |
| "potassium_excess": { | |
| "Description": "May reduce magnesium and calcium uptake.", | |
| "Homemade/Natural Remedies": "Avoid excessive potassium-containing composts.", | |
| "Commercial Suggestions": "Balance with magnesium/calcium fertilizers.", | |
| "Cultural Practices": "Test soil regularly for K levels.", | |
| "Crop-Specific Notes": "Leafy vegetables may show interveinal chlorosis if Mg is low." | |
| }, | |
| "iron_deficiency": { | |
| "Description": "Young leaves turn yellow with green veins (chlorosis).", | |
| "Homemade/Natural Remedies": "Foliar spray with iron sulfate or iron chelates.", | |
| "Commercial Suggestions": "Apply chelated iron to soil or foliage.", | |
| "Cultural Practices": "Maintain soil pH below 7.5 for better uptake.", | |
| "Crop-Specific Notes": "Fruit trees like apple and citrus are sensitive." | |
| }, | |
| "iron_excess": { | |
| "Description": "Can cause nutrient imbalance and toxicity.", | |
| "Homemade/Natural Remedies": "Avoid iron-rich amendments in high-Fe soils.", | |
| "Commercial Suggestions": "Test soil before adding iron fertilizers.", | |
| "Cultural Practices": "Improve drainage in high-iron soils.", | |
| "Crop-Specific Notes": "Rice paddies may tolerate slightly higher iron naturally." | |
| }, | |
| "water_deficiency": { | |
| "Description": "Wilting, leaf curl, and reduced yield.", | |
| "Homemade/Natural Remedies": "Mulch soil to retain moisture; use organic matter.", | |
| "Commercial Suggestions": "Implement drip or sprinkler irrigation.", | |
| "Cultural Practices": "Schedule watering based on crop stage and weather.", | |
| "Crop-Specific Notes": "Tomatoes and peppers are highly sensitive to water stress." | |
| }, | |
| "water_excess": { | |
| "Description": "Root rot, yellowing leaves, poor aeration.", | |
| "Homemade/Natural Remedies": "Improve soil drainage using sand or organic matter.", | |
| "Commercial Suggestions": "Raised beds; controlled irrigation.", | |
| "Cultural Practices": "Avoid waterlogging; monitor soil moisture regularly.", | |
| "Crop-Specific Notes": "Root crops like carrots and potatoes are prone to rot." | |
| }, | |
| "pH_deficiency": { | |
| "Description": "Soil too acidic (<5.5); stunted growth.", | |
| "Homemade/Natural Remedies": "Apply wood ash or crushed eggshells.", | |
| "Commercial Suggestions": "Use agricultural lime to raise pH.", | |
| "Cultural Practices": "Test soil pH regularly; avoid acid-forming fertilizers.", | |
| "Crop-Specific Notes": "Legumes prefer slightly acidic to neutral pH." | |
| }, | |
| "pH_excess": { | |
| "Description": "Soil too alkaline (>8); micronutrient deficiencies.", | |
| "Homemade/Natural Remedies": "Incorporate organic matter like compost.", | |
| "Commercial Suggestions": "Apply elemental sulfur to lower soil pH.", | |
| "Cultural Practices": "Select tolerant crop varieties.", | |
| "Crop-Specific Notes": "Tomatoes and spinach are sensitive to high pH." | |
| }, | |
| "temperature_stress": { | |
| "Description": "Too high or too low temperature affects growth and yield.", | |
| "Homemade/Natural Remedies": "Shade nets or mulching to regulate temperature.", | |
| "Commercial Suggestions": "Use protective covers or greenhouses.", | |
| "Cultural Practices": "Plant at optimal seasonal windows.", | |
| "Crop-Specific Notes": "Tomato, cucumber, and leafy greens are sensitive." | |
| }, | |
| "pest_disease_issue": { | |
| "Description": "Presence of pests or disease symptoms.", | |
| "Homemade/Natural Remedies": "Neem oil, garlic extract, or organic sprays.", | |
| "Commercial Suggestions": "Use approved pesticides or fungicides; follow IPM.", | |
| "Cultural Practices": "Sanitation, crop rotation, resistant varieties.", | |
| "Crop-Specific Notes": "Leafy vegetables and solanaceous crops need regular monitoring." | |
| } | |
| } | |
| } | |
| # selected issues dictionary | |
| selected = {} | |
| # Step 1: Check crop-specific template first | |
| for prob in problems: | |
| if prob in self.template.get(crop, {}): | |
| selected[prob] = self.template[crop][prob] | |
| # Step 2: If nothing found, use default template | |
| if not selected: | |
| for prob in problems: | |
| if prob in default_template.get("general", {}): | |
| selected[prob] = default_template["general"][prob] | |
| # Step 3: If still nothing found, fallback message | |
| if not selected: | |
| issues_text = ', '.join(problems) | |
| return f"⚠️ **Issues detected:** {issues_text}\n\n❗ No recommendations available even in the default template." | |
| # Step 4: Build formatted output | |
| context = f"Crop: {crop}\n" | |
| for issue, details in selected.items(): | |
| context += f"\n## {issue.replace('_',' ').title()}\n" | |
| for k, v in details.items(): | |
| context += f"💠 {k}: {v}\n" | |
| # Generate AI recommendations if model available | |
| ai_response = "" | |
| if self.model_loaded: | |
| try: | |
| ai_response = self.generate_ai_recommendations(context) | |
| except Exception as e: | |
| logger.error(f"AI generation failed: {str(e)}") | |
| ai_response = "AI recommendations temporarily unavailable." | |
| # Format response | |
| issues_summary = f"📊 **Issues Detected:** {', '.join(problems)}\n\n" | |
| diff_summary = f"📈 **Parameter Differences:** {', '.join([f'{k}: {v:+.1f}' for k, v in diff_dict.items()])}\n\n" | |
| # template_info = "📋 **Available Information:**\n" | |
| # for issue, details in selected.items(): | |
| # template_info += f"\n**{issue.replace('_', ' ').title()}:**\n" | |
| # template_info += f"• Description: {details.get('Description', 'N/A')}\n" | |
| # template_info += f"• Natural Remedies: {details.get('Homemade/Natural Remedies', 'N/A')}\n" | |
| # template_info += f"• Commercial Solutions: {details.get('Commercial Suggestions', 'N/A')}\n\n" | |
| ai_section = f"🤖 **AI Recommendations:**\n{ai_response}\n" if ai_response else "" | |
| return f"{issues_summary}{ai_section}" | |
| except Exception as e: | |
| logger.error(f"Analysis error: {str(e)}") | |
| return f"❌ Error during analysis: {str(e)}" | |
| def generate_ai_recommendations(self, context): | |
| """Generate AI recommendations with proper error handling""" | |
| try: | |
| messages = [ | |
| { | |
| "role": "system", | |
| "content": [{"type": "text", "text": "You are a helpful agronomy assistant. Based on soil conditions, suggest remedies for the detected crop issues."}] | |
| }, | |
| { | |
| "role": "user", | |
| "content": [{"type": "text", "text": f"Here is reference info:\n{context}\n\nPlease give a concise recommendation."}] | |
| } | |
| ] | |
| inputs = self.tokenizer.apply_chat_template( | |
| messages, | |
| add_generation_prompt=True, | |
| return_tensors="pt", | |
| tokenize=True, | |
| return_dict=True, | |
| ).to(self.model.device) | |
| with torch.no_grad(): | |
| output = self.model.generate( | |
| **inputs, | |
| max_new_tokens=200, | |
| temperature=0.7, | |
| top_p=0.9, | |
| pad_token_id=self.tokenizer.eos_token_id, | |
| do_sample=True | |
| ) | |
| # Decode response | |
| response = self.tokenizer.decode( | |
| output[0][inputs['input_ids'].shape[1]:], | |
| skip_special_tokens=True | |
| ) | |
| return response.strip() | |
| except Exception as e: | |
| logger.error(f"AI generation error: {str(e)}") | |
| return f"AI recommendations temporarily unavailable due to: {str(e)}" | |
| # Initialize advisor with error handling | |
| logger.info("🚀 Initializing Agricultural Advisor...") | |
| try: | |
| advisor = AgriculturalAdvisor() | |
| initialization_status = "✅ System Ready" | |
| crops_available = list(advisor.df1['label'].unique()) | |
| except Exception as e: | |
| logger.error(f"❌ Failed to initialize advisor: {str(e)}") | |
| advisor = None | |
| initialization_status = f"❌ Initialization Failed: {str(e)}" | |
| crops_available = ["rice", "wheat", "maize"] # Fallback | |
| # def get_crop_recommendations(crop, N, P, K, temperature, humidity, pH, rainfall): | |
| # """Gradio interface function""" | |
| # if advisor is None: | |
| # return f"❌ System not initialized properly. Status: {initialization_status}" | |
| # try: | |
| # return advisor.analyze_crop_conditions( | |
| # crop, N, P, K, temperature, humidity, pH, rainfall | |
| # ) | |
| # except Exception as e: | |
| # logger.error(f"Interface error: {str(e)}") | |
| # return f"❌ Error processing request: {str(e)}" | |
| ## Pydantic models for API | |
| class CropAnalysisRequest(BaseModel): | |
| crop: str = Field(..., description="Name of the crop to analyze") | |
| N: float = Field(..., ge=0, le=300, description="Nitrogen content (kg/ha)") | |
| P: float = Field(..., ge=0, le=150, description="Phosphorus content (kg/ha)") | |
| K: float = Field(..., ge=0, le=200, description="Potassium content (kg/ha)") | |
| temperature: float = Field(..., ge=0, le=50, description="Temperature (°C)") | |
| humidity: float = Field(..., ge=0, le=100, description="Humidity (%)") | |
| pH: float = Field(..., ge=3, le=10, description="Soil pH level") | |
| rainfall: float = Field(..., ge=0, le=2000, description="Rainfall (mm)") | |
| class CropAnalysisResponse(BaseModel): | |
| success: bool | |
| message: str | |
| recommendations: str | |
| status: str | |
| class SystemStatusResponse(BaseModel): | |
| status: str | |
| model_loaded: bool | |
| data_loaded: bool | |
| available_crops: List[str] | |
| model_config = { | |
| "protected_namespaces": () | |
| } | |
| # Initialize advisor with error handling | |
| logger.info("🚀 Initializing Agricultural Advisor...") | |
| try: | |
| advisor = AgriculturalAdvisor() | |
| initialization_status = "✅ System Ready" | |
| crops_available = list(advisor.df1['label'].unique()) | |
| except Exception as e: | |
| logger.error(f"❌ Failed to initialize advisor: {str(e)}") | |
| advisor = None | |
| initialization_status = f"❌ Initialization Failed: {str(e)}" | |
| crops_available = ["rice", "wheat", "maize"] # Fallback | |
| # FastAPI app | |
| app = FastAPI( | |
| title="🌾 Agricultural Advisor API", | |
| description="AI-powered agricultural advisor for crop recommendations based on soil and climate conditions", | |
| version="1.0.0" | |
| ) | |
| # CORS middleware | |
| app.add_middleware( | |
| CORSMiddleware, | |
| allow_origins=["*"], # Configure as needed for production | |
| allow_credentials=True, | |
| allow_methods=["*"], | |
| allow_headers=["*"], | |
| ) | |
| async def root(): | |
| """Serve basic HTML interface""" | |
| html_content = """ | |
| <!DOCTYPE html> | |
| <html> | |
| <head> | |
| <title>🌾 Agricultural Advisor API</title> | |
| <style> | |
| body { font-family: Arial, sans-serif; margin: 40px; background: #f5f5f5; } | |
| .container { max-width: 800px; margin: 0 auto; background: white; padding: 30px; border-radius: 10px; box-shadow: 0 2px 10px rgba(0,0,0,0.1); } | |
| h1 { color: #2e7d32; text-align: center; } | |
| .endpoint { background: #f8f9fa; padding: 15px; margin: 10px 0; border-radius: 5px; border-left: 4px solid #4caf50; } | |
| .method { color: #1976d2; font-weight: bold; } | |
| .example { background: #e8f5e8; padding: 10px; margin: 10px 0; border-radius: 5px; font-family: monospace; } | |
| pre { overflow-x: auto; } | |
| </style> | |
| </head> | |
| <body> | |
| <div class="container"> | |
| <h1>🌾 Agricultural Advisor API</h1> | |
| <p>AI-powered agricultural advisor for crop recommendations based on soil and climate conditions.</p> | |
| <h2>📋 Available Endpoints</h2> | |
| <div class="endpoint"> | |
| <span class="method">GET</span> <strong>/status</strong> | |
| <p>Get system status and available crops</p> | |
| </div> | |
| <div class="endpoint"> | |
| <span class="method">POST</span> <strong>/analyze</strong> | |
| <p>Analyze crop conditions and get recommendations</p> | |
| <div class="example"> | |
| <strong>Example Request:</strong> | |
| <pre>{ | |
| "crop": "rice", | |
| "N": 80, | |
| "P": 40, | |
| "K": 67, | |
| "temperature": 25, | |
| "humidity": 60, | |
| "pH": 7.0, | |
| "rainfall": 200 | |
| }</pre> | |
| </div> | |
| </div> | |
| <div class="endpoint"> | |
| <span class="method">GET</span> <strong>/crops</strong> | |
| <p>Get list of available crops</p> | |
| </div> | |
| <h2>📖 Documentation</h2> | |
| <p>Visit <a href="/docs">/docs</a> for interactive API documentation</p> | |
| <p>Visit <a href="/redoc">/redoc</a> for alternative documentation</p> | |
| <h2>🔧 System Status</h2> | |
| <p><strong>Status:</strong> """ + initialization_status + """</p> | |
| <p><strong>Available Crops:</strong> """ + ", ".join(crops_available) + """</p> | |
| </div> | |
| </body> | |
| </html> | |
| """ | |
| return HTMLResponse(content=html_content) | |
| async def get_system_status(): | |
| """Get system status""" | |
| if advisor is None: | |
| return SystemStatusResponse( | |
| status=initialization_status, | |
| model_loaded=False, | |
| data_loaded=False, | |
| available_crops=crops_available | |
| ) | |
| return SystemStatusResponse( | |
| status=initialization_status, | |
| model_loaded=advisor.model_loaded, | |
| data_loaded=advisor.data_loaded, | |
| available_crops=crops_available | |
| ) | |
| async def get_available_crops(): | |
| """Get list of available crops""" | |
| return {"crops": crops_available} | |
| async def analyze_crop(request: CropAnalysisRequest): | |
| """Analyze crop conditions and provide recommendations""" | |
| if advisor is None: | |
| raise HTTPException( | |
| status_code=503, | |
| detail=f"System not initialized properly. Status: {initialization_status}" | |
| ) | |
| try: | |
| # Validate crop | |
| if request.crop not in crops_available: | |
| raise HTTPException( | |
| status_code=400, | |
| detail=f"Crop '{request.crop}' not available. Available crops: {', '.join(crops_available)}" | |
| ) | |
| # Analyze crop conditions using the same method as Gradio version | |
| recommendations = advisor.analyze_crop_conditions( | |
| request.crop, request.N, request.P, request.K, | |
| request.temperature, request.humidity, request.pH, request.rainfall | |
| ) | |
| # Determine status based on recommendations | |
| if "❌" in recommendations: | |
| status = "error" | |
| elif "⚠️" in recommendations: | |
| status = "warning" | |
| elif "✅" in recommendations: | |
| status = "success" | |
| else: | |
| status = "info" | |
| return CropAnalysisResponse( | |
| success=True, | |
| message="Analysis completed successfully", | |
| recommendations=recommendations, | |
| status=status | |
| ) | |
| except HTTPException: | |
| raise | |
| except Exception as e: | |
| logger.error(f"Analysis error: {str(e)}") | |
| raise HTTPException( | |
| status_code=500, | |
| detail=f"Error processing request: {str(e)}" | |
| ) | |
| async def health_check(): | |
| """Health check endpoint""" | |
| return { | |
| "status": "healthy", | |
| "system_status": initialization_status, | |
| "timestamp": pd.Timestamp.now().isoformat() | |
| } | |
| if __name__ == "__main__": | |
| uvicorn.run( | |
| app, | |
| host="0.0.0.0", | |
| port=8000, | |
| log_level="info" | |
| ) |