EvSentry8 / app.py
Jeff28's picture
Updated app.py with Groq API integration for improved AI assistant
ee1f474 verified
import os
import numpy as np
import tensorflow as tf
from tensorflow.keras.preprocessing import image
import gradio as gr
import requests
import json
# Suppress TensorFlow warnings
os.environ["TF_CPP_MIN_LOG_LEVEL"] = "3"
device = "cuda" if tf.test.is_gpu_available() else "cpu"
print(f"Running on: {device.upper()}")
# Groq API key for AI assistant
GROQ_API_KEY = "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"
GROQ_MODEL = "llama3-70b-8192" # Using Llama 3 70B model
# Fallback to Hugging Face token if Groq fails
HF_API_TOKEN = os.getenv("HUGGINGFACE_TOKEN")
print(f"API tokens available: Groq=Yes, HF={'Yes' if HF_API_TOKEN else 'No'}")
# Load the trained tomato disease detection model
model = tf.keras.models.load_model("Tomato_Leaf_Disease_Model.h5")
# Disease categories
class_labels = [
"Tomato Bacterial Spot",
"Tomato Early Blight",
"Tomato Late Blight",
"Tomato Mosaic Virus",
"Tomato Yellow Leaf Curl Virus"
]
# Disease information database (fallback if API fails)
disease_info = {
"Tomato Bacterial Spot": {
"description": "A bacterial disease that causes small, dark spots on leaves, stems, and fruits.",
"causes": "Caused by Xanthomonas bacteria, spread by water splash, contaminated tools, and seeds.",
"recommendations": [
"Remove and destroy infected plants",
"Rotate crops with non-solanaceous plants",
"Use copper-based fungicides",
"Avoid overhead irrigation"
]
},
"Tomato Early Blight": {
"description": "A fungal disease that causes dark spots with concentric rings on lower leaves first.",
"causes": "Caused by Alternaria solani fungus, favored by warm, humid conditions.",
"recommendations": [
"Remove infected leaves promptly",
"Improve air circulation around plants",
"Apply fungicides preventatively",
"Mulch around plants to prevent soil splash"
]
},
"Tomato Late Blight": {
"description": "A devastating fungal disease that causes dark, water-soaked lesions on leaves and fruits.",
"causes": "Caused by Phytophthora infestans, favored by cool, wet conditions.",
"recommendations": [
"Remove and destroy infected plants immediately",
"Apply fungicides preventatively in humid conditions",
"Improve drainage and air circulation",
"Plant resistant varieties when available"
]
},
"Tomato Mosaic Virus": {
"description": "A viral disease that causes mottled green/yellow patterns on leaves and stunted growth.",
"causes": "Caused by tobacco mosaic virus (TMV), spread by handling, tools, and sometimes seeds.",
"recommendations": [
"Remove and destroy infected plants",
"Wash hands and tools after handling infected plants",
"Control insect vectors like aphids",
"Plant resistant varieties"
]
},
"Tomato Yellow Leaf Curl Virus": {
"description": "A viral disease transmitted by whiteflies that causes yellowing and curling of leaves.",
"causes": "Caused by a begomovirus, transmitted primarily by whiteflies.",
"recommendations": [
"Use whitefly control measures",
"Remove and destroy infected plants",
"Use reflective mulches to repel whiteflies",
"Plant resistant varieties"
]
}
}
# Image preprocessing function
def preprocess_image(img):
img = img.resize((224, 224)) # Resize for model input
img = image.img_to_array(img) / 255.0 # Normalize
return np.expand_dims(img, axis=0) # Add batch dimension
# Temperature Scaling: Adjusts predictions using a temperature parameter.
def apply_temperature_scaling(prediction, temperature):
# Avoid log(0) by adding a small epsilon
eps = 1e-8
scaled_logits = np.log(np.maximum(prediction, eps)) / temperature
exp_logits = np.exp(scaled_logits)
scaled_probs = exp_logits / np.sum(exp_logits)
return scaled_probs
# Min-Max Normalization: Scales the raw confidence based on provided min and max values.
def apply_min_max_scaling(confidence, min_conf, max_conf):
norm = (confidence - min_conf) / (max_conf - min_conf) * 100
norm = np.clip(norm, 0, 100)
return norm
# Call Groq API for AI assistant
def call_groq_api(prompt):
"""Call Groq API for detailed disease analysis and advice"""
headers = {
"Authorization": f"Bearer {GROQ_API_KEY}",
"Content-Type": "application/json"
}
payload = {
"model": GROQ_MODEL,
"messages": [
{"role": "system", "content": "You are an expert agricultural advisor specializing in tomato farming and plant diseases."},
{"role": "user", "content": prompt}
],
"max_tokens": 800,
"temperature": 0.7
}
try:
response = requests.post(
"https://api.groq.com/openai/v1/chat/completions",
headers=headers,
json=payload,
timeout=30
)
if response.status_code == 200:
result = response.json()
if "choices" in result and len(result["choices"]) > 0:
return result["choices"][0]["message"]["content"]
print(f"Groq API error: {response.status_code} - {response.text}")
return None
except Exception as e:
print(f"Error with Groq API: {str(e)}")
return None
# Fallback to Hugging Face if Groq fails
def call_hf_model(prompt, model_id="mistralai/Mistral-7B-Instruct-v0.2"):
"""Call an AI model on Hugging Face for detailed disease analysis."""
if not HF_API_TOKEN:
return None
headers = {"Authorization": f"Bearer {HF_API_TOKEN}"}
# Format prompt for instruction-tuned models
formatted_prompt = f"""<s>[INST] {prompt} [/INST]"""
payload = {
"inputs": formatted_prompt,
"parameters": {
"max_new_tokens": 500,
"temperature": 0.7,
"top_p": 0.95,
"do_sample": True
}
}
url = f"https://api-inference.huggingface.co/models/{model_id}"
try:
response = requests.post(url, headers=headers, json=payload, timeout=30)
if response.status_code == 200:
result = response.json()
if isinstance(result, list) and len(result) > 0:
if "generated_text" in result[0]:
# Extract just the response part (after the prompt)
generated_text = result[0]["generated_text"]
# Remove the prompt from the response
response_text = generated_text.split("[/INST]")[-1].strip()
return response_text
return None
except Exception as e:
print(f"Exception when calling HF model: {str(e)}")
return None
# Combined AI model call with fallback
def call_ai_model(prompt):
"""Call AI models with fallback mechanisms"""
# Try Groq first
response = call_groq_api(prompt)
if response:
return response
# If Groq fails, try Hugging Face
response = call_hf_model(prompt)
if response:
return response
# If both fail, return fallback message
return "Sorry, I'm having trouble connecting to the AI service. Using fallback information instead."
# Generate AI response for disease analysis
def generate_ai_response(disease_name, confidence):
"""Generate a detailed AI response about the detected disease."""
# Get fallback information in case AI call fails
info = disease_info.get(disease_name, {
"description": "Information not available for this disease.",
"causes": "Unknown causes.",
"recommendations": ["Consult with a local agricultural extension service."]
})
# Create prompt for AI model
prompt = (
f"You are an agricultural expert advisor. A tomato plant disease has been detected: {disease_name} "
f"with {confidence:.2f}% confidence. "
f"Provide a detailed analysis including: "
f"1) A brief description of the disease "
f"2) What causes it and how it spreads "
f"3) The impact on tomato plants and yield "
f"4) Detailed treatment options (both organic and chemical) "
f"5) Prevention strategies for future crops "
f"Format your response in clear sections with bullet points where appropriate."
)
# Call AI model with fallback mechanisms
ai_response = call_ai_model(prompt)
# If AI response contains error message, use fallback information
if "Sorry, I'm having trouble" in ai_response:
ai_response = f"""
# Disease: {disease_name}
## Description
{info['description']}
## Causes
{info.get('causes', 'Information not available.')}
## Recommended Treatment
{chr(10).join(f"- {rec}" for rec in info['recommendations'])}
*Note: This is fallback information. For more detailed advice, please try again later when the AI service is available.*
"""
return ai_response
# Chat with agricultural expert
def chat_with_expert(message, chat_history):
"""Handle chat interactions with farmers about agricultural topics."""
if not message.strip():
return "", chat_history
# Prepare context from chat history - use last 3 exchanges for context to avoid token limits
context = "\n".join([f"Farmer: {q}\nExpert: {a}" for q, a in chat_history[-3:]])
prompt = (
f"You are an expert agricultural advisor specializing in tomato farming and plant diseases. "
f"You provide helpful, accurate, and practical advice to farmers. "
f"Always be respectful and considerate of farmers' knowledge while providing expert guidance. "
f"If you're unsure about something, acknowledge it and provide the best information you can. "
f"Previous conversation:\n{context}\n\n"
f"Farmer's new question: {message}\n\n"
f"Provide a helpful, informative response about farming, focusing on tomatoes if relevant."
)
# Call AI model with fallback mechanisms
response = call_ai_model(prompt)
# If AI response contains error message, use fallback response
if "Sorry, I'm having trouble" in response:
response = "I apologize, but I'm having trouble connecting to my knowledge base at the moment. Please try again later, or ask a different question about tomato farming or plant diseases."
chat_history.append((message, response))
return "", chat_history
# Main detection function with adjustable confidence scaling
def detect_disease_scaled(img, scaling_method, temperature, min_conf, max_conf):
processed_img = preprocess_image(img)
prediction = model.predict(processed_img)[0] # Get prediction for single image
raw_confidence = np.max(prediction) * 100
class_idx = np.argmax(prediction)
disease_name = class_labels[class_idx]
if scaling_method == "Temperature Scaling":
scaled_probs = apply_temperature_scaling(prediction, temperature)
adjusted_confidence = np.max(scaled_probs) * 100
elif scaling_method == "Min-Max Normalization":
adjusted_confidence = apply_min_max_scaling(raw_confidence, min_conf, max_conf)
else:
adjusted_confidence = raw_confidence
# Generate AI response
ai_response = generate_ai_response(disease_name, adjusted_confidence)
# Return results
result = f"{disease_name} (Confidence: {adjusted_confidence:.2f}%)"
raw_text = f"Raw Confidence: {raw_confidence:.2f}%"
return result, raw_text, ai_response
# Simplified Gradio UI for better compatibility
with gr.Blocks() as demo:
gr.Markdown("# 🍅 EvSentry8: Tomato Disease Detection with AI Assistant")
with gr.Tab("Disease Detection"):
with gr.Row():
with gr.Column():
image_input = gr.Image(type="pil", label="Upload a Tomato Leaf Image")
scaling_method = gr.Radio(
["Temperature Scaling", "Min-Max Normalization"],
label="Confidence Scaling Method",
value="Temperature Scaling"
)
temperature_slider = gr.Slider(0.5, 2.0, step=0.1, label="Temperature", value=1.0)
min_conf_slider = gr.Slider(0, 100, step=1, label="Min Confidence", value=20)
max_conf_slider = gr.Slider(0, 100, step=1, label="Max Confidence", value=90)
detect_button = gr.Button("Detect Disease")
with gr.Column():
disease_output = gr.Textbox(label="Detected Disease & Adjusted Confidence")
raw_confidence_output = gr.Textbox(label="Raw Confidence")
ai_response_output = gr.Markdown(label="AI Assistant's Analysis & Recommendations")
with gr.Tab("Chat with Expert"):
gr.Markdown("# 💬 Chat with Agricultural Expert")
gr.Markdown("Ask any questions about tomato farming, diseases, or agricultural practices.")
chatbot = gr.Chatbot(height=400)
with gr.Row():
chat_input = gr.Textbox(
label="Your Question",
placeholder="Ask about tomato farming, diseases, or agricultural practices...",
lines=2
)
chat_button = gr.Button("Send")
gr.Markdown("""
### Example Questions:
- How do I identify tomato bacterial spot?
- What's the best way to prevent late blight?
- How often should I water my tomato plants?
- What are the signs of nutrient deficiency in tomatoes?
""")
# Set up event handlers
detect_button.click(
detect_disease_scaled,
inputs=[image_input, scaling_method, temperature_slider, min_conf_slider, max_conf_slider],
outputs=[disease_output, raw_confidence_output, ai_response_output]
)
# Chat functionality
chat_button.click(
fn=chat_with_expert,
inputs=[chat_input, chatbot],
outputs=[chat_input, chatbot]
)
# Also allow pressing Enter to send chat
chat_input.submit(
fn=chat_with_expert,
inputs=[chat_input, chatbot],
outputs=[chat_input, chatbot]
)
demo.launch()