Shafaq25 commited on
Commit
dcae033
Β·
verified Β·
1 Parent(s): 426b7cb

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +520 -0
app.py ADDED
@@ -0,0 +1,520 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import gradio as gr
3
+ import requests
4
+ import openai
5
+
6
+ # πŸ” Load API keys from environment variables (set these in Hugging Face Secrets)
7
+ weather_api_key = os.getenv("OPENWEATHER_API_KEY")
8
+ groq_api_key = os.getenv("GROQ_API_KEY")
9
+ openai.api_key = os.getenv("OPENAI_API_KEY")
10
+ serper_api_key = os.getenv("SERPER_API_KEY")
11
+
12
+ # 🌀️ Weather Fetch
13
+ def get_weather(city_name):
14
+ if not city_name.strip():
15
+ city_name = "Dubai"
16
+ try:
17
+ url = f"https://api.openweathermap.org/data/2.5/weather?q={city_name}&appid={weather_api_key}&units=metric"
18
+ data = requests.get(url).json()
19
+
20
+ if data["cod"] == 200:
21
+ rain = data.get("rain", {}).get("1h", 0)
22
+ condition = data["weather"][0]["main"]
23
+ emoji_map = {
24
+ "Clear": "β˜€οΈ", "Clouds": "☁️", "Rain": "🌧️",
25
+ "Snow": "❄️", "Thunderstorm": "β›ˆοΈ", "Drizzle": "🌦️",
26
+ "Mist": "🌫️", "Haze": "🌁", "Fog": "🌫️"
27
+ }
28
+ emoji = emoji_map.get(condition, "🌈")
29
+
30
+ return {
31
+ "city": data["name"],
32
+ "country": data["sys"]["country"],
33
+ "temperature": int(data["main"]["temp"]),
34
+ "feels_like": int(data["main"]["feels_like"]),
35
+ "humidity": data["main"]["humidity"],
36
+ "pressure": data["main"]["pressure"],
37
+ "description": f"{data['weather'][0]['description'].title()} {emoji}",
38
+ "wind_speed": data["wind"]["speed"],
39
+ "visibility": data.get("visibility", 10000) // 1000,
40
+ "rain_chance": f"{rain} mm"
41
+ }
42
+ else:
43
+ return None
44
+ except:
45
+ return None
46
+
47
+ # πŸ–ΌοΈ Format Weather Display
48
+ def format_weather_display(data):
49
+ if not data:
50
+ return "<div style='text-align:center; color: #e74c3c; font-size: 18px; padding: 40px;'>❌ City not found. Please try again.</div>"
51
+
52
+ font_color = "#2d3436"
53
+ card_bg = "#e8f5e9"
54
+ main_bg = "#ffffff"
55
+
56
+ return f"""
57
+ <div style="background: {main_bg}; border-radius: 16px; padding: 25px; box-shadow: 0 10px 30px rgba(0,0,0,0.1);">
58
+ <div style="text-align: center; margin-bottom: 25px;">
59
+ <h2 style="margin: 0; color: {font_color}; font-size: 24px; font-weight: 600;">πŸ“ {data['city']}, {data['country']}</h2>
60
+ <h1 style="margin: 10px 0; font-size: 64px; color: {font_color}; font-weight: 300;">{data['temperature']}Β°C</h1>
61
+ <p style="margin: 5px 0; color: {font_color}; font-size: 18px; font-weight: 500;">{data['description']}</p>
62
+ </div>
63
+
64
+ <div style="display: grid; grid-template-columns: repeat(3, 1fr); gap: 15px; margin-top: 25px;">
65
+ <div style="background: {card_bg}; border-radius: 12px; padding: 16px; text-align: center; color: {font_color};">
66
+ <div style="font-size: 24px; margin-bottom: 8px;">πŸ’§</div>
67
+ <strong style="font-size: 16px; display: block;">{data['rain_chance']}</strong>
68
+ <span style="font-size: 12px; opacity: 0.8;">Precipitation</span>
69
+ </div>
70
+ <div style="background: {card_bg}; border-radius: 12px; padding: 16px; text-align: center; color: {font_color};">
71
+ <div style="font-size: 24px; margin-bottom: 8px;">πŸ“Š</div>
72
+ <strong style="font-size: 16px; display: block;">{data['pressure']} mb</strong>
73
+ <span style="font-size: 12px; opacity: 0.8;">Pressure</span>
74
+ </div>
75
+ <div style="background: {card_bg}; border-radius: 12px; padding: 16px; text-align: center; color: {font_color};">
76
+ <div style="font-size: 24px; margin-bottom: 8px;">πŸ’¨</div>
77
+ <strong style="font-size: 16px; display: block;">{data['wind_speed']} km/h</strong>
78
+ <span style="font-size: 12px; opacity: 0.8;">Wind Speed</span>
79
+ </div>
80
+ <div style="background: {card_bg}; border-radius: 12px; padding: 16px; text-align: center; color: {font_color};">
81
+ <div style="font-size: 24px; margin-bottom: 8px;">🌑️</div>
82
+ <strong style="font-size: 16px; display: block;">{data['feels_like']}Β°C</strong>
83
+ <span style="font-size: 12px; opacity: 0.8;">Feels Like</span>
84
+ </div>
85
+ <div style="background: {card_bg}; border-radius: 12px; padding: 16px; text-align: center; color: {font_color};">
86
+ <div style="font-size: 24px; margin-bottom: 8px;">πŸ‘οΈ</div>
87
+ <strong style="font-size: 16px; display: block;">{data['visibility']} km</strong>
88
+ <span style="font-size: 12px; opacity: 0.8;">Visibility</span>
89
+ </div>
90
+ <div style="background: {card_bg}; border-radius: 12px; padding: 16px; text-align: center; color: {font_color};">
91
+ <div style="font-size: 24px; margin-bottom: 8px;">πŸ’¦</div>
92
+ <strong style="font-size: 16px; display: block;">{data['humidity']}%</strong>
93
+ <span style="font-size: 12px; opacity: 0.8;">Humidity</span>
94
+ </div>
95
+ </div>
96
+ </div>
97
+ """
98
+
99
+ # 🌾 Farming Assistant Chatbot (Groq API)
100
+ def agri_chat(msg, history):
101
+ prompt = f"You're a smart farming assistant. Help the farmer clearly and briefly.\nUser: {msg}\nAssistant:"
102
+ url = "https://api.groq.com/openai/v1/chat/completions"
103
+ headers = {"Authorization": f"Bearer {groq_api_key}", "Content-Type": "application/json"}
104
+ body = {
105
+ "model": "llama3-8b-8192",
106
+ "messages": [{"role": "user", "content": prompt}],
107
+ "temperature": 0.7
108
+ }
109
+ try:
110
+ res = requests.post(url, headers=headers, json=body).json()
111
+ reply = res["choices"][0]["message"]["content"]
112
+ except:
113
+ reply = "⚠️ Unable to get response. Try again."
114
+ history.append({"role": "user", "content": msg})
115
+ history.append({"role": "assistant", "content": reply})
116
+ return history, history
117
+
118
+ # 🌱 Crop Search (OpenAI)
119
+ def search_crop(crop_name):
120
+ try:
121
+ messages = [
122
+ {
123
+ "role": "system",
124
+ "content": (
125
+ "You are an expert agronomist. When asked about a crop, provide the following in well-structured HTML:\n\n"
126
+ "1. A short paragraph (3–5 sentences) about the crop, including where it's commonly grown and its basic growing needs.\n"
127
+ "2. A bullet list of 3–5 benefits of growing or consuming the crop.\n"
128
+ "3. A numbered list (5–7 steps) explaining how to grow this crop from seed to harvest.\n\n"
129
+ "Use proper HTML structure with <h3>, <p>, <ul>, <ol>, <li> tags."
130
+ )
131
+ },
132
+ {
133
+ "role": "user",
134
+ "content": f"Tell me about {crop_name}. Include a short paragraph, key benefits, and growing steps."
135
+ }
136
+ ]
137
+
138
+ response = openai.chat.completions.create(
139
+ model="gpt-3.5-turbo",
140
+ messages=messages
141
+ )
142
+
143
+ return response.choices[0].message.content.strip()
144
+
145
+ except Exception as e:
146
+ return f"<div style='color:red;'>⚠️ Error fetching crop info: {str(e)}</div>"
147
+
148
+
149
+ # 🌱 Fetch best crops based on region using OpenAI
150
+ def get_best_crops_for_region(city, country, temp, humidity, description):
151
+ try:
152
+ messages = [
153
+ {
154
+ "role": "system",
155
+ "content": (
156
+ "You are an expert agronomist. Based on the region, temperature, humidity, and weather condition, "
157
+ "suggest 6 to 9 crops that grow best in this region. Return each crop as a Python tuple like:\n"
158
+ "(\"Tomato\", \"Needs warm weather, full sun, and well-drained soil.\")"
159
+ )
160
+ },
161
+ {
162
+ "role": "user",
163
+ "content": (
164
+ f"Suggest best crops for {city}, {country} where the average temperature is {temp}Β°C, "
165
+ f"humidity is {humidity}%, and the weather condition is '{description.lower()}'. "
166
+ "Output the results as Python tuples."
167
+ )
168
+ }
169
+ ]
170
+
171
+ response = openai.chat.completions.create(
172
+ model="gpt-3.5-turbo",
173
+ messages=messages
174
+ )
175
+
176
+ content = response.choices[0].message.content.strip()
177
+
178
+ crops = []
179
+ for line in content.split('\n'):
180
+ line = line.strip().rstrip(',')
181
+ if line.startswith('(') and line.endswith(')'):
182
+ try:
183
+ crop_tuple = eval(line)
184
+ if isinstance(crop_tuple, tuple) and len(crop_tuple) == 2:
185
+ crops.append(crop_tuple)
186
+ except:
187
+ continue
188
+ return crops if crops else [("⚠️ No Crops Found", "Try a different city or adjust your weather input.")]
189
+
190
+ except Exception as e:
191
+ return [("⚠️ Error", f"Could not fetch crops: {str(e)}")]
192
+
193
+ # 🎨 Format each crop card for the grid layout
194
+ def format_crop_card(name, details):
195
+ return f"""
196
+ <div class='crop-card'>
197
+ <div class='crop-name'>{name.title()}</div>
198
+ <div class='crop-description'>{details}</div>
199
+ </div>
200
+ """
201
+
202
+
203
+ # Launch UI with layout from old code + crop cards and search
204
+ custom_css = """
205
+ body, .gradio-container {
206
+ background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%) !important;
207
+ font-family: 'Segoe UI', 'Roboto', sans-serif;
208
+ min-height: 100vh;
209
+ }
210
+ #main-title {
211
+ text-align: center;
212
+ font-size: 2.5rem;
213
+ font-weight: 700;
214
+ margin-bottom: 10px;
215
+ color: #2e7d32;
216
+ text-shadow: 2px 2px 4px rgba(0,0,0,0.1);
217
+ }
218
+ #subtitle {
219
+ text-align: center;
220
+ font-size: 1.1rem;
221
+ color: #666;
222
+ margin-bottom: 30px;
223
+ font-weight: 400;
224
+ }
225
+ .section-header {
226
+ background: linear-gradient(135deg, #43a047 0%, #388e3c 100%);
227
+ color: white;
228
+ padding: 12px 20px;
229
+ border-radius: 12px 12px 0 0;
230
+ margin: 0;
231
+ font-size: 1.2rem;
232
+ font-weight: 600;
233
+ text-align: center;
234
+ box-shadow: 0 4px 15px rgba(67, 160, 71, 0.3);
235
+ }
236
+ .content-box {
237
+ background-color: #ffffff;
238
+ border-radius: 0 0 16px 16px;
239
+ padding: 25px;
240
+ box-shadow: 0 8px 32px rgba(0, 0, 0, 0.1);
241
+ min-height: 520px;
242
+ border-top: 3px solid #43a047;
243
+ }
244
+ .weather-controls {
245
+ background: #f8f9fa;
246
+ padding: 20px;
247
+ border-radius: 12px;
248
+ margin-bottom: 20px;
249
+ border: 1px solid #e9ecef;
250
+ }
251
+ .footer {
252
+ background: linear-gradient(135deg, #2e7d32 0%, #1b5e20 100%);
253
+ color: white;
254
+ padding: 30px;
255
+ border-radius: 16px;
256
+ margin-top: 30px;
257
+ box-shadow: 0 8px 32px rgba(0, 0, 0, 0.15);
258
+ }
259
+ .footer h3 {
260
+ margin: 0 0 15px 0;
261
+ font-size: 1.3rem;
262
+ font-weight: 600;
263
+ color: #ffffff;
264
+ }
265
+ .footer-content {
266
+ display: grid;
267
+ grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
268
+ gap: 25px;
269
+ margin-bottom: 20px;
270
+ }
271
+ .footer-section {
272
+ background: rgba(255, 255, 255, 0.1);
273
+ padding: 20px;
274
+ border-radius: 12px;
275
+ backdrop-filter: blur(10px);
276
+ }
277
+ .footer-section h4 {
278
+ margin: 0 0 10px 0;
279
+ color: #a8e6cf;
280
+ font-size: 1.1rem;
281
+ }
282
+ .footer-section ul {
283
+ list-style: none;
284
+ padding: 0;
285
+ margin: 0;
286
+ }
287
+ .footer-section li {
288
+ margin: 8px 0;
289
+ padding-left: 20px;
290
+ position: relative;
291
+ }
292
+ .footer-section li:before {
293
+ content: "βœ“";
294
+ position: absolute;
295
+ left: 0;
296
+ color: #a8e6cf;
297
+ font-weight: bold;
298
+ }
299
+ button {
300
+ background: linear-gradient(135deg, #43a047 0%, #388e3c 100%) !important;
301
+ color: white !important;
302
+ border-radius: 10px !important;
303
+ border: none !important;
304
+ padding: 12px 24px !important;
305
+ font-weight: 600 !important;
306
+ transition: all 0.3s ease !important;
307
+ box-shadow: 0 4px 15px rgba(67, 160, 71, 0.3) !important;
308
+ }
309
+ button:hover {
310
+ transform: translateY(-2px) !important;
311
+ box-shadow: 0 6px 20px rgba(67, 160, 71, 0.4) !important;
312
+ }
313
+ .gradio-textbox input {
314
+ border-radius: 10px !important;
315
+ border: 2px solid #e9ecef !important;
316
+ padding: 12px 16px !important;
317
+ font-size: 16px !important;
318
+ transition: all 0.3s ease !important;
319
+ }
320
+ .gradio-textbox input:focus {
321
+ border-color: #43a047 !important;
322
+ box-shadow: 0 0 0 3px rgba(67, 160, 71, 0.1) !important;
323
+ }
324
+ .card-grid {
325
+ display: grid;
326
+ grid-template-columns: repeat(3, 1fr);
327
+ gap: 18px;
328
+ margin-top: 25px;
329
+ }
330
+
331
+ .crop-card {
332
+ background: #ffffff;
333
+ border: 1px solid #c8e6c9;
334
+ border-radius: 16px;
335
+ padding: 20px;
336
+ box-shadow: 0 4px 12px rgba(67, 160, 71, 0.1);
337
+ transition: transform 0.2s ease, box-shadow 0.2s ease;
338
+ }
339
+ .crop-card:hover {
340
+ transform: translateY(-4px);
341
+ box-shadow: 0 8px 20px rgba(67, 160, 71, 0.2);
342
+ }
343
+ .crop-name {
344
+ font-size: 20px;
345
+ font-weight: 600;
346
+ color: #2e7d32;
347
+ margin-bottom: 12px;
348
+ text-align: center;
349
+ }
350
+ .crop-description {
351
+ font-size: 15px;
352
+ color: #444;
353
+ line-height: 1.5;
354
+ text-align: center;
355
+ }
356
+
357
+ """
358
+
359
+ def launch_ui():
360
+ with gr.Blocks(css=custom_css, title="Shafaq's AgriWeather") as demo:
361
+ # Header Section
362
+ gr.Markdown("<div id='main-title'>🌿 Shafaq's AgriWeather Hub</div>")
363
+ gr.Markdown("<div id='subtitle'>Real-time Weather Data & Smart Farming Assistant</div>")
364
+
365
+ # Main Content
366
+ with gr.Row(equal_height=True):
367
+ # Weather Section
368
+ with gr.Column(scale=1):
369
+ gr.Markdown("<div class='section-header'>🌀️ Weather Dashboard</div>")
370
+ with gr.Group(elem_classes="content-box"):
371
+ with gr.Group(elem_classes="weather-controls"):
372
+ city_input = gr.Textbox(
373
+ label="πŸ™οΈ City Name",
374
+ value="Dubai",
375
+ placeholder="Enter city name (e.g., Dubai, London, Tokyo)",
376
+ info="Get real-time weather data for any city worldwide"
377
+ )
378
+ update_btn = gr.Button("πŸ“ Get Weather Data", variant="primary")
379
+ weather_html = gr.HTML()
380
+
381
+ # Chat Section
382
+ with gr.Column(scale=1):
383
+ gr.Markdown("<div class='section-header'>🌾 AgriBot Assistant</div>")
384
+ with gr.Group(elem_classes="content-box"):
385
+ chat = gr.Chatbot(
386
+ height=350,
387
+ type="messages",
388
+ show_label=False,
389
+ bubble_full_width=False,
390
+ avatar_images=("πŸ‘¨β€πŸŒΎ", "πŸ€–")
391
+ )
392
+ with gr.Row():
393
+ message = gr.Textbox(
394
+ placeholder="Ask about crops, weather impact, farming tips...",
395
+ show_label=False,
396
+ scale=4
397
+ )
398
+ ask_btn = gr.Button("Send", variant="primary", scale=1)
399
+ # Quick suggestions
400
+ gr.Markdown("""
401
+ **πŸ’‘ Quick Questions:**
402
+ - "What crops grow best in hot weather?"
403
+ - "How does humidity affect plant growth?"
404
+ - "Best irrigation practices for dry season?"
405
+ """)
406
+
407
+ state = gr.State([])
408
+
409
+ # --- Dynamic Crop Cards Section ---
410
+ gr.Markdown("<div class='section-header'>🌿 Crops That Grow Best in Your Region</div>")
411
+ crop_cards_html = gr.HTML()
412
+
413
+ def generate_crop_cards(city):
414
+ weather = get_weather(city)
415
+ if not weather:
416
+ return "<div style='padding:20px; color:red;'>⚠️ Couldn't fetch crops due to missing weather data.</div>"
417
+
418
+ crops = get_best_crops_for_region(
419
+ city=weather["city"],
420
+ country=weather["country"],
421
+ temp=weather["temperature"],
422
+ humidity=weather["humidity"],
423
+ description=weather["description"]
424
+ )
425
+ return "<div class='card-grid'>" + "".join(format_crop_card(name, details) for name, details in crops) + "</div>"
426
+
427
+
428
+ # --- Crop search section ---
429
+ gr.Markdown("<div class='section-header'>πŸ”Ž Search for Crop Growing Conditions</div>")
430
+ with gr.Group(elem_classes="content-box"):
431
+ with gr.Row():
432
+ crop_search_input = gr.Textbox(
433
+ placeholder="Type a crop name (e.g., Tomato, Wheat)...",
434
+ show_label=False,
435
+ scale=5
436
+ )
437
+ crop_search_btn = gr.Button("πŸ” Search", variant="primary", scale=1)
438
+ crop_search_output = gr.HTML()
439
+
440
+
441
+
442
+ # Footer Section (fixed)
443
+ gr.HTML("""
444
+ <div class='footer'>
445
+ <h3>🌱 About Shafaq's AgriWeather Hub</h3>
446
+ <div class='footer-content'>
447
+ <div class='footer-section'>
448
+ <h4>🌀️ Weather Features</h4>
449
+ <ul>
450
+ <li>Real-time weather updates for any city</li>
451
+ <li>Detailed forecasts: temperature, wind, humidity, etc.</li>
452
+ <li>Smart visual indicators with icons</li>
453
+ <li>Location-based crop recommendations</li>
454
+ </ul>
455
+ </div>
456
+
457
+ <div class='footer-section'>
458
+ <h4>πŸ€– AgriBot Capabilities</h4>
459
+ <ul>
460
+ <li>Ask farming-related questions interactively</li>
461
+ <li>Get irrigation, soil & seasonal guidance</li>
462
+ <li>Pest, disease & crop management tips</li>
463
+ <li>Uses advanced AI (Groq + OpenAI)</li>
464
+ </ul>
465
+ </div>
466
+
467
+ <div class='footer-section'>
468
+ <h4>πŸ”Ž Crop Search Intelligence</h4>
469
+ <ul>
470
+ <li>Search growing conditions for any crop</li>
471
+ <li>Uses trusted sources via Serper API</li>
472
+ <li>Clean layout with links to learn more</li>
473
+ <li>Get contextual crop care information</li>
474
+ </ul>
475
+ </div>
476
+
477
+ <div class='footer-section'>
478
+ <h4>πŸš€ Quick Usage Tips</h4>
479
+ <ul>
480
+ <li>Start by entering a city to get crop advice</li>
481
+ <li>Ask specific crop or weather questions</li>
482
+ <li>Use the crop search for deeper research</li>
483
+ <li>Combine data for informed decisions</li>
484
+ </ul>
485
+ </div>
486
+ </div>
487
+
488
+ <div style='text-align: center; margin-top: 20px; padding-top: 20px; border-top: 1px solid rgba(255,255,255,0.2); color: rgba(255,255,255,0.85); font-size: 14px;'>
489
+ <p>πŸ’š Built by Shafaq Mandha | Powered by OpenWeather, Groq, OpenAI, and Serper APIs | All rights reserved Β© 2025</p>
490
+ </div>
491
+ </div>
492
+ """)
493
+
494
+
495
+ # Event bindings
496
+ update_btn.click(
497
+ fn=lambda city: (format_weather_display(get_weather(city)), generate_crop_cards(city)),
498
+ inputs=city_input,
499
+ outputs=[weather_html, crop_cards_html]
500
+ )
501
+
502
+ city_input.submit(
503
+ fn=lambda city: (format_weather_display(get_weather(city)), generate_crop_cards(city)),
504
+ inputs=city_input,
505
+ outputs=[weather_html, crop_cards_html]
506
+ )
507
+
508
+ ask_btn.click(fn=agri_chat, inputs=[message, state], outputs=[chat, state])
509
+ message.submit(fn=agri_chat, inputs=[message, state], outputs=[chat, state])
510
+ crop_search_btn.click(fn=search_crop, inputs=crop_search_input, outputs=crop_search_output)
511
+
512
+ # Load initial weather on launch
513
+ demo.load(fn=lambda: (format_weather_display(get_weather("Dubai")), generate_crop_cards("Dubai")),
514
+ inputs=None,
515
+ outputs=[weather_html, crop_cards_html])
516
+
517
+
518
+ demo.launch()
519
+
520
+ launch_ui()