Jeff28 commited on
Commit
be51c66
Β·
verified Β·
1 Parent(s): 9c71cf7

Update app.py with effective Groq API integration

Browse files
Files changed (1) hide show
  1. app.py +353 -226
app.py CHANGED
@@ -1,42 +1,35 @@
1
  import os
2
- import numpy as np
3
- import tensorflow as tf
4
- from tensorflow.keras.preprocessing import image
5
  import gradio as gr
 
6
  import requests
7
  import json
 
 
 
8
 
9
- # Suppress TensorFlow warnings
10
- os.environ["TF_CPP_MIN_LOG_LEVEL"] = "3"
11
- device = "cuda" if tf.test.is_gpu_available() else "cpu"
12
- print(f"Running on: {device.upper()}")
13
 
14
- # Groq API key for AI assistant
15
- GROQ_API_KEY = "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"
16
  GROQ_MODEL = "llama3-70b-8192" # Using Llama 3 70B model
 
17
 
18
- # Fallback to Hugging Face token if Groq fails
19
- HF_API_TOKEN = os.getenv("HUGGINGFACE_TOKEN")
20
- print(f"API tokens available: Groq=Yes, HF={'Yes' if HF_API_TOKEN else 'No'}")
21
-
22
- # Load the trained tomato disease detection model
23
- model = tf.keras.models.load_model("Tomato_Leaf_Disease_Model.h5")
24
 
25
- # Disease categories
26
- class_labels = [
27
- "Tomato Bacterial Spot",
28
- "Tomato Early Blight",
29
- "Tomato Late Blight",
30
- "Tomato Mosaic Virus",
31
- "Tomato Yellow Leaf Curl Virus"
32
- ]
33
 
34
- # Disease information database (fallback if API fails)
35
  disease_info = {
36
  "Tomato Bacterial Spot": {
37
  "description": "A bacterial disease that causes small, dark spots on leaves, stems, and fruits.",
38
  "causes": "Caused by Xanthomonas bacteria, spread by water splash, contaminated tools, and seeds.",
39
- "recommendations": [
40
  "Remove and destroy infected plants",
41
  "Rotate crops with non-solanaceous plants",
42
  "Use copper-based fungicides",
@@ -46,7 +39,7 @@ disease_info = {
46
  "Tomato Early Blight": {
47
  "description": "A fungal disease that causes dark spots with concentric rings on lower leaves first.",
48
  "causes": "Caused by Alternaria solani fungus, favored by warm, humid conditions.",
49
- "recommendations": [
50
  "Remove infected leaves promptly",
51
  "Improve air circulation around plants",
52
  "Apply fungicides preventatively",
@@ -56,7 +49,7 @@ disease_info = {
56
  "Tomato Late Blight": {
57
  "description": "A devastating fungal disease that causes dark, water-soaked lesions on leaves and fruits.",
58
  "causes": "Caused by Phytophthora infestans, favored by cool, wet conditions.",
59
- "recommendations": [
60
  "Remove and destroy infected plants immediately",
61
  "Apply fungicides preventatively in humid conditions",
62
  "Improve drainage and air circulation",
@@ -66,7 +59,7 @@ disease_info = {
66
  "Tomato Mosaic Virus": {
67
  "description": "A viral disease that causes mottled green/yellow patterns on leaves and stunted growth.",
68
  "causes": "Caused by tobacco mosaic virus (TMV), spread by handling, tools, and sometimes seeds.",
69
- "recommendations": [
70
  "Remove and destroy infected plants",
71
  "Wash hands and tools after handling infected plants",
72
  "Control insect vectors like aphids",
@@ -76,44 +69,106 @@ disease_info = {
76
  "Tomato Yellow Leaf Curl Virus": {
77
  "description": "A viral disease transmitted by whiteflies that causes yellowing and curling of leaves.",
78
  "causes": "Caused by a begomovirus, transmitted primarily by whiteflies.",
79
- "recommendations": [
80
  "Use whitefly control measures",
81
  "Remove and destroy infected plants",
82
  "Use reflective mulches to repel whiteflies",
83
  "Plant resistant varieties"
84
  ]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
85
  }
86
  }
87
 
88
- # Image preprocessing function
89
- def preprocess_image(img):
90
- img = img.resize((224, 224)) # Resize for model input
91
- img = image.img_to_array(img) / 255.0 # Normalize
92
- return np.expand_dims(img, axis=0) # Add batch dimension
93
-
94
- # Temperature Scaling: Adjusts predictions using a temperature parameter.
95
- def apply_temperature_scaling(prediction, temperature):
96
- # Avoid log(0) by adding a small epsilon
97
- eps = 1e-8
98
- scaled_logits = np.log(np.maximum(prediction, eps)) / temperature
99
- exp_logits = np.exp(scaled_logits)
100
- scaled_probs = exp_logits / np.sum(exp_logits)
101
- return scaled_probs
 
 
 
 
 
 
102
 
103
- # Min-Max Normalization: Scales the raw confidence based on provided min and max values.
104
- def apply_min_max_scaling(confidence, min_conf, max_conf):
105
- norm = (confidence - min_conf) / (max_conf - min_conf) * 100
106
- norm = np.clip(norm, 0, 100)
107
- return norm
 
 
 
108
 
109
- # Call Groq API for AI assistant
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
110
  def call_groq_api(prompt):
111
  """Call Groq API for detailed disease analysis and advice"""
 
 
112
  headers = {
113
  "Authorization": f"Bearer {GROQ_API_KEY}",
114
  "Content-Type": "application/json"
115
  }
116
-
117
  payload = {
118
  "model": GROQ_MODEL,
119
  "messages": [
@@ -123,7 +178,7 @@ def call_groq_api(prompt):
123
  "max_tokens": 800,
124
  "temperature": 0.7
125
  }
126
-
127
  try:
128
  response = requests.post(
129
  "https://api.groq.com/openai/v1/chat/completions",
@@ -131,30 +186,34 @@ def call_groq_api(prompt):
131
  json=payload,
132
  timeout=30
133
  )
134
-
 
 
135
  if response.status_code == 200:
136
  result = response.json()
137
  if "choices" in result and len(result["choices"]) > 0:
138
- return result["choices"][0]["message"]["content"]
139
-
 
 
140
  print(f"Groq API error: {response.status_code} - {response.text}")
141
  return None
142
-
143
  except Exception as e:
144
  print(f"Error with Groq API: {str(e)}")
145
  return None
146
 
147
- # Fallback to Hugging Face if Groq fails
148
- def call_hf_model(prompt, model_id="mistralai/Mistral-7B-Instruct-v0.2"):
149
- """Call an AI model on Hugging Face for detailed disease analysis."""
150
  if not HF_API_TOKEN:
151
  return None
152
-
153
  headers = {"Authorization": f"Bearer {HF_API_TOKEN}"}
154
-
155
  # Format prompt for instruction-tuned models
156
  formatted_prompt = f"""<s>[INST] {prompt} [/INST]"""
157
-
158
  payload = {
159
  "inputs": formatted_prompt,
160
  "parameters": {
@@ -164,12 +223,12 @@ def call_hf_model(prompt, model_id="mistralai/Mistral-7B-Instruct-v0.2"):
164
  "do_sample": True
165
  }
166
  }
167
-
168
  url = f"https://api-inference.huggingface.co/models/{model_id}"
169
-
170
  try:
171
  response = requests.post(url, headers=headers, json=payload, timeout=30)
172
-
173
  if response.status_code == 200:
174
  result = response.json()
175
  if isinstance(result, list) and len(result) > 0:
@@ -179,193 +238,261 @@ def call_hf_model(prompt, model_id="mistralai/Mistral-7B-Instruct-v0.2"):
179
  # Remove the prompt from the response
180
  response_text = generated_text.split("[/INST]")[-1].strip()
181
  return response_text
182
-
183
  return None
184
-
185
  except Exception as e:
186
- print(f"Exception when calling HF model: {str(e)}")
187
  return None
188
 
189
- # Combined AI model call with fallback
190
- def call_ai_model(prompt):
191
- """Call AI models with fallback mechanisms"""
192
- # Try Groq first
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
193
  response = call_groq_api(prompt)
194
- if response:
195
- return response
196
-
197
- # If Groq fails, try Hugging Face
198
- response = call_hf_model(prompt)
199
- if response:
200
- return response
201
-
202
- # If both fail, return fallback message
203
- return "Sorry, I'm having trouble connecting to the AI service. Using fallback information instead."
204
-
205
- # Generate AI response for disease analysis
206
- def generate_ai_response(disease_name, confidence):
207
- """Generate a detailed AI response about the detected disease."""
208
- # Get fallback information in case AI call fails
209
- info = disease_info.get(disease_name, {
210
- "description": "Information not available for this disease.",
211
- "causes": "Unknown causes.",
212
- "recommendations": ["Consult with a local agricultural extension service."]
213
- })
214
-
215
- # Create prompt for AI model
216
- prompt = (
217
- f"You are an agricultural expert advisor. A tomato plant disease has been detected: {disease_name} "
218
- f"with {confidence:.2f}% confidence. "
219
- f"Provide a detailed analysis including: "
220
- f"1) A brief description of the disease "
221
- f"2) What causes it and how it spreads "
222
- f"3) The impact on tomato plants and yield "
223
- f"4) Detailed treatment options (both organic and chemical) "
224
- f"5) Prevention strategies for future crops "
225
- f"Format your response in clear sections with bullet points where appropriate."
226
- )
227
-
228
- # Call AI model with fallback mechanisms
229
- ai_response = call_ai_model(prompt)
230
-
231
- # If AI response contains error message, use fallback information
232
- if "Sorry, I'm having trouble" in ai_response:
233
- ai_response = f"""
234
- # Disease: {disease_name}
235
 
236
  ## Description
237
- {info['description']}
238
 
239
  ## Causes
240
  {info.get('causes', 'Information not available.')}
241
 
242
  ## Recommended Treatment
243
- {chr(10).join(f"- {rec}" for rec in info['recommendations'])}
244
 
245
  *Note: This is fallback information. For more detailed advice, please try again later when the AI service is available.*
246
  """
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
247
 
248
- return ai_response
249
-
250
- # Chat with agricultural expert
251
- def chat_with_expert(message, chat_history):
252
- """Handle chat interactions with farmers about agricultural topics."""
253
- if not message.strip():
254
- return "", chat_history
255
-
256
- # Prepare context from chat history - use last 3 exchanges for context to avoid token limits
257
- context = "\n".join([f"Farmer: {q}\nExpert: {a}" for q, a in chat_history[-3:]])
258
-
259
- prompt = (
260
- f"You are an expert agricultural advisor specializing in tomato farming and plant diseases. "
261
- f"You provide helpful, accurate, and practical advice to farmers. "
262
- f"Always be respectful and considerate of farmers' knowledge while providing expert guidance. "
263
- f"If you're unsure about something, acknowledge it and provide the best information you can. "
264
- f"Previous conversation:\n{context}\n\n"
265
- f"Farmer's new question: {message}\n\n"
266
- f"Provide a helpful, informative response about farming, focusing on tomatoes if relevant."
267
- )
268
-
269
- # Call AI model with fallback mechanisms
270
- response = call_ai_model(prompt)
271
-
272
- # If AI response contains error message, use fallback response
273
- if "Sorry, I'm having trouble" in response:
274
- 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."
275
 
276
- chat_history.append((message, response))
277
- return "", chat_history
278
 
279
- # Main detection function with adjustable confidence scaling
280
- def detect_disease_scaled(img, scaling_method, temperature, min_conf, max_conf):
281
- processed_img = preprocess_image(img)
282
- prediction = model.predict(processed_img)[0] # Get prediction for single image
283
- raw_confidence = np.max(prediction) * 100
284
- class_idx = np.argmax(prediction)
285
- disease_name = class_labels[class_idx]
286
 
287
- if scaling_method == "Temperature Scaling":
288
- scaled_probs = apply_temperature_scaling(prediction, temperature)
289
- adjusted_confidence = np.max(scaled_probs) * 100
290
- elif scaling_method == "Min-Max Normalization":
291
- adjusted_confidence = apply_min_max_scaling(raw_confidence, min_conf, max_conf)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
292
  else:
293
- adjusted_confidence = raw_confidence
 
 
 
 
 
 
 
 
 
 
 
 
 
294
 
295
- # Generate AI response
296
- ai_response = generate_ai_response(disease_name, adjusted_confidence)
 
 
 
 
 
297
 
298
- # Return results
299
- result = f"{disease_name} (Confidence: {adjusted_confidence:.2f}%)"
300
- raw_text = f"Raw Confidence: {raw_confidence:.2f}%"
301
- return result, raw_text, ai_response
 
302
 
303
- # Simplified Gradio UI for better compatibility
304
  with gr.Blocks() as demo:
305
- gr.Markdown("# πŸ… EvSentry8: Tomato Disease Detection with AI Assistant")
306
-
307
- with gr.Tab("Disease Detection"):
308
- with gr.Row():
309
- with gr.Column():
310
- image_input = gr.Image(type="pil", label="Upload a Tomato Leaf Image", sources=["upload", "webcam", "clipboard"])
311
-
312
- scaling_method = gr.Radio(
313
- ["Temperature Scaling", "Min-Max Normalization"],
314
- label="Confidence Scaling Method",
315
- value="Temperature Scaling"
316
- )
317
- temperature_slider = gr.Slider(0.5, 2.0, step=0.1, label="Temperature", value=1.0)
318
- min_conf_slider = gr.Slider(0, 100, step=1, label="Min Confidence", value=20)
319
- max_conf_slider = gr.Slider(0, 100, step=1, label="Max Confidence", value=90)
320
-
321
- detect_button = gr.Button("Detect Disease")
322
-
323
- with gr.Column():
324
- disease_output = gr.Textbox(label="Detected Disease & Adjusted Confidence")
325
- raw_confidence_output = gr.Textbox(label="Raw Confidence")
326
- ai_response_output = gr.Markdown(label="AI Assistant's Analysis & Recommendations")
327
-
328
- with gr.Tab("Chat with Expert"):
329
- gr.Markdown("# πŸ’¬ Chat with Agricultural Expert")
330
- gr.Markdown("Ask any questions about tomato farming, diseases, or agricultural practices.")
331
-
332
- chatbot = gr.Chatbot(height=400)
333
-
334
- with gr.Row():
335
- chat_input = gr.Textbox(
336
- label="Your Question",
337
- placeholder="Ask about tomato farming, diseases, or agricultural practices...",
338
- lines=2
339
  )
340
- chat_button = gr.Button("Send")
341
-
342
- gr.Markdown("""
343
- ### Example Questions:
344
- - How do I identify tomato bacterial spot?
345
- - What's the best way to prevent late blight?
346
- - How often should I water my tomato plants?
347
- - What are the signs of nutrient deficiency in tomatoes?
348
- """)
349
-
350
- # Set up event handlers
351
- detect_button.click(
352
- detect_disease_scaled,
353
- inputs=[image_input, scaling_method, temperature_slider, min_conf_slider, max_conf_slider],
354
- outputs=[disease_output, raw_confidence_output, ai_response_output]
355
- )
356
-
357
- # Chat functionality
358
- chat_button.click(
359
- fn=chat_with_expert,
360
- inputs=[chat_input, chatbot],
361
- outputs=[chat_input, chatbot]
362
- )
363
-
364
- # Also allow pressing Enter to send chat
365
- chat_input.submit(
366
- fn=chat_with_expert,
367
- inputs=[chat_input, chatbot],
368
- outputs=[chat_input, chatbot]
 
 
 
 
 
 
369
  )
370
 
371
  demo.launch()
 
1
  import os
 
 
 
2
  import gradio as gr
3
+ import numpy as np
4
  import requests
5
  import json
6
+ from dotenv import load_dotenv
7
+ from tensorflow.keras.models import load_model
8
+ from PIL import Image
9
 
10
+ # Load environment variables
11
+ load_dotenv()
 
 
12
 
13
+ # ===== Groq API Key =====
14
+ GROQ_API_KEY = os.getenv("GROQ_API_KEY", "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")
15
  GROQ_MODEL = "llama3-70b-8192" # Using Llama 3 70B model
16
+ print(f"Groq API key available: {'Yes' if GROQ_API_KEY else 'No'}")
17
 
18
+ # ===== Fallback to Hugging Face API Token =====
19
+ HF_API_TOKEN = os.getenv("HF_API_TOKEN")
20
+ print(f"HF API token available: {'Yes' if HF_API_TOKEN else 'No'}")
 
 
 
21
 
22
+ # ===== Load Trained Models =====
23
+ model_a = load_model("Tomato_Leaf_Disease_Model.h5")
24
+ model_b = load_model("tomato_leaf_model_final(77%).h5")
25
+ classifier_model = load_model("tomato_leaf_classifier_optimized.h5")
 
 
 
 
26
 
27
+ # ===== Disease Information Database (fallback if API fails) =====
28
  disease_info = {
29
  "Tomato Bacterial Spot": {
30
  "description": "A bacterial disease that causes small, dark spots on leaves, stems, and fruits.",
31
  "causes": "Caused by Xanthomonas bacteria, spread by water splash, contaminated tools, and seeds.",
32
+ "treatment": [
33
  "Remove and destroy infected plants",
34
  "Rotate crops with non-solanaceous plants",
35
  "Use copper-based fungicides",
 
39
  "Tomato Early Blight": {
40
  "description": "A fungal disease that causes dark spots with concentric rings on lower leaves first.",
41
  "causes": "Caused by Alternaria solani fungus, favored by warm, humid conditions.",
42
+ "treatment": [
43
  "Remove infected leaves promptly",
44
  "Improve air circulation around plants",
45
  "Apply fungicides preventatively",
 
49
  "Tomato Late Blight": {
50
  "description": "A devastating fungal disease that causes dark, water-soaked lesions on leaves and fruits.",
51
  "causes": "Caused by Phytophthora infestans, favored by cool, wet conditions.",
52
+ "treatment": [
53
  "Remove and destroy infected plants immediately",
54
  "Apply fungicides preventatively in humid conditions",
55
  "Improve drainage and air circulation",
 
59
  "Tomato Mosaic Virus": {
60
  "description": "A viral disease that causes mottled green/yellow patterns on leaves and stunted growth.",
61
  "causes": "Caused by tobacco mosaic virus (TMV), spread by handling, tools, and sometimes seeds.",
62
+ "treatment": [
63
  "Remove and destroy infected plants",
64
  "Wash hands and tools after handling infected plants",
65
  "Control insect vectors like aphids",
 
69
  "Tomato Yellow Leaf Curl Virus": {
70
  "description": "A viral disease transmitted by whiteflies that causes yellowing and curling of leaves.",
71
  "causes": "Caused by a begomovirus, transmitted primarily by whiteflies.",
72
+ "treatment": [
73
  "Use whitefly control measures",
74
  "Remove and destroy infected plants",
75
  "Use reflective mulches to repel whiteflies",
76
  "Plant resistant varieties"
77
  ]
78
+ },
79
+ "Tomato___Target_Spot": {
80
+ "description": "A fungal disease causing circular lesions with concentric rings on leaves, stems, and fruits.",
81
+ "causes": "Caused by Corynespora cassiicola fungus, favored by warm, humid conditions.",
82
+ "treatment": [
83
+ "Remove infected plant parts",
84
+ "Improve air circulation",
85
+ "Apply fungicides at first sign of disease",
86
+ "Avoid overhead irrigation"
87
+ ]
88
+ },
89
+ "Tomato___Bacterial_spot": {
90
+ "description": "A bacterial disease causing small, dark, water-soaked spots on leaves, stems, and fruits.",
91
+ "causes": "Caused by Xanthomonas species, spread by water splash and contaminated tools.",
92
+ "treatment": [
93
+ "Remove infected plant debris",
94
+ "Use copper-based bactericides",
95
+ "Rotate crops",
96
+ "Use disease-free seeds and transplants"
97
+ ]
98
+ },
99
+ "Tomato___healthy": {
100
+ "description": "The plant shows no signs of disease and appears to be in good health.",
101
+ "causes": "Proper growing conditions, good management practices, and disease prevention.",
102
+ "treatment": [
103
+ "Continue regular watering and fertilization",
104
+ "Monitor for early signs of disease",
105
+ "Maintain good air circulation",
106
+ "Practice crop rotation"
107
+ ]
108
  }
109
  }
110
 
111
+ # ===== Preprocessing Function =====
112
+ def preprocess_image(image, target_size=(224, 224)):
113
+ # Ensure the image is resized and normalized.
114
+ if isinstance(image, Image.Image):
115
+ img = image.resize(target_size)
116
+ else:
117
+ img = Image.fromarray(image).resize(target_size)
118
+ img_array = np.array(img) / 255.0
119
+ img_array = np.expand_dims(img_array, axis=0)
120
+ return img_array
121
+
122
+ # ===== Disease Label Mappings =====
123
+ # Model A labels
124
+ disease_labels_a = {
125
+ 0: "Tomato Bacterial Spot",
126
+ 1: "Tomato Early Blight",
127
+ 2: "Tomato Late Blight",
128
+ 3: "Tomato Mosaic Virus",
129
+ 4: "Tomato Yellow Leaf Curl Virus"
130
+ }
131
 
132
+ # Model B labels
133
+ disease_labels_b = {
134
+ 0: "Tomato___Target_Spot",
135
+ 1: "Tomato___Bacterial_spot",
136
+ 2: "Tomato___Early_blight",
137
+ 3: "Tomato___healthy",
138
+ 4: "Tomato___Late_blight"
139
+ }
140
 
141
+ # ===== Prediction Functions =====
142
+ def predict_model_a(image):
143
+ img = preprocess_image(image)
144
+ pred = model_a.predict(img)
145
+ predicted_class = np.argmax(pred)
146
+ confidence = float(np.max(pred) * 100)
147
+ return disease_labels_a.get(predicted_class, "Unknown result"), confidence
148
+
149
+ def predict_model_b(image):
150
+ img = preprocess_image(image)
151
+ pred = model_b.predict(img)
152
+ predicted_class = np.argmax(pred)
153
+ confidence = float(np.max(pred) * 100)
154
+ return disease_labels_b.get(predicted_class, "Unknown result"), confidence
155
+
156
+ def predict_classifier(image):
157
+ img = preprocess_image(image)
158
+ pred = classifier_model.predict(img)
159
+ # Here we assume the classifier returns class 1 for "Tomato Leaf"
160
+ return "Tomato Leaf" if np.argmax(pred) == 1 else "Not Tomato Leaf"
161
+
162
+ # ===== Groq API Call =====
163
  def call_groq_api(prompt):
164
  """Call Groq API for detailed disease analysis and advice"""
165
+ print(f"Calling Groq API with prompt: {prompt[:50]}...")
166
+
167
  headers = {
168
  "Authorization": f"Bearer {GROQ_API_KEY}",
169
  "Content-Type": "application/json"
170
  }
171
+
172
  payload = {
173
  "model": GROQ_MODEL,
174
  "messages": [
 
178
  "max_tokens": 800,
179
  "temperature": 0.7
180
  }
181
+
182
  try:
183
  response = requests.post(
184
  "https://api.groq.com/openai/v1/chat/completions",
 
186
  json=payload,
187
  timeout=30
188
  )
189
+
190
+ print(f"Groq API response status: {response.status_code}")
191
+
192
  if response.status_code == 200:
193
  result = response.json()
194
  if "choices" in result and len(result["choices"]) > 0:
195
+ content = result["choices"][0]["message"]["content"]
196
+ print(f"Groq API response received: {len(content)} characters")
197
+ return content
198
+
199
  print(f"Groq API error: {response.status_code} - {response.text}")
200
  return None
201
+
202
  except Exception as e:
203
  print(f"Error with Groq API: {str(e)}")
204
  return None
205
 
206
+ # ===== Fallback to Hugging Face API =====
207
+ def call_hf_api(prompt, model_id="mistralai/Mistral-7B-Instruct-v0.2"):
208
+ """Call Hugging Face API as fallback"""
209
  if not HF_API_TOKEN:
210
  return None
211
+
212
  headers = {"Authorization": f"Bearer {HF_API_TOKEN}"}
213
+
214
  # Format prompt for instruction-tuned models
215
  formatted_prompt = f"""<s>[INST] {prompt} [/INST]"""
216
+
217
  payload = {
218
  "inputs": formatted_prompt,
219
  "parameters": {
 
223
  "do_sample": True
224
  }
225
  }
226
+
227
  url = f"https://api-inference.huggingface.co/models/{model_id}"
228
+
229
  try:
230
  response = requests.post(url, headers=headers, json=payload, timeout=30)
231
+
232
  if response.status_code == 200:
233
  result = response.json()
234
  if isinstance(result, list) and len(result) > 0:
 
238
  # Remove the prompt from the response
239
  response_text = generated_text.split("[/INST]")[-1].strip()
240
  return response_text
241
+
242
  return None
243
+
244
  except Exception as e:
245
+ print(f"Error with Hugging Face API: {str(e)}")
246
  return None
247
 
248
+ # ===== AI Assistant Functions =====
249
+ def ai_assistant_v1(image, prediction, confidence):
250
+ """Use Groq API for Model A versions"""
251
+ if "healthy" in prediction.lower():
252
+ prompt = (
253
+ "You are an agricultural advisor speaking to a farmer. "
254
+ "The tomato crop appears healthy. "
255
+ "Provide detailed preventive tips and best practices for maintaining tomato crop health. "
256
+ "Include information about watering, fertilization, pest prevention, and optimal growing conditions. "
257
+ "Format your response in clear sections with bullet points where appropriate."
258
+ )
259
+ else:
260
+ prompt = (
261
+ f"You are an agricultural advisor speaking to a farmer. "
262
+ f"A disease has been detected in their tomato crop: {prediction} with {confidence:.1f}% confidence. "
263
+ f"Provide detailed advice on how to identify, manage and treat this disease. "
264
+ f"Include information about: "
265
+ f"1) What causes this disease "
266
+ f"2) How it spreads "
267
+ f"3) Specific treatments (both organic and chemical options) "
268
+ f"4) Preventive measures for the future "
269
+ f"Format your response in clear sections with bullet points where appropriate."
270
+ )
271
+
272
+ # Call Groq API
273
  response = call_groq_api(prompt)
274
+
275
+ # If Groq API fails, try Hugging Face API
276
+ if not response:
277
+ response = call_hf_api(prompt)
278
+
279
+ # If both APIs fail, use fallback information
280
+ if not response:
281
+ # Get fallback information from our database
282
+ info = disease_info.get(prediction, {
283
+ "description": "Information not available for this disease.",
284
+ "causes": "Unknown causes.",
285
+ "treatment": ["Consult with a local agricultural extension service."]
286
+ })
287
+
288
+ response = f"""
289
+ # {prediction}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
290
 
291
  ## Description
292
+ {info.get('description', 'No description available.')}
293
 
294
  ## Causes
295
  {info.get('causes', 'Information not available.')}
296
 
297
  ## Recommended Treatment
298
+ {chr(10).join(f"- {rec}" for rec in info.get('treatment', ['No specific treatment information available.']))}
299
 
300
  *Note: This is fallback information. For more detailed advice, please try again later when the AI service is available.*
301
  """
302
+
303
+ return response
304
+
305
+ def ai_assistant_v2(image, prediction, confidence):
306
+ """Use Groq API for Model B versions"""
307
+ if "healthy" in prediction.lower():
308
+ prompt = (
309
+ "You are an agricultural advisor speaking to a farmer. "
310
+ "The tomato crop appears healthy. "
311
+ "Provide detailed preventive tips and best practices for maintaining tomato crop health. "
312
+ "Include information about watering, fertilization, pest prevention, and optimal growing conditions. "
313
+ "Format your response in clear sections with bullet points where appropriate."
314
+ )
315
+ else:
316
+ prompt = (
317
+ f"You are an agricultural advisor speaking to a farmer. "
318
+ f"A disease has been detected in their tomato crop: {prediction} with {confidence:.1f}% confidence. "
319
+ f"Provide detailed advice on how to identify, manage and treat this disease. "
320
+ f"Include information about: "
321
+ f"1) What causes this disease "
322
+ f"2) How it spreads "
323
+ f"3) Specific treatments (both organic and chemical options) "
324
+ f"4) Preventive measures for the future "
325
+ f"Format your response in clear sections with bullet points where appropriate."
326
+ )
327
+
328
+ # Call Groq API
329
+ response = call_groq_api(prompt)
330
+
331
+ # If Groq API fails, try Hugging Face API
332
+ if not response:
333
+ response = call_hf_api(prompt)
334
+
335
+ # If both APIs fail, use fallback information
336
+ if not response:
337
+ # Get fallback information from our database
338
+ info = disease_info.get(prediction, {
339
+ "description": "Information not available for this disease.",
340
+ "causes": "Unknown causes.",
341
+ "treatment": ["Consult with a local agricultural extension service."]
342
+ })
343
+
344
+ response = f"""
345
+ # {prediction}
346
 
347
+ ## Description
348
+ {info.get('description', 'No description available.')}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
349
 
350
+ ## Causes
351
+ {info.get('causes', 'Information not available.')}
352
 
353
+ ## Recommended Treatment
354
+ {chr(10).join(f"- {rec}" for rec in info.get('treatment', ['No specific treatment information available.']))}
 
 
 
 
 
355
 
356
+ *Note: This is fallback information. For more detailed advice, please try again later when the AI service is available.*
357
+ """
358
+
359
+ return response
360
+
361
+ # ===== Process Function Based on Version =====
362
+ def process_version(image, version):
363
+ if image is None:
364
+ return "No image provided."
365
+
366
+ # --- Version 1.x (Model A) ---
367
+ if version == "1.1":
368
+ result, confidence = predict_model_a(image)
369
+ 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"
370
+
371
+ elif version == "1.2":
372
+ result, confidence = predict_model_a(image)
373
+ advice = ai_assistant_v1(image, result, confidence)
374
+ return f"Model A Prediction: {result} (Confidence: {confidence:.1f}%)\n\nExpert Advice:\n{advice}"
375
+
376
+ elif version == "1.3":
377
+ cls_result = predict_classifier(image)
378
+ if cls_result != "Tomato Leaf":
379
+ return "Classifier: The image is not a tomato leaf. Please try again with a tomato leaf image."
380
+
381
+ result, confidence = predict_model_a(image)
382
+ advice = ai_assistant_v1(image, result, confidence)
383
+ return (
384
+ f"Classifier: {cls_result}\n"
385
+ f"Model A Prediction: {result} (Confidence: {confidence:.1f}%)\n\n"
386
+ f"Expert Advice:\n{advice}\n\n"
387
+ f"[View Model A & Classifier Training Notebook](https://colab.research.google.com/drive/1FMjs7JmdO6WVoXbzLA-ymwnIKq-GaV6w?usp=sharing)"
388
+ )
389
+
390
+ # --- Version 2.x (Model B) ---
391
+ elif version == "2.1":
392
+ result, confidence = predict_model_b(image)
393
+ 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)"
394
+
395
+ elif version == "2.2":
396
+ result, confidence = predict_model_b(image)
397
+ advice = ai_assistant_v2(image, result, confidence)
398
+ return f"Model B Prediction: {result} (Confidence: {confidence:.1f}%)\n\nExpert Advice:\n{advice}"
399
+
400
+ elif version == "2.3":
401
+ cls_result = predict_classifier(image)
402
+ if cls_result != "Tomato Leaf":
403
+ return "Classifier: The image is not a tomato leaf. Please try again with a tomato leaf image."
404
+
405
+ result, confidence = predict_model_b(image)
406
+ advice = ai_assistant_v2(image, result, confidence)
407
+ return (
408
+ f"Classifier: {cls_result}\n"
409
+ f"Model B Prediction: {result} (Confidence: {confidence:.1f}%)\n\n"
410
+ f"Expert Advice:\n{advice}\n\n"
411
+ f"[View Model B & Classifier Training Notebook](https://colab.research.google.com/drive/1CvoQY40gK2YsMgt4wq9kM2ZSO2c4lzFU?usp=sharing)"
412
+ )
413
+
414
  else:
415
+ return "Invalid version selected."
416
+
417
+ # ===== Helper Function to Choose Between Uploaded & Camera Image =====
418
+ def combine_images(uploaded, camera):
419
+ return camera if camera is not None else uploaded
420
+
421
+ # ===== CSS for Theme Switching =====
422
+ light_css = """
423
+ <style>
424
+ body { background-color: white; color: black; }
425
+ .gr-button { background-color: #4CAF50; color: white; }
426
+ .gr-input, .gr-textbox, .gr-dropdown, .gr-radio, .gr-markdown, .gr-container { background-color: white; color: black; }
427
+ </style>
428
+ """
429
 
430
+ dark_css = """
431
+ <style>
432
+ body { background-color: #121212 !important; color: #e0e0e0 !important; }
433
+ .gr-button { background-color: #555 !important; color: white !important; }
434
+ .gr-input, .gr-textbox, .gr-dropdown, .gr-radio, .gr-markdown, .gr-container { background-color: #333 !important; color: #e0e0e0 !important; }
435
+ </style>
436
+ """
437
 
438
+ def update_css(theme):
439
+ if theme == "Dark":
440
+ return dark_css
441
+ else:
442
+ return light_css
443
 
444
+ # ===== Gradio Interface =====
445
  with gr.Blocks() as demo:
446
+ # Hidden element for CSS injection (initially Light theme)
447
+ css_injector = gr.HTML(update_css("Light"))
448
+
449
+ gr.Markdown("# 🌿 FarMVi8ioN – AI-powered Crop Monitoring")
450
+ gr.Markdown("Detect tomato leaf diseases and get actionable advice on how to curb them.")
451
+
452
+ with gr.Row():
453
+ # ----- Left Column (β‰ˆ30%) -----
454
+ with gr.Column(scale=1):
455
+ version = gr.Dropdown(
456
+ choices=["1.1", "1.2", "1.3", "2.1", "2.2", "2.3"],
457
+ label="Select Version",
458
+ value="1.3",
459
+ info="Versions 1.x use Model A; Versions 2.x use Model B."
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
460
  )
461
+
462
+ theme_choice = gr.Radio(
463
+ choices=["Light", "Dark"],
464
+ label="Select Theme",
465
+ value="Light"
466
+ )
467
+
468
+ gr.Markdown("### Notebook Links")
469
+ gr.Markdown(
470
+ """
471
+ **For Model A:**
472
+ - Model A Only: [Training Notebook](https://colab.research.google.com/drive/1FMjs7JmdO6WVoXbzLA-ymwnIKq-GaV6w?usp=sharing)
473
+ - Model A & Classifier: [Training Notebook](https://colab.research.google.com/drive/1CvoQY40gK2YsMgt4wq9kM2ZSO2c4lzFU?usp=sharing)
474
+
475
+ **For Model B:**
476
+ - Model B Only: [Training Notebook](https://colab.research.google.com/drive/1CvoQY40gK2YsMgt4wq9kM2ZSO2c4lzFU?usp=sharing)
477
+ - Model B & Classifier: [Training Notebook](https://colab.research.google.com/drive/1CvoQY40gK2YsMgt4wq9kM2ZSO2c4lzFU?usp=sharing)
478
+ """
479
+ )
480
+
481
+ # ----- Right Column (β‰ˆ70%) -----
482
+ with gr.Column(scale=2):
483
+ image_input = gr.Image(label="πŸ“‚ Upload Tomato Leaf Image", type="pil", sources=["upload", "webcam", "clipboard"])
484
+ submit = gr.Button("πŸ” Analyze", variant="primary")
485
+
486
+ output = gr.Markdown(label="πŸ“ Diagnosis & Advice")
487
+
488
+ # Update CSS dynamically based on theme selection
489
+ theme_choice.change(fn=update_css, inputs=theme_choice, outputs=css_injector)
490
+
491
+ # When submit is clicked, combine image inputs and process the selected version
492
+ submit.click(
493
+ fn=lambda img, ver: process_version(img, ver),
494
+ inputs=[image_input, version],
495
+ outputs=output
496
  )
497
 
498
  demo.launch()