FarMVi8ioN / app.py
Jeff28's picture
Update app.py with Groq API integration
48f68ce verified
raw
history blame
22.3 kB
import os
import gradio as gr
import numpy as np
import requests
import json
import time
from tensorflow.keras.models import load_model
from PIL import Image
# ===== API Configuration =====
# Try to get API tokens from environment variables
HF_API_TOKEN = os.getenv("HUGGINGFACE_TOKEN") # Hugging Face API token
GROQ_API_KEY = os.getenv("GROQ_API_KEY") # Groq API key
OPENAI_API_KEY = os.getenv("OPENAI_API_KEY") # OpenAI API key (fallback)
print(f"API tokens available: HF={'Yes' if HF_API_TOKEN else 'No'}, Groq={'Yes' if GROQ_API_KEY else 'No'}, OpenAI={'Yes' if OPENAI_API_KEY else 'No'}")
# ===== Disease Information Database =====
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.",
"treatment": [
"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.",
"treatment": [
"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.",
"treatment": [
"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.",
"treatment": [
"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.",
"treatment": [
"Use whitefly control measures",
"Remove and destroy infected plants",
"Use reflective mulches to repel whiteflies",
"Plant resistant varieties"
]
},
"Tomato___Target_Spot": {
"description": "A fungal disease causing circular lesions with concentric rings on leaves, stems, and fruits.",
"causes": "Caused by Corynespora cassiicola fungus, favored by warm, humid conditions.",
"treatment": [
"Remove infected plant parts",
"Improve air circulation",
"Apply fungicides at first sign of disease",
"Avoid overhead irrigation"
]
},
"Tomato___Bacterial_spot": {
"description": "A bacterial disease causing small, dark, water-soaked spots on leaves, stems, and fruits.",
"causes": "Caused by Xanthomonas species, spread by water splash and contaminated tools.",
"treatment": [
"Remove infected plant debris",
"Use copper-based bactericides",
"Rotate crops",
"Use disease-free seeds and transplants"
]
},
"Tomato___healthy": {
"description": "The plant shows no signs of disease and appears to be in good health.",
"causes": "Proper growing conditions, good management practices, and disease prevention.",
"treatment": [
"Continue regular watering and fertilization",
"Monitor for early signs of disease",
"Maintain good air circulation",
"Practice crop rotation"
]
}
}
# ===== Load Trained Models =====
model_a = load_model("Tomato_Leaf_Disease_Model.h5")
model_b = load_model("tomato_leaf_model_final(77%).h5")
classifier_model = load_model("tomato_leaf_classifier_optimized.h5")
# ===== Preprocessing Function =====
def preprocess_image(image, target_size=(224, 224)):
# Ensure the image is resized and normalized.
if isinstance(image, Image.Image):
img = image.resize(target_size)
else:
img = Image.fromarray(image).resize(target_size)
img_array = np.array(img) / 255.0
img_array = np.expand_dims(img_array, axis=0)
return img_array
# ===== Disease Label Mappings =====
# Model A labels
disease_labels_a = {
0: "Tomato Bacterial Spot",
1: "Tomato Early Blight",
2: "Tomato Late Blight",
3: "Tomato Mosaic Virus",
4: "Tomato Yellow Leaf Curl Virus"
}
# Model B labels
disease_labels_b = {
0: "Tomato___Target_Spot",
1: "Tomato___Bacterial_spot",
2: "Tomato___Early_blight",
3: "Tomato___healthy",
4: "Tomato___Late_blight"
}
# ===== Prediction Functions =====
def predict_model_a(image):
img = preprocess_image(image)
pred = model_a.predict(img)
predicted_class = np.argmax(pred)
confidence = float(np.max(pred) * 100)
return disease_labels_a.get(predicted_class, "Unknown result"), confidence
def predict_model_b(image):
img = preprocess_image(image)
pred = model_b.predict(img)
predicted_class = np.argmax(pred)
confidence = float(np.max(pred) * 100)
return disease_labels_b.get(predicted_class, "Unknown result"), confidence
def predict_classifier(image):
img = preprocess_image(image)
pred = classifier_model.predict(img)
# Here we assume the classifier returns class 1 for "Tomato Leaf"
return "Tomato Leaf" if np.argmax(pred) == 1 else "Not Tomato Leaf"
# ===== AI Model API Calls =====
def get_ai_advice(prompt, retries=2):
"""Try multiple AI models with fallback mechanisms"""
# Try Groq API first (if key available)
if GROQ_API_KEY:
try:
headers = {
"Authorization": f"Bearer {GROQ_API_KEY}",
"Content-Type": "application/json"
}
payload = {
"model": "llama3-8b-8192", # Using Llama 3 8B model
"messages": [
{"role": "system", "content": "You are an expert agricultural advisor specializing in tomato farming."},
{"role": "user", "content": prompt}
],
"max_tokens": 800,
"temperature": 0.7
}
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}")
except Exception as e:
print(f"Error with Groq API: {str(e)}")
# Try Hugging Face Inference API as first fallback (if token available)
if HF_API_TOKEN:
try:
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": 800,
"temperature": 0.7,
"top_p": 0.95,
"do_sample": True
}
}
# Try Mistral model first
url = "https://api-inference.huggingface.co/models/mistralai/Mistral-7B-Instruct-v0.2"
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
# If Mistral fails, try Llama 3
url = "https://api-inference.huggingface.co/models/meta-llama/Meta-Llama-3-8B-Instruct"
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]:
generated_text = result[0]["generated_text"]
response_text = generated_text.split("[/INST]")[-1].strip()
return response_text
except Exception as e:
print(f"Error with Hugging Face API: {str(e)}")
# Try OpenAI API as final fallback (if key available)
if OPENAI_API_KEY:
try:
headers = {
"Authorization": f"Bearer {OPENAI_API_KEY}",
"Content-Type": "application/json"
}
payload = {
"model": "gpt-3.5-turbo",
"messages": [
{"role": "system", "content": "You are an expert agricultural advisor specializing in tomato farming."},
{"role": "user", "content": prompt}
],
"max_tokens": 800,
"temperature": 0.7
}
response = requests.post(
"https://api.openai.com/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"]
except Exception as e:
print(f"Error with OpenAI API: {str(e)}")
# If all API calls fail, use the fallback information from our database
disease_name = prompt.split("disease has been detected: ")[-1].split(" with")[0] if "disease has been detected:" in prompt else ""
if disease_name and disease_name in disease_info:
info = disease_info[disease_name]
return f"""
# {disease_name}
## Description
{info['description']}
## Causes
{info['causes']}
## Recommended Treatment
{chr(10).join(f"- {rec}" for rec in info['treatment'])}
*Note: This is fallback information as our AI service is currently unavailable.*
"""
else:
# Generic fallback response
return """
# Agricultural Advice
I apologize, but I'm currently unable to connect to our AI service. Here are some general tips for tomato plant care:
## General Tomato Care Tips
- Water consistently, aiming for 1-2 inches per week
- Provide support with stakes or cages
- Fertilize regularly with balanced fertilizer
- Remove suckers for indeterminate varieties
- Monitor for pests and diseases regularly
- Ensure good air circulation between plants
- Mulch to retain moisture and prevent soil-borne diseases
Please try again later for more specific advice.
"""
# ===== AI Assistant Functions =====
def generate_disease_advice(disease_name, confidence):
"""Generate advice for a specific disease with confidence level."""
if "healthy" in disease_name.lower():
prompt = (
"You are an agricultural advisor speaking to a farmer. "
"The tomato crop appears healthy. "
"Provide detailed preventive tips and best practices for maintaining tomato crop health. "
"Include information about watering, fertilization, pest prevention, and optimal growing conditions. "
"Format your response in clear sections with bullet points where appropriate."
)
else:
prompt = (
f"You are an agricultural advisor speaking to a farmer. "
f"A disease has been detected in their tomato crop: {disease_name} with {confidence:.1f}% confidence. "
f"Provide detailed advice on how to identify, manage and treat this disease. "
f"Include information about: "
f"1) What causes this disease "
f"2) How it spreads "
f"3) Specific treatments (both organic and chemical options) "
f"4) Preventive measures for the future "
f"Format your response in clear sections with bullet points where appropriate."
)
return get_ai_advice(prompt)
def chat_with_farmer(message, chat_history):
"""Handle chat interactions with farmers about agricultural topics."""
if not message.strip():
return "", chat_history
# Prepare context from chat history
context = "\n".join([f"Farmer: {q}\nAdvisor: {a}" for q, a in chat_history[-3:]]) # Use last 3 exchanges for context
prompt = (
f"You are FarmAssist, 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."
)
response = get_ai_advice(prompt)
chat_history.append((message, response))
return "", chat_history
# ===== Process Function Based on Version =====
def process_version(image, version):
if image is None:
return "No image provided."
# --- Version 1.x (Model A) ---
if version == "1.1":
result, confidence = predict_model_a(image)
return f"Model A Prediction: {result} (Confidence: {confidence:.1f}%)\n\nView Model A Training Notebook: https://colab.research.google.com/drive/1FMjs7JmdO6WVoXbzLA-ymwnIKq-GaV6w?usp=sharing"
elif version == "1.2":
result, confidence = predict_model_a(image)
advice = generate_disease_advice(result, confidence)
return f"Model A Prediction: {result} (Confidence: {confidence:.1f}%)\n\nExpert Advice:\n{advice}"
elif version == "1.3":
cls_result = predict_classifier(image)
if cls_result != "Tomato Leaf":
return "Classifier: The image is not a tomato leaf. Please try again with a tomato leaf image."
result, confidence = predict_model_a(image)
advice = generate_disease_advice(result, confidence)
return (
f"Classifier: {cls_result}\n"
f"Model A Prediction: {result} (Confidence: {confidence:.1f}%)\n\n"
f"Expert Advice:\n{advice}\n\n"
f"[View Model A & Classifier Training Notebook](https://colab.research.google.com/drive/1FMjs7JmdO6WVoXbzLA-ymwnIKq-GaV6w?usp=sharing)"
)
# --- Version 2.x (Model B) ---
elif version == "2.1":
result, confidence = predict_model_b(image)
return f"Model B Prediction: {result} (Confidence: {confidence:.1f}%)\n\n[View Model B Training Notebook](https://colab.research.google.com/drive/1CvoQY40gK2YsMgt4wq9kM2ZSO2c4lzFU?usp=sharing)"
elif version == "2.2":
result, confidence = predict_model_b(image)
advice = generate_disease_advice(result, confidence)
return f"Model B Prediction: {result} (Confidence: {confidence:.1f}%)\n\nExpert Advice:\n{advice}"
elif version == "2.3":
cls_result = predict_classifier(image)
if cls_result != "Tomato Leaf":
return "Classifier: The image is not a tomato leaf. Please try again with a tomato leaf image."
result, confidence = predict_model_b(image)
advice = generate_disease_advice(result, confidence)
return (
f"Classifier: {cls_result}\n"
f"Model B Prediction: {result} (Confidence: {confidence:.1f}%)\n\n"
f"Expert Advice:\n{advice}\n\n"
f"[View Model B & Classifier Training Notebook](https://colab.research.google.com/drive/1CvoQY40gK2YsMgt4wq9kM2ZSO2c4lzFU?usp=sharing)"
)
else:
return "Invalid version selected."
# ===== Helper Function to Choose Between Uploaded & Camera Image =====
def combine_images(uploaded, camera):
return camera if camera is not None else uploaded
# ===== CSS for Theme Switching =====
light_css = """
<style>
body { background-color: white; color: black; }
.gr-button { background-color: #4CAF50; color: white; }
.gr-input, .gr-textbox, .gr-dropdown, .gr-radio, .gr-markdown, .gr-container { background-color: white; color: black; }
</style>
"""
dark_css = """
<style>
body { background-color: #121212 !important; color: #e0e0e0 !important; }
.gr-button { background-color: #555 !important; color: white !important; }
.gr-input, .gr-textbox, .gr-dropdown, .gr-radio, .gr-markdown, .gr-container { background-color: #333 !important; color: #e0e0e0 !important; }
</style>
"""
def update_css(theme):
if theme == "Dark":
return dark_css
else:
return light_css
# ===== Gradio Interface =====
with gr.Blocks() as demo:
# Hidden element for CSS injection (initially Light theme)
css_injector = gr.HTML(update_css("Light"))
gr.Markdown("# 🌿 FarMVi8ioN – AI-powered Crop Monitoring")
gr.Markdown("Detect tomato leaf diseases and get actionable advice on how to curb them.")
with gr.Tabs():
# === Disease Detection Tab ===
with gr.TabItem("Disease Detection"):
with gr.Row():
# ----- Left Column (β‰ˆ30%) -----
with gr.Column(scale=1):
version = gr.Dropdown(
choices=["1.1", "1.2", "1.3", "2.1", "2.2", "2.3"],
label="Select Version",
value="1.3",
info="Versions 1.x use Model A; Versions 2.x use Model B."
)
theme_choice = gr.Radio(
choices=["Light", "Dark"],
label="Select Theme",
value="Light"
)
gr.Markdown("### Notebook Links")
gr.Markdown(
"""
**For Model A:**
- Model A Only: [Training Notebook](https://colab.research.google.com/drive/1FMjs7JmdO6WVoXbzLA-ymwnIKq-GaV6w?usp=sharing)
- Model A & Classifier: [Training Notebook](https://colab.research.google.com/drive/1CvoQY40gK2YsMgt4wq9kM2ZSO2c4lzFU?usp=sharing)
**For Model B:**
- Model B Only: [Training Notebook](https://colab.research.google.com/drive/1CvoQY40gK2YsMgt4wq9kM2ZSO2c4lzFU?usp=sharing)
- Model B & Classifier: [Training Notebook](https://colab.research.google.com/drive/1CvoQY40gK2YsMgt4wq9kM2ZSO2c4lzFU?usp=sharing)
"""
)
# ----- Right Column (β‰ˆ70%) -----
with gr.Column(scale=2):
image_input = gr.Image(label="πŸ“‚ Upload Tomato Leaf Image", type="pil")
camera_input = gr.Image(label="πŸ“Έ Use Camera (Live Preview)", type="pil", sources=["webcam"])
submit = gr.Button("πŸ” Analyze", variant="primary")
output = gr.Markdown(label="πŸ“ Diagnosis & Advice")
# === Farmer Chat Tab ===
with gr.TabItem("Chat with Farm Assistant"):
gr.Markdown("# πŸ’¬ Chat with Farm Assistant")
gr.Markdown("Ask any questions about farming, crop diseases, or agricultural practices.")
chatbot = gr.Chatbot(
label="Chat History",
height=400,
bubble_full_width=False,
show_copy_button=True
)
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", variant="primary")
gr.Markdown("""
### Example Questions:
- How often should I water my tomato plants?
- What's the best fertilizer for tomatoes?
- How do I prevent early blight?
- What are the signs of nutrient deficiency in tomatoes?
""")
# Update CSS dynamically based on theme selection
theme_choice.change(fn=update_css, inputs=theme_choice, outputs=css_injector)
# When submit is clicked, combine image inputs and process the selected version
submit.click(
fn=lambda uploaded, camera, ver: process_version(combine_images(uploaded, camera), ver),
inputs=[image_input, camera_input, version],
outputs=output
)
# Chat functionality
chat_button.click(
fn=chat_with_farmer,
inputs=[chat_input, chatbot],
outputs=[chat_input, chatbot]
)
# Also allow pressing Enter to send chat
chat_input.submit(
fn=chat_with_farmer,
inputs=[chat_input, chatbot],
outputs=[chat_input, chatbot]
)
# Launch the app
demo.launch()