Jeff28 commited on
Commit
d6edddf
·
verified ·
1 Parent(s): 8d170a5

Update app.py with real-time chat functionality

Browse files
Files changed (1) hide show
  1. app.py +260 -436
app.py CHANGED
@@ -1,26 +1,42 @@
1
  import os
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",
@@ -30,7 +46,7 @@ disease_info = {
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",
@@ -40,7 +56,7 @@ disease_info = {
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",
@@ -50,7 +66,7 @@ disease_info = {
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",
@@ -60,295 +76,188 @@ disease_info = {
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")
104
- model_b = load_model("tomato_leaf_model_final(77%).h5")
105
- classifier_model = load_model("tomato_leaf_classifier_optimized.h5")
106
-
107
- # ===== Preprocessing Function =====
108
- def preprocess_image(image, target_size=(224, 224)):
109
- # Ensure the image is resized and normalized.
110
- if isinstance(image, Image.Image):
111
- img = image.resize(target_size)
112
- else:
113
- img = Image.fromarray(image).resize(target_size)
114
- img_array = np.array(img) / 255.0
115
- img_array = np.expand_dims(img_array, axis=0)
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",
123
- 2: "Tomato Late Blight",
124
- 3: "Tomato Mosaic Virus",
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",
132
- 2: "Tomato___Early_blight",
133
- 3: "Tomato___healthy",
134
- 4: "Tomato___Late_blight"
135
- }
136
 
137
- # ===== Prediction Functions =====
138
- def predict_model_a(image):
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)
154
- pred = classifier_model.predict(img)
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. "
@@ -357,191 +266,106 @@ def chat_with_farmer(message, chat_history):
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
-
420
- # ===== Helper Function to Choose Between Uploaded & Camera Image =====
421
- def combine_images(uploaded, camera):
422
- return camera if camera is not None else uploaded
423
-
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
 
441
- def update_css(theme):
442
- if theme == "Dark":
443
- return dark_css
444
- else:
445
- return light_css
446
 
447
- # ===== Gradio Interface =====
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()
 
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
  "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
  "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
  "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
  "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": [
120
+ {"role": "system", "content": "You are an expert agricultural advisor specializing in tomato farming and plant diseases."},
121
+ {"role": "user", "content": prompt}
122
+ ],
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",
130
+ headers=headers,
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": {
161
+ "max_new_tokens": 500,
162
+ "temperature": 0.7,
163
+ "top_p": 0.95,
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:
176
+ if "generated_text" in result[0]:
177
+ # Extract just the response part (after the prompt)
178
+ generated_text = result[0]["generated_text"]
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. "
 
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()