Jeff28 commited on
Commit
48f68ce
Β·
verified Β·
1 Parent(s): 7ac72e2

Update app.py with Groq API integration

Browse files
Files changed (1) hide show
  1. app.py +420 -128
app.py CHANGED
@@ -2,12 +2,102 @@ import os
2
  import gradio as gr
3
  import numpy as np
4
  import requests
 
 
5
  from tensorflow.keras.models import load_model
6
  from PIL import Image
7
 
8
- # ===== Hugging Face API Token =====
9
- # Set HF_API_TOKEN in your environment (as a secret on Hugging Face Spaces)
10
- HF_API_TOKEN = os.getenv("HF_API_TOKEN") # e.g., "hf_xxx..."
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
11
 
12
  # ===== Load Trained Models =====
13
  model_a = load_model("Tomato_Leaf_Disease_Model.h5")
@@ -26,7 +116,7 @@ def preprocess_image(image, target_size=(224, 224)):
26
  return img_array
27
 
28
  # ===== Disease Label Mappings =====
29
- # Model A labels (for example)
30
  disease_labels_a = {
31
  0: "Tomato Bacterial Spot",
32
  1: "Tomato Early Blight",
@@ -35,7 +125,7 @@ disease_labels_a = {
35
  4: "Tomato Yellow Leaf Curl Virus"
36
  }
37
 
38
- # Model B labels (based on your training dataset)
39
  disease_labels_b = {
40
  0: "Tomato___Target_Spot",
41
  1: "Tomato___Bacterial_spot",
@@ -49,13 +139,15 @@ def predict_model_a(image):
49
  img = preprocess_image(image)
50
  pred = model_a.predict(img)
51
  predicted_class = np.argmax(pred)
52
- return disease_labels_a.get(predicted_class, "Unknown result")
 
53
 
54
  def predict_model_b(image):
55
  img = preprocess_image(image)
56
  pred = model_b.predict(img)
57
  predicted_class = np.argmax(pred)
58
- return disease_labels_b.get(predicted_class, "Unknown result")
 
59
 
60
  def predict_classifier(image):
61
  img = preprocess_image(image)
@@ -63,113 +155,265 @@ def predict_classifier(image):
63
  # Here we assume the classifier returns class 1 for "Tomato Leaf"
64
  return "Tomato Leaf" if np.argmax(pred) == 1 else "Not Tomato Leaf"
65
 
66
- # ===== Hugging Face Inference API Calls =====
67
- def call_llama2(prompt):
68
- """Call Llama 2-7B Chat model on Hugging Face for conversational advice."""
69
- headers = {"Authorization": f"Bearer {HF_API_TOKEN}"}
70
- payload = {"inputs": prompt, "parameters": {"max_new_tokens": 100}}
71
- url = "https://api-inference.huggingface.co/models/meta-llama/Llama-2-7b-chat-hf"
72
- response = requests.post(url, headers=headers, json=payload)
73
- if response.status_code == 200:
74
- result = response.json()
75
- if isinstance(result, list) and "generated_text" in result[0]:
76
- return result[0]["generated_text"]
77
- else:
78
- return "No response from Llama 2."
79
- else:
80
- return f"Error calling Llama 2 API: {response.status_code}"
81
-
82
- def call_openassistant(prompt):
83
- """Call an OpenAssistant model on Hugging Face for conversational advice."""
84
- headers = {"Authorization": f"Bearer {HF_API_TOKEN}"}
85
- payload = {"inputs": prompt, "parameters": {"max_new_tokens": 100}}
86
- url = "https://api-inference.huggingface.co/models/OpenAssistant/oasst-sft-7-llama"
87
- response = requests.post(url, headers=headers, json=payload)
88
- if response.status_code == 200:
89
- result = response.json()
90
- if isinstance(result, list) and "generated_text" in result[0]:
91
- return result[0]["generated_text"]
92
- else:
93
- return "No response from OpenAssistant."
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
94
  else:
95
- return f"Error calling OpenAssistant API: {response.status_code}"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
96
 
97
  # ===== AI Assistant Functions =====
98
- def ai_assistant_v1(image, prediction):
99
- # Use Llama 2-7B Chat (for Model A versions)
100
- if "Tomato" not in prediction:
101
  prompt = (
102
- "You are an agricultural advisor. The tomato crop appears healthy. "
103
- "Provide additional preventive tips and best practices for maintaining crop health."
 
 
 
104
  )
105
  else:
106
  prompt = (
107
- f"You are an agricultural advisor. A disease has been detected: {prediction}. "
108
- "Provide detailed advice on how to manage and curb this disease, explaining it in simple terms."
 
 
 
 
 
 
 
109
  )
110
- return call_llama2(prompt)
111
 
112
- def ai_assistant_v2(image, prediction):
113
- # Use OpenAssistant (for Model B versions)
114
- if "Tomato" not in prediction:
115
- prompt = (
116
- "You are an agricultural advisor. The tomato crop appears healthy. "
117
- "Offer additional preventive tips and guidelines for maintaining a healthy crop."
118
- )
119
- else:
120
- prompt = (
121
- f"You are an agricultural advisor. A disease has been detected: {prediction}. "
122
- "Provide actionable steps and detailed advice on how to control and manage this disease in tomato crops."
123
- )
124
- return call_openassistant(prompt)
 
 
 
 
 
 
 
 
 
 
125
 
126
  # ===== Process Function Based on Version =====
127
  def process_version(image, version):
128
  if image is None:
129
  return "No image provided."
130
-
131
  # --- Version 1.x (Model A) ---
132
  if version == "1.1":
133
- result = predict_model_a(image)
134
- return f"Model A Prediction: {result}\n\nView Model A Training Notebook-https://colab.research.google.com/drive/1FMjs7JmdO6WVoXbzLA-ymwnIKq-GaV6w?usp=sharing"
135
-
136
  elif version == "1.2":
137
- result = predict_model_a(image)
138
- advice = ai_assistant_v1(image, result)
139
- return f"Model A Prediction: {result}\nAdvice: {advice}"
140
-
141
  elif version == "1.3":
142
  cls_result = predict_classifier(image)
143
  if cls_result != "Tomato Leaf":
144
- return "Classifier: The image is not a tomato leaf. Please try again."
145
- result = predict_model_a(image)
146
- advice = ai_assistant_v1(image, result)
 
147
  return (
148
- f"Classifier: {cls_result}\nModel A Prediction: {result}\nAdvice: {advice}\n\n"
149
- f"[View Model A & Classifier Training Notebook]--(https://colab.research.google.com/drive/1FMjs7JmdO6WVoXbzLA-ymwnIKq-GaV6w?usp=sharing)"
 
 
150
  )
151
-
152
  # --- Version 2.x (Model B) ---
153
  elif version == "2.1":
154
- result = predict_model_b(image)
155
- return f"Model B Prediction: {result}\n\n[View Model B Training Notebook]--(https://colab.research.google.com/drive/1CvoQY40gK2YsMgt4wq9kM2ZSO2c4lzFU?usp=sharing)"
156
-
157
  elif version == "2.2":
158
- result = predict_model_b(image)
159
- advice = ai_assistant_v2(image, result)
160
- return f"Model B Prediction: {result}\nAdvice: {advice}"
161
-
162
  elif version == "2.3":
163
  cls_result = predict_classifier(image)
164
  if cls_result != "Tomato Leaf":
165
- return "Classifier: The image is not a tomato leaf. Please try again."
166
- result = predict_model_b(image)
167
- advice = ai_assistant_v2(image, result)
 
168
  return (
169
- f"Classifier: {cls_result}\nModel B Prediction: {result}\nAdvice: {advice}\n\n"
 
 
170
  f"[View Model B & Classifier Training Notebook](https://colab.research.google.com/drive/1CvoQY40gK2YsMgt4wq9kM2ZSO2c4lzFU?usp=sharing)"
171
  )
172
-
173
  else:
174
  return "Invalid version selected."
175
 
@@ -180,17 +424,17 @@ def combine_images(uploaded, camera):
180
  # ===== CSS for Theme Switching =====
181
  light_css = """
182
  <style>
183
- body { background-color: white; color: black; }
184
- .gr-button { background-color: #4CAF50; color: white; }
185
- .gr-input, .gr-textbox, .gr-dropdown, .gr-radio, .gr-markdown, .gr-container { background-color: white; color: black; }
186
  </style>
187
  """
188
 
189
  dark_css = """
190
  <style>
191
- body { background-color: #121212 !important; color: #e0e0e0 !important; }
192
- .gr-button { background-color: #555 !important; color: white !important; }
193
- .gr-input, .gr-textbox, .gr-dropdown, .gr-radio, .gr-markdown, .gr-container { background-color: #333 !important; color: #e0e0e0 !important; }
194
  </style>
195
  """
196
 
@@ -204,52 +448,100 @@ def update_css(theme):
204
  with gr.Blocks() as demo:
205
  # Hidden element for CSS injection (initially Light theme)
206
  css_injector = gr.HTML(update_css("Light"))
207
-
208
- gr.Markdown("# 🌿 FarmVi8ion – AI-powered Crop Monitoring")
209
  gr.Markdown("Detect tomato leaf diseases and get actionable advice on how to curb them.")
210
-
211
- with gr.Row():
212
- # ----- Left Column (β‰ˆ30%) -----
213
- with gr.Column(scale=1):
214
- version = gr.Dropdown(
215
- choices=["1.1", "1.2", "1.3", "2.1", "2.2", "2.3"],
216
- label="Select Version",
217
- value="1.1",
218
- info="Versions 1.x use Model A; Versions 2.x use Model B."
219
- )
220
- theme_choice = gr.Radio(
221
- choices=["Light", "Dark"],
222
- label="Select Theme",
223
- value="Light"
224
- )
225
- gr.Markdown("### Notebook Links")
226
- gr.Markdown(
227
- """
228
- **For Model A:**
229
- - Model A Only: [Training Notebook](https://colab.research.google.com/drive/1FMjs7JmdO6WVoXbzLA-ymwnIKq-GaV6w?usp=sharing)
230
- - Model A & Classifier: [Training Notebook](https://colab.research.google.com/drive/1CvoQY40gK2YsMgt4wq9kM2ZSO2c4lzFU?usp=sharing)
231
-
232
- **For Model B:**
233
- - Model B Only: [Training Notebook](https://huggingface.co/your-model-b-notebook)
234
- - Model B & Classifier: [Training Notebook](https://huggingface.co/your-model-b-classifier-notebook)
235
- """
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
236
  )
237
- # ----- Right Column (β‰ˆ70%) -----
238
- with gr.Column(scale=2):
239
- image_input = gr.Image(label="πŸ“‚ Upload Tomato Leaf Image", type="pil")
240
- camera_input = gr.Image(label="πŸ“Έ Use Camera (Live Preview)", type="pil")
241
- submit = gr.Button("πŸ” Analyze")
242
-
243
- output = gr.Textbox(label="πŸ“ Diagnosis & Advice", lines=8)
244
-
245
- # Update CSS dynamically based on theme selection.
 
 
 
 
 
 
 
 
 
246
  theme_choice.change(fn=update_css, inputs=theme_choice, outputs=css_injector)
247
-
248
- # When submit is clicked, combine image inputs and process the selected version.
249
  submit.click(
250
  fn=lambda uploaded, camera, ver: process_version(combine_images(uploaded, camera), ver),
251
  inputs=[image_input, camera_input, version],
252
  outputs=output
253
  )
254
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
255
  demo.launch()
 
2
  import gradio as gr
3
  import numpy as np
4
  import requests
5
+ import json
6
+ import time
7
  from tensorflow.keras.models import load_model
8
  from PIL import Image
9
 
10
+ # ===== API Configuration =====
11
+ # Try to get API tokens from environment variables
12
+ HF_API_TOKEN = os.getenv("HUGGINGFACE_TOKEN") # Hugging Face API token
13
+ GROQ_API_KEY = os.getenv("GROQ_API_KEY") # Groq API key
14
+ OPENAI_API_KEY = os.getenv("OPENAI_API_KEY") # OpenAI API key (fallback)
15
+
16
+ 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'}")
17
+
18
+ # ===== Disease Information Database =====
19
+ disease_info = {
20
+ "Tomato Bacterial Spot": {
21
+ "description": "A bacterial disease that causes small, dark spots on leaves, stems, and fruits.",
22
+ "causes": "Caused by Xanthomonas bacteria, spread by water splash, contaminated tools, and seeds.",
23
+ "treatment": [
24
+ "Remove and destroy infected plants",
25
+ "Rotate crops with non-solanaceous plants",
26
+ "Use copper-based fungicides",
27
+ "Avoid overhead irrigation"
28
+ ]
29
+ },
30
+ "Tomato Early Blight": {
31
+ "description": "A fungal disease that causes dark spots with concentric rings on lower leaves first.",
32
+ "causes": "Caused by Alternaria solani fungus, favored by warm, humid conditions.",
33
+ "treatment": [
34
+ "Remove infected leaves promptly",
35
+ "Improve air circulation around plants",
36
+ "Apply fungicides preventatively",
37
+ "Mulch around plants to prevent soil splash"
38
+ ]
39
+ },
40
+ "Tomato Late Blight": {
41
+ "description": "A devastating fungal disease that causes dark, water-soaked lesions on leaves and fruits.",
42
+ "causes": "Caused by Phytophthora infestans, favored by cool, wet conditions.",
43
+ "treatment": [
44
+ "Remove and destroy infected plants immediately",
45
+ "Apply fungicides preventatively in humid conditions",
46
+ "Improve drainage and air circulation",
47
+ "Plant resistant varieties when available"
48
+ ]
49
+ },
50
+ "Tomato Mosaic Virus": {
51
+ "description": "A viral disease that causes mottled green/yellow patterns on leaves and stunted growth.",
52
+ "causes": "Caused by tobacco mosaic virus (TMV), spread by handling, tools, and sometimes seeds.",
53
+ "treatment": [
54
+ "Remove and destroy infected plants",
55
+ "Wash hands and tools after handling infected plants",
56
+ "Control insect vectors like aphids",
57
+ "Plant resistant varieties"
58
+ ]
59
+ },
60
+ "Tomato Yellow Leaf Curl Virus": {
61
+ "description": "A viral disease transmitted by whiteflies that causes yellowing and curling of leaves.",
62
+ "causes": "Caused by a begomovirus, transmitted primarily by whiteflies.",
63
+ "treatment": [
64
+ "Use whitefly control measures",
65
+ "Remove and destroy infected plants",
66
+ "Use reflective mulches to repel whiteflies",
67
+ "Plant resistant varieties"
68
+ ]
69
+ },
70
+ "Tomato___Target_Spot": {
71
+ "description": "A fungal disease causing circular lesions with concentric rings on leaves, stems, and fruits.",
72
+ "causes": "Caused by Corynespora cassiicola fungus, favored by warm, humid conditions.",
73
+ "treatment": [
74
+ "Remove infected plant parts",
75
+ "Improve air circulation",
76
+ "Apply fungicides at first sign of disease",
77
+ "Avoid overhead irrigation"
78
+ ]
79
+ },
80
+ "Tomato___Bacterial_spot": {
81
+ "description": "A bacterial disease causing small, dark, water-soaked spots on leaves, stems, and fruits.",
82
+ "causes": "Caused by Xanthomonas species, spread by water splash and contaminated tools.",
83
+ "treatment": [
84
+ "Remove infected plant debris",
85
+ "Use copper-based bactericides",
86
+ "Rotate crops",
87
+ "Use disease-free seeds and transplants"
88
+ ]
89
+ },
90
+ "Tomato___healthy": {
91
+ "description": "The plant shows no signs of disease and appears to be in good health.",
92
+ "causes": "Proper growing conditions, good management practices, and disease prevention.",
93
+ "treatment": [
94
+ "Continue regular watering and fertilization",
95
+ "Monitor for early signs of disease",
96
+ "Maintain good air circulation",
97
+ "Practice crop rotation"
98
+ ]
99
+ }
100
+ }
101
 
102
  # ===== Load Trained Models =====
103
  model_a = load_model("Tomato_Leaf_Disease_Model.h5")
 
116
  return img_array
117
 
118
  # ===== Disease Label Mappings =====
119
+ # Model A labels
120
  disease_labels_a = {
121
  0: "Tomato Bacterial Spot",
122
  1: "Tomato Early Blight",
 
125
  4: "Tomato Yellow Leaf Curl Virus"
126
  }
127
 
128
+ # Model B labels
129
  disease_labels_b = {
130
  0: "Tomato___Target_Spot",
131
  1: "Tomato___Bacterial_spot",
 
139
  img = preprocess_image(image)
140
  pred = model_a.predict(img)
141
  predicted_class = np.argmax(pred)
142
+ confidence = float(np.max(pred) * 100)
143
+ return disease_labels_a.get(predicted_class, "Unknown result"), confidence
144
 
145
  def predict_model_b(image):
146
  img = preprocess_image(image)
147
  pred = model_b.predict(img)
148
  predicted_class = np.argmax(pred)
149
+ confidence = float(np.max(pred) * 100)
150
+ return disease_labels_b.get(predicted_class, "Unknown result"), confidence
151
 
152
  def predict_classifier(image):
153
  img = preprocess_image(image)
 
155
  # Here we assume the classifier returns class 1 for "Tomato Leaf"
156
  return "Tomato Leaf" if np.argmax(pred) == 1 else "Not Tomato Leaf"
157
 
158
+ # ===== AI Model API Calls =====
159
+ def get_ai_advice(prompt, retries=2):
160
+ """Try multiple AI models with fallback mechanisms"""
161
+
162
+ # Try Groq API first (if key available)
163
+ if GROQ_API_KEY:
164
+ try:
165
+ headers = {
166
+ "Authorization": f"Bearer {GROQ_API_KEY}",
167
+ "Content-Type": "application/json"
168
+ }
169
+
170
+ payload = {
171
+ "model": "llama3-8b-8192", # Using Llama 3 8B model
172
+ "messages": [
173
+ {"role": "system", "content": "You are an expert agricultural advisor specializing in tomato farming."},
174
+ {"role": "user", "content": prompt}
175
+ ],
176
+ "max_tokens": 800,
177
+ "temperature": 0.7
178
+ }
179
+
180
+ response = requests.post(
181
+ "https://api.groq.com/openai/v1/chat/completions",
182
+ headers=headers,
183
+ json=payload,
184
+ timeout=30
185
+ )
186
+
187
+ if response.status_code == 200:
188
+ result = response.json()
189
+ if "choices" in result and len(result["choices"]) > 0:
190
+ return result["choices"][0]["message"]["content"]
191
+
192
+ print(f"Groq API error: {response.status_code} - {response.text}")
193
+
194
+ except Exception as e:
195
+ print(f"Error with Groq API: {str(e)}")
196
+
197
+ # Try Hugging Face Inference API as first fallback (if token available)
198
+ if HF_API_TOKEN:
199
+ try:
200
+ headers = {"Authorization": f"Bearer {HF_API_TOKEN}"}
201
+
202
+ # Format prompt for instruction-tuned models
203
+ formatted_prompt = f"""<s>[INST] {prompt} [/INST]"""
204
+
205
+ payload = {
206
+ "inputs": formatted_prompt,
207
+ "parameters": {
208
+ "max_new_tokens": 800,
209
+ "temperature": 0.7,
210
+ "top_p": 0.95,
211
+ "do_sample": True
212
+ }
213
+ }
214
+
215
+ # Try Mistral model first
216
+ url = "https://api-inference.huggingface.co/models/mistralai/Mistral-7B-Instruct-v0.2"
217
+
218
+ response = requests.post(url, headers=headers, json=payload, timeout=30)
219
+
220
+ if response.status_code == 200:
221
+ result = response.json()
222
+ if isinstance(result, list) and len(result) > 0:
223
+ if "generated_text" in result[0]:
224
+ # Extract just the response part (after the prompt)
225
+ generated_text = result[0]["generated_text"]
226
+ # Remove the prompt from the response
227
+ response_text = generated_text.split("[/INST]")[-1].strip()
228
+ return response_text
229
+
230
+ # If Mistral fails, try Llama 3
231
+ url = "https://api-inference.huggingface.co/models/meta-llama/Meta-Llama-3-8B-Instruct"
232
+ response = requests.post(url, headers=headers, json=payload, timeout=30)
233
+
234
+ if response.status_code == 200:
235
+ result = response.json()
236
+ if isinstance(result, list) and len(result) > 0:
237
+ if "generated_text" in result[0]:
238
+ generated_text = result[0]["generated_text"]
239
+ response_text = generated_text.split("[/INST]")[-1].strip()
240
+ return response_text
241
+
242
+ except Exception as e:
243
+ print(f"Error with Hugging Face API: {str(e)}")
244
+
245
+ # Try OpenAI API as final fallback (if key available)
246
+ if OPENAI_API_KEY:
247
+ try:
248
+ headers = {
249
+ "Authorization": f"Bearer {OPENAI_API_KEY}",
250
+ "Content-Type": "application/json"
251
+ }
252
+
253
+ payload = {
254
+ "model": "gpt-3.5-turbo",
255
+ "messages": [
256
+ {"role": "system", "content": "You are an expert agricultural advisor specializing in tomato farming."},
257
+ {"role": "user", "content": prompt}
258
+ ],
259
+ "max_tokens": 800,
260
+ "temperature": 0.7
261
+ }
262
+
263
+ response = requests.post(
264
+ "https://api.openai.com/v1/chat/completions",
265
+ headers=headers,
266
+ json=payload,
267
+ timeout=30
268
+ )
269
+
270
+ if response.status_code == 200:
271
+ result = response.json()
272
+ if "choices" in result and len(result["choices"]) > 0:
273
+ return result["choices"][0]["message"]["content"]
274
+
275
+ except Exception as e:
276
+ print(f"Error with OpenAI API: {str(e)}")
277
+
278
+ # If all API calls fail, use the fallback information from our database
279
+ disease_name = prompt.split("disease has been detected: ")[-1].split(" with")[0] if "disease has been detected:" in prompt else ""
280
+
281
+ if disease_name and disease_name in disease_info:
282
+ info = disease_info[disease_name]
283
+ return f"""
284
+ # {disease_name}
285
+
286
+ ## Description
287
+ {info['description']}
288
+
289
+ ## Causes
290
+ {info['causes']}
291
+
292
+ ## Recommended Treatment
293
+ {chr(10).join(f"- {rec}" for rec in info['treatment'])}
294
+
295
+ *Note: This is fallback information as our AI service is currently unavailable.*
296
+ """
297
  else:
298
+ # Generic fallback response
299
+ return """
300
+ # Agricultural Advice
301
+
302
+ I apologize, but I'm currently unable to connect to our AI service. Here are some general tips for tomato plant care:
303
+
304
+ ## General Tomato Care Tips
305
+ - Water consistently, aiming for 1-2 inches per week
306
+ - Provide support with stakes or cages
307
+ - Fertilize regularly with balanced fertilizer
308
+ - Remove suckers for indeterminate varieties
309
+ - Monitor for pests and diseases regularly
310
+ - Ensure good air circulation between plants
311
+ - Mulch to retain moisture and prevent soil-borne diseases
312
+
313
+ Please try again later for more specific advice.
314
+ """
315
 
316
  # ===== AI Assistant Functions =====
317
+ def generate_disease_advice(disease_name, confidence):
318
+ """Generate advice for a specific disease with confidence level."""
319
+ if "healthy" in disease_name.lower():
320
  prompt = (
321
+ "You are an agricultural advisor speaking to a farmer. "
322
+ "The tomato crop appears healthy. "
323
+ "Provide detailed preventive tips and best practices for maintaining tomato crop health. "
324
+ "Include information about watering, fertilization, pest prevention, and optimal growing conditions. "
325
+ "Format your response in clear sections with bullet points where appropriate."
326
  )
327
  else:
328
  prompt = (
329
+ f"You are an agricultural advisor speaking to a farmer. "
330
+ f"A disease has been detected in their tomato crop: {disease_name} with {confidence:.1f}% confidence. "
331
+ f"Provide detailed advice on how to identify, manage and treat this disease. "
332
+ f"Include information about: "
333
+ f"1) What causes this disease "
334
+ f"2) How it spreads "
335
+ f"3) Specific treatments (both organic and chemical options) "
336
+ f"4) Preventive measures for the future "
337
+ f"Format your response in clear sections with bullet points where appropriate."
338
  )
 
339
 
340
+ return get_ai_advice(prompt)
341
+
342
+ def chat_with_farmer(message, chat_history):
343
+ """Handle chat interactions with farmers about agricultural topics."""
344
+ if not message.strip():
345
+ return "", chat_history
346
+
347
+ # Prepare context from chat history
348
+ context = "\n".join([f"Farmer: {q}\nAdvisor: {a}" for q, a in chat_history[-3:]]) # Use last 3 exchanges for context
349
+
350
+ prompt = (
351
+ f"You are FarmAssist, an expert agricultural advisor specializing in tomato farming and plant diseases. "
352
+ f"You provide helpful, accurate, and practical advice to farmers. "
353
+ f"Always be respectful and considerate of farmers' knowledge while providing expert guidance. "
354
+ f"If you're unsure about something, acknowledge it and provide the best information you can. "
355
+ f"Previous conversation:\n{context}\n\n"
356
+ f"Farmer's new question: {message}\n\n"
357
+ f"Provide a helpful, informative response about farming, focusing on tomatoes if relevant."
358
+ )
359
+
360
+ response = get_ai_advice(prompt)
361
+ chat_history.append((message, response))
362
+ return "", chat_history
363
 
364
  # ===== Process Function Based on Version =====
365
  def process_version(image, version):
366
  if image is None:
367
  return "No image provided."
368
+
369
  # --- Version 1.x (Model A) ---
370
  if version == "1.1":
371
+ result, confidence = predict_model_a(image)
372
+ 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"
373
+
374
  elif version == "1.2":
375
+ result, confidence = predict_model_a(image)
376
+ advice = generate_disease_advice(result, confidence)
377
+ return f"Model A Prediction: {result} (Confidence: {confidence:.1f}%)\n\nExpert Advice:\n{advice}"
378
+
379
  elif version == "1.3":
380
  cls_result = predict_classifier(image)
381
  if cls_result != "Tomato Leaf":
382
+ return "Classifier: The image is not a tomato leaf. Please try again with a tomato leaf image."
383
+
384
+ result, confidence = predict_model_a(image)
385
+ advice = generate_disease_advice(result, confidence)
386
  return (
387
+ f"Classifier: {cls_result}\n"
388
+ f"Model A Prediction: {result} (Confidence: {confidence:.1f}%)\n\n"
389
+ f"Expert Advice:\n{advice}\n\n"
390
+ f"[View Model A & Classifier Training Notebook](https://colab.research.google.com/drive/1FMjs7JmdO6WVoXbzLA-ymwnIKq-GaV6w?usp=sharing)"
391
  )
392
+
393
  # --- Version 2.x (Model B) ---
394
  elif version == "2.1":
395
+ result, confidence = predict_model_b(image)
396
+ 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)"
397
+
398
  elif version == "2.2":
399
+ result, confidence = predict_model_b(image)
400
+ advice = generate_disease_advice(result, confidence)
401
+ return f"Model B Prediction: {result} (Confidence: {confidence:.1f}%)\n\nExpert Advice:\n{advice}"
402
+
403
  elif version == "2.3":
404
  cls_result = predict_classifier(image)
405
  if cls_result != "Tomato Leaf":
406
+ return "Classifier: The image is not a tomato leaf. Please try again with a tomato leaf image."
407
+
408
+ result, confidence = predict_model_b(image)
409
+ advice = generate_disease_advice(result, confidence)
410
  return (
411
+ f"Classifier: {cls_result}\n"
412
+ f"Model B Prediction: {result} (Confidence: {confidence:.1f}%)\n\n"
413
+ f"Expert Advice:\n{advice}\n\n"
414
  f"[View Model B & Classifier Training Notebook](https://colab.research.google.com/drive/1CvoQY40gK2YsMgt4wq9kM2ZSO2c4lzFU?usp=sharing)"
415
  )
416
+
417
  else:
418
  return "Invalid version selected."
419
 
 
424
  # ===== CSS for Theme Switching =====
425
  light_css = """
426
  <style>
427
+ body { background-color: white; color: black; }
428
+ .gr-button { background-color: #4CAF50; color: white; }
429
+ .gr-input, .gr-textbox, .gr-dropdown, .gr-radio, .gr-markdown, .gr-container { background-color: white; color: black; }
430
  </style>
431
  """
432
 
433
  dark_css = """
434
  <style>
435
+ body { background-color: #121212 !important; color: #e0e0e0 !important; }
436
+ .gr-button { background-color: #555 !important; color: white !important; }
437
+ .gr-input, .gr-textbox, .gr-dropdown, .gr-radio, .gr-markdown, .gr-container { background-color: #333 !important; color: #e0e0e0 !important; }
438
  </style>
439
  """
440
 
 
448
  with gr.Blocks() as demo:
449
  # Hidden element for CSS injection (initially Light theme)
450
  css_injector = gr.HTML(update_css("Light"))
451
+
452
+ gr.Markdown("# 🌿 FarMVi8ioN – AI-powered Crop Monitoring")
453
  gr.Markdown("Detect tomato leaf diseases and get actionable advice on how to curb them.")
454
+
455
+ with gr.Tabs():
456
+ # === Disease Detection Tab ===
457
+ with gr.TabItem("Disease Detection"):
458
+ with gr.Row():
459
+ # ----- Left Column (β‰ˆ30%) -----
460
+ with gr.Column(scale=1):
461
+ version = gr.Dropdown(
462
+ choices=["1.1", "1.2", "1.3", "2.1", "2.2", "2.3"],
463
+ label="Select Version",
464
+ value="1.3",
465
+ info="Versions 1.x use Model A; Versions 2.x use Model B."
466
+ )
467
+
468
+ theme_choice = gr.Radio(
469
+ choices=["Light", "Dark"],
470
+ label="Select Theme",
471
+ value="Light"
472
+ )
473
+
474
+ gr.Markdown("### Notebook Links")
475
+ gr.Markdown(
476
+ """
477
+ **For Model A:**
478
+ - Model A Only: [Training Notebook](https://colab.research.google.com/drive/1FMjs7JmdO6WVoXbzLA-ymwnIKq-GaV6w?usp=sharing)
479
+ - Model A & Classifier: [Training Notebook](https://colab.research.google.com/drive/1CvoQY40gK2YsMgt4wq9kM2ZSO2c4lzFU?usp=sharing)
480
+
481
+ **For Model B:**
482
+ - Model B Only: [Training Notebook](https://colab.research.google.com/drive/1CvoQY40gK2YsMgt4wq9kM2ZSO2c4lzFU?usp=sharing)
483
+ - Model B & Classifier: [Training Notebook](https://colab.research.google.com/drive/1CvoQY40gK2YsMgt4wq9kM2ZSO2c4lzFU?usp=sharing)
484
+ """
485
+ )
486
+
487
+ # ----- Right Column (β‰ˆ70%) -----
488
+ with gr.Column(scale=2):
489
+ image_input = gr.Image(label="πŸ“‚ Upload Tomato Leaf Image", type="pil")
490
+ camera_input = gr.Image(label="πŸ“Έ Use Camera (Live Preview)", type="pil", sources=["webcam"])
491
+ submit = gr.Button("πŸ” Analyze", variant="primary")
492
+ output = gr.Markdown(label="πŸ“ Diagnosis & Advice")
493
+
494
+ # === Farmer Chat Tab ===
495
+ with gr.TabItem("Chat with Farm Assistant"):
496
+ gr.Markdown("# πŸ’¬ Chat with Farm Assistant")
497
+ gr.Markdown("Ask any questions about farming, crop diseases, or agricultural practices.")
498
+
499
+ chatbot = gr.Chatbot(
500
+ label="Chat History",
501
+ height=400,
502
+ bubble_full_width=False,
503
+ show_copy_button=True
504
  )
505
+
506
+ with gr.Row():
507
+ chat_input = gr.Textbox(
508
+ label="Your Question",
509
+ placeholder="Ask about tomato farming, diseases, or agricultural practices...",
510
+ lines=2
511
+ )
512
+ chat_button = gr.Button("Send", variant="primary")
513
+
514
+ gr.Markdown("""
515
+ ### Example Questions:
516
+ - How often should I water my tomato plants?
517
+ - What's the best fertilizer for tomatoes?
518
+ - How do I prevent early blight?
519
+ - What are the signs of nutrient deficiency in tomatoes?
520
+ """)
521
+
522
+ # Update CSS dynamically based on theme selection
523
  theme_choice.change(fn=update_css, inputs=theme_choice, outputs=css_injector)
524
+
525
+ # When submit is clicked, combine image inputs and process the selected version
526
  submit.click(
527
  fn=lambda uploaded, camera, ver: process_version(combine_images(uploaded, camera), ver),
528
  inputs=[image_input, camera_input, version],
529
  outputs=output
530
  )
531
 
532
+ # Chat functionality
533
+ chat_button.click(
534
+ fn=chat_with_farmer,
535
+ inputs=[chat_input, chatbot],
536
+ outputs=[chat_input, chatbot]
537
+ )
538
+
539
+ # Also allow pressing Enter to send chat
540
+ chat_input.submit(
541
+ fn=chat_with_farmer,
542
+ inputs=[chat_input, chatbot],
543
+ outputs=[chat_input, chatbot]
544
+ )
545
+
546
+ # Launch the app
547
  demo.launch()