Spaces:
Sleeping
Sleeping
File size: 13,378 Bytes
7fc7a89 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 | import os
import json
import re
import logging
from datetime import datetime, timedelta
import random
from flask import (
Flask, render_template, request, jsonify, session,
make_response, redirect, url_for
)
from werkzeug.middleware.proxy_fix import ProxyFix
from flask_session import Session
from cachelib import SimpleCache
from dotenv import load_dotenv
load_dotenv()
# Logging config
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
# API Keys
GEMINI_API_KEY = os.getenv("GEMINI_API_KEY") or os.getenv("GEMINI_API")
NVIDIA_API_KEY = os.getenv("NVIDIA_API_KEY")
# Initialize Gemini if available
if GEMINI_API_KEY:
import google.generativeai as genai
genai.configure(api_key=GEMINI_API_KEY)
# Initialize NVIDIA client if available
nvidia_client = None
if NVIDIA_API_KEY:
try:
from openai import OpenAI
nvidia_client = OpenAI(
base_url="https://integrate.api.nvidia.com/v1",
api_key=NVIDIA_API_KEY
)
except ImportError:
logger.warning("OpenAI library not installed for NVIDIA fallback")
app = Flask(__name__)
app.secret_key = os.getenv("SECRET_KEY", "pestipedia-secret-key-2024")
app.wsgi_app = ProxyFix(app.wsgi_app, x_for=1, x_proto=1, x_host=1)
app.config["SESSION_TYPE"] = "cachelib"
app.config["SESSION_CACHELIB"] = SimpleCache()
app.config["SESSION_PERMANENT"] = True
app.config["PERMANENT_SESSION_LIFETIME"] = timedelta(days=30)
disable_secure = os.getenv("DISABLE_SECURE_COOKIE", "0") in ("1", "true", "True")
app.config["SESSION_COOKIE_SAMESITE"] = "None"
app.config["SESSION_COOKIE_SECURE"] = False if disable_secure else True
app.config["SESSION_COOKIE_NAME"] = os.getenv("SESSION_COOKIE_NAME", "hf_app_session")
Session(app)
# Simple in-memory cache
cache = SimpleCache()
# Supported languages
SUPPORTED_LANGUAGES = {
"en": "English", "hi": "हिंदी (Hindi)", "bn": "বাংলা (Bengali)", "te": "తెలుగు (Telugu)",
"mr": "मराठी (Marathi)", "ta": "தமிழ் (Tamil)", "gu": "ગુજરાતી (Gujarati)", "ur": "اردو (Urdu)",
"kn": "ಕನ್ನಡ (Kannada)", "or": "ଓଡ଼ିଆ (Odia)", "ml": "മലയാളം (Malayalam)"
}
PESTS_DISEASES = [
{"id": 1, "name": "Fall Armyworm", "type": "pest", "crop": "Maize, Sorghum", "image_url": "/static/images/fall_armyworm.jpg"},
{"id": 2, "name": "Brown Plant Hopper", "type": "pest", "crop": "Rice", "image_url": "/static/images/brown_planthopper.jpg"},
{"id": 3, "name": "Rice Blast", "type": "disease", "crop": "Rice", "image_url": "/static/images/rice_blast.jpg"},
{"id": 4, "name": "Bacterial Leaf Blight", "type": "disease", "crop": "Rice", "image_url": "/static/images/bacterial_leaf_blight.jpg"},
{"id": 5, "name": "Pink Bollworm", "type": "pest", "crop": "Cotton", "image_url": "/static/images/stem_borer.jpg"},
{"id": 6, "name": "Red Cotton Bug", "type": "pest", "crop": "Cotton", "image_url": "/static/images/jassids.jpg"},
{"id": 7, "name": "Powdery Mildew", "type": "disease", "crop": "Wheat, Grapes", "image_url": "/static/images/powdery_mildew.jpg"},
{"id": 8, "name": "Tomato Fruit Borer", "type": "pest", "crop": "Tomato, Cotton", "image_url": "/static/images/aphids.jpg"},
{"id": 9, "name": "Late Blight", "type": "disease", "crop": "Potato, Tomato", "image_url": "/static/images/yellow_mosaic.jpg"},
{"id": 10, "name": "Downy Mildew", "type": "disease", "crop": "Grapes, Onion", "image_url": "/static/images/downy_mildew.jpg"},
{"id": 11, "name": "Whitefly", "type": "pest", "crop": "Tomato, Cotton", "image_url": "/static/images/whitefly.jpg"},
{"id": 12, "name": "Fusarium Wilt", "type": "disease", "crop": "Banana, Tomato", "image_url": "/static/images/fusarium_wilt.jpg"}
]
def extract_json_from_response(content: str):
"""Extract JSON from model response."""
content = content.strip()
json_match = re.search(r'\{[\s\S]*\}', content)
if json_match:
try:
return json.loads(json_match.group())
except json.JSONDecodeError:
pass
try:
return json.loads(content)
except json.JSONDecodeError:
return {}
def call_llm(prompt):
"""Call LLM with Google (Gemini + Gemma) -> NVIDIA (Llama) fallback."""
# 1. Google API: Gemini + Gemma models
if GEMINI_API_KEY:
google_models = [
"gemini-2.5-flash",
"gemini-2.5-flash-lite",
"gemma-3-27b-it",
"gemma-3-12b-it",
"gemma-3-4b-it",
"gemma-3-1b-it"
]
for model_name in google_models:
try:
logger.info(f" >> Trying Google {model_name}...")
model = genai.GenerativeModel(model_name)
response = model.generate_content(prompt)
if response and response.text:
logger.info(f" >> SUCCESS with {model_name}")
return response.text
except Exception as e:
logger.warning(f" >> {model_name} failed: {e}")
continue
# 2. NVIDIA API: Llama models only
if nvidia_client:
nvidia_models = ["meta/llama-3.1-405b-instruct", "meta/llama-3.1-70b-instruct"]
for model_name in nvidia_models:
try:
logger.info(f" >> Trying NVIDIA {model_name}...")
response = nvidia_client.chat.completions.create(
model=model_name,
messages=[{"role": "user", "content": prompt}],
max_tokens=4096,
temperature=0.3
)
logger.info(f" >> SUCCESS with {model_name}")
return response.choices[0].message.content
except Exception as e:
logger.warning(f" >> {model_name} failed: {e}")
continue
return None
@app.route("/")
def index():
language = session.get("language", "en")
return render_template(
"index.html",
pests_diseases=PESTS_DISEASES,
languages=SUPPORTED_LANGUAGES,
current_language=language
)
@app.route("/set_language", methods=["POST"])
def set_language():
lang = request.form.get("language", "en")
if lang in SUPPORTED_LANGUAGES:
session["language"] = lang
session.permanent = True
resp = make_response(redirect(url_for("index")))
resp.set_cookie("language", lang, max_age=60*60*24*365, samesite="None", secure=not disable_secure)
return resp
@app.route("/get_details/<int:pest_id>")
def get_details(pest_id):
lang_param = request.args.get("lang")
language = lang_param or session.get("language") or request.cookies.get("language") or "en"
session["language"] = language
session.permanent = True
pest = next((p for p in PESTS_DISEASES if p["id"] == pest_id), None)
if not pest:
return jsonify({"error": "Not found"}), 404
cache_key = f"pest_{pest_id}_{language}"
cached = cache.get(cache_key)
if cached:
logger.info("Cache hit for pest %s (%s)", pest_id, language)
return jsonify(cached)
# Build prompt with EXPLICIT JSON EXAMPLE
lang_instructions = {
"en": "Respond in English",
"hi": "हिंदी में जवाब दें (Respond in Hindi)",
}
prompt = f"""
{lang_instructions.get(language, 'Respond in English')}
Provide detailed information about the agricultural {pest['type']} '{pest['name']}' affecting '{pest['crop']}' in India.
Include these sections: Description, Lifecycle, Symptoms, Economic Impact, Management (Organic and Chemical), Prevention.
YOU MUST FORMAT YOUR RESPONSE AS A SINGLE, VALID JSON OBJECT EXACTLY LIKE THIS EXAMPLE:
{{
"description": {{"title": "Description", "text": "..."}},
"lifecycle": {{"title": "Lifecycle", "text": "..."}},
"symptoms": {{"title": "Symptoms", "text": "..."}},
"impact": {{"title": "Economic Impact", "text": "..."}},
"management": {{
"organic": {{"title": "Organic Control", "text": "Detailed organic and biological control methods..."}},
"chemical": {{"title": "Chemical Control", "text": "Recommended insecticides and fungicides..."}}
}},
"prevention": {{"title": "Prevention", "text": "..."}}
}}
IMPORTANT: The "management" key MUST have TWO sub-keys: "organic" and "chemical", each with "title" and "text".
Return ONLY the JSON object, no markdown formatting, no code blocks.
"""
logger.info(f"[Pestipedia] Fetching details for {pest['name']} in {language}")
response_text = call_llm(prompt)
# --- ROBUST FALLBACK (MOCK DATA) ---
if not response_text:
logger.warning("All LLM models failed; returning High-Quality Mock")
mock_details = {
"description": {
"title": "Scientific Diagnosis",
"text": f"The {pest['name']} is a significant agricultural threat in Indian subcontinents. It is characterized by specific patterns on the leaves/fruit. Early detection is critical."
},
"lifecycle": {
"title": "Life Cycle Analysis",
"text": "The lifecycle typically spans 30-45 days, moving from egg -> larva -> pupa -> adult. Rapid reproduction occurs in humid conditions (25-30°C)."
},
"symptoms": {
"title": "Clinical Symptoms",
"text": "Look for: 1. Yellowing of leaves. 2. Stunted growth. 3. Visible lesions on the stem or fruit. 4. Reduced yield quality."
},
"impact": {
"title": "Economic Forecast",
"text": "Can cause yield losses of up to 40-70% if untreated. Reduces market value significantly due to cosmetic damage."
},
"management": {
"organic": {
"title": "Organic Control",
"text": "**Biological Control:** Use Neem Oil (5ml/L), Pheromone traps (10/acre), and introduce natural predators like Trichogramma. **Cultural Practices:** Crop rotation, deep summer ploughing, and field sanitation."
},
"chemical": {
"title": "Chemical Control",
"text": "**Recommended Insecticides:** Apply Chlorpyrifos 20EC (2ml/L) or Imidacloprid 17.8SL (0.3ml/L) during early infestation. **Application:** Spray in the early morning or late evening for best results. Always follow label instructions."
}
},
"prevention": {
"title": "Preventive Strategy",
"text": "1. Crop rotation. 2. Use resistant varieties. 3. Deep summer ploughing to destroy pupae. 4. Maintain field sanitation."
}
}
result = {
**pest,
"details": mock_details,
"language": language,
"cached_at": datetime.utcnow().strftime("%Y-%m-%d %H:%M:%S UTC")
}
return jsonify(result)
detailed_info = extract_json_from_response(response_text)
# Validate and fix keys (including nested management)
required_keys = ["description", "lifecycle", "symptoms", "impact", "prevention"]
for key in required_keys:
if key not in detailed_info or "text" not in detailed_info.get(key, {}):
detailed_info[key] = {"title": key.capitalize(), "text": "Details unavailable at this moment."}
# Fix management structure if AI didn't follow instructions
if "management" not in detailed_info:
detailed_info["management"] = {}
mgmt = detailed_info["management"]
# If AI returned flat text, convert it
if "text" in mgmt and "organic" not in mgmt:
old_text = mgmt.get("text", "")
detailed_info["management"] = {
"organic": {"title": "Organic Control", "text": old_text if old_text else "No organic control data available."},
"chemical": {"title": "Chemical Control", "text": "No chemical control data available."}
}
else:
# Ensure organic and chemical keys exist
if "organic" not in mgmt or "text" not in mgmt.get("organic", {}):
mgmt["organic"] = {"title": "Organic Control", "text": "No organic control data available."}
if "chemical" not in mgmt or "text" not in mgmt.get("chemical", {}):
mgmt["chemical"] = {"title": "Chemical Control", "text": "No chemical control data available."}
result = {
**pest,
"details": detailed_info,
"language": language,
"cached_at": datetime.utcnow().strftime("%Y-%m-%d %H:%M:%S UTC")
}
cache.set(cache_key, result)
return jsonify(result)
if __name__ == "__main__":
# Changed PORT to 5006 to avoid conflicts
port = int(os.environ.get("PORT", 5006))
host = os.environ.get("HOST", "0.0.0.0")
app.run(host=host, port=port, debug=True)
|