Amaanali01 commited on
Commit
36bc3f6
·
verified ·
1 Parent(s): c33bf9a

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +1720 -121
app.py CHANGED
@@ -1,153 +1,1752 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  # ============================================================
2
- # RAG DEMO SECTION hides automatically when image uploaded
3
  # ============================================================
4
- if not uploaded_file:
5
- st.markdown("---")
6
- if is_urdu:
7
- st.markdown("### 🗂️ نمونہ RAG سسٹم — جانوروں کا علمی ذخیرہ")
8
- st.markdown("تصویر اپ لوڈ کرنے سے پہلے، آپ ہمارے علمی ذخیرے سے براہ راست سوال پوچھ سکتے ہیں۔")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
9
  else:
10
- st.markdown("### 🗂️ Sample RAG System Animal Knowledge Base")
11
- st.markdown("Before uploading an image, you can query our knowledge base directly. Upload an image above to start full AI classification.")
 
12
 
13
- # Show available animals from JSON
14
- if animals_json:
15
- available_names = [a["name"] for a in animals_json[:12]] # show up to 12
16
- if is_urdu:
17
- st.markdown(f"**علمی ذخیرے میں موجود جانور:** {', '.join(available_names)}")
18
- else:
19
- st.markdown(f"**Animals in knowledge base:** {', '.join(available_names)}")
20
 
21
- rag_col1, rag_col2 = st.columns([2, 1])
22
 
23
- with rag_col1:
24
- if is_urdu:
25
- rag_query = st.text_input(
26
- "علمی ذخیرے سے سوال کریں:",
27
- placeholder="مثال: شیر کہاں رہتا ہے؟ یا ہاتھی کی خوراک کیا ہے؟",
28
- key="rag_query_input"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
29
  )
 
 
 
 
 
 
 
 
 
 
 
 
30
  else:
31
- rag_query = st.text_input(
32
- "Query the knowledge base:",
33
- placeholder="e.g. Where does a lion live? or What does an elephant eat?",
34
- key="rag_query_input"
 
 
 
 
 
 
 
 
35
  )
36
 
37
- with rag_col2:
38
- st.markdown("<br>", unsafe_allow_html=True)
39
- rag_search_btn = st.button(
40
- "🔍 Search Knowledge Base" if not is_urdu else "🔍 علمی ذخیرہ تلاش کریں",
41
- key="rag_search_btn",
42
- use_container_width=True
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
43
  )
44
 
45
- # Quick sample buttons
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
46
  if is_urdu:
47
- st.markdown("**فوری نمونے:**")
48
- sample_rag_queries = [
49
- "شیر کہاں رہتا ہے؟",
50
- "ہاتھی کیا کھاتا ہے؟",
51
- "زرافہ کتنا جیتا ہے؟",
52
- "ببر شیر معدوم ہے کیا؟"
53
  ]
54
  else:
55
- st.markdown("**Quick samples:**")
56
- sample_rag_queries = [
57
- "Where does a lion live?",
58
- "What does an elephant eat?",
59
- "How long does a giraffe live?",
60
- "Is the tiger endangered?"
61
  ]
62
 
63
- rag_sample_cols = st.columns(4)
64
- for idx, sq in enumerate(sample_rag_queries):
65
- with rag_sample_cols[idx]:
66
- if st.button(sq, key=f"rag_sample_{idx}", use_container_width=True):
67
- st.session_state["rag_active_query"] = sq
68
-
69
- # If a sample was clicked, use it
70
- active_rag_query = st.session_state.get("rag_active_query", None)
71
- final_rag_query = rag_query if rag_search_btn and rag_query else active_rag_query
72
-
73
- if final_rag_query:
74
- # --- RAG Retrieval: find the best matching animal from JSON ---
75
- with st.spinner("🔍 Searching knowledge base..." if not is_urdu else "🔍 علمی ذخیرے میں تلاش ہو رہی ہے..."):
76
- # Simple keyword retrieval from JSON
77
- matched_animal = None
78
- query_lower = final_rag_query.lower()
79
-
80
- if animals_json:
81
- for animal in animals_json:
82
- name_match = animal["name"].lower() in query_lower
83
- tag_match = any(tag.lower() in query_lower for tag in animal.get("tags", []))
84
- if name_match or tag_match:
85
- matched_animal = animal
86
- break
87
 
88
- if matched_animal:
89
- # Determine what field the user is asking about
90
- if any(kw in query_lower for kw in ["eat", "food", "diet", "کھاتا", "خوراک"]):
91
- field_en = matched_animal.get("diet", "N/A")
92
- field_ur = matched_animal.get("diet_ur", field_en)
93
- field_label = "Diet / خوراک"
94
- elif any(kw in query_lower for kw in ["live", "habitat", "where", "رہتا", "رہائش", "کہاں"]):
95
- field_en = matched_animal.get("habitat", "N/A")
96
- field_ur = matched_animal.get("habitat_ur", field_en)
97
- field_label = "Habitat / رہائش گاہ"
98
- elif any(kw in query_lower for kw in ["long", "lifespan", "age", "زندہ", "عمر", "کتنا جیتا"]):
99
- field_en = matched_animal.get("lifespan", "N/A")
100
- field_ur = matched_animal.get("lifespan_ur", field_en)
101
- field_label = "Lifespan / زندگی کی مدت"
102
- elif any(kw in query_lower for kw in ["endanger", "conservation", "extinct", "معدوم", "تحفظ"]):
103
- field_en = matched_animal.get("conservation", "N/A")
104
- field_ur = matched_animal.get("conservation_ur", field_en)
105
- field_label = "Conservation / تحفظ"
106
- else:
107
- field_en = matched_animal.get("fun_fact", "N/A")
108
- field_ur = matched_animal.get("fun_fact_ur", field_en)
109
- field_label = "Fun Fact / دلچسپ حقیقت"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
110
 
111
- answer = field_ur if is_urdu else field_en
112
- animal_name_display = matched_animal["name"]
113
- sci_name = matched_animal.get("scientific_name", "")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
114
 
 
115
  st.markdown(f"""
116
- <div style="background: var(--bg-elevated, #112219); border: 1px solid rgba(0,232,122,0.25);
117
- border-left: 4px solid #00e87a; border-radius: 16px; padding: 1.4rem 1.2rem; margin: 1rem 0;">
118
- <p style="color: #00e87a; font-size: 0.8rem; font-weight: 700; letter-spacing: 0.08em;
119
- text-transform: uppercase; margin: 0 0 0.5rem;">
120
- 📚 RAG RESULT — {animal_name_display} <i style="font-weight:400; color:#7eb894;">({sci_name})</i>
121
- </p>
122
- <p style="color: #7eb894; font-size: 0.78rem; margin: 0 0 0.3rem;">{field_label}</p>
123
- <p style="color: #e8f5ee; font-size: 1rem; margin: 0; line-height: 1.6;">{answer}</p>
124
- <p style="color: #3d6651; font-size: 0.78rem; margin: 0.8rem 0 0;">
125
- ✅ Retrieved from <strong>animals_data.json</strong> · Upload an image above for full AI classification
126
- </p>
 
127
  </div>
128
  """, unsafe_allow_html=True)
129
 
130
- # Also ask Groq to expand the answer
131
  if groq_client:
132
- with st.spinner("🤖 AI expanding answer..." if not is_urdu else "🤖 AI جواب وسیع کر رہا ہے..."):
133
- ai_expansion, _ = get_llm_response(
134
- groq_client, animal_name_display,
135
- final_rag_query, is_urdu,
136
- tokenizer, translation_model
 
 
 
 
 
 
 
137
  )
138
- if is_urdu:
139
- st.markdown(f'<div class="assistant-message">🤖 AI اضافی جواب: {ai_expansion}</div>', unsafe_allow_html=True)
140
- else:
141
- st.markdown(f'<div class="assistant-message">🤖 AI insight: {ai_expansion}</div>', unsafe_allow_html=True)
142
 
143
- # Clear the session query after showing
144
- if rag_search_btn:
145
- st.session_state["rag_active_query"] = None
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
146
 
147
  else:
148
  if is_urdu:
149
- st.warning("⚠️ علمی ذخیرے میں یہ جانور نہیں ملا۔ براہ کرم کسی معروف جانور کا نام استعمال کریں۔")
 
 
 
150
  else:
151
- st.warning("⚠️ Animal not found in knowledge base. Try a known animal name like Lion, Tiger, or Elephant.")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
152
 
153
- st.markdown("---")
 
 
1
+ """
2
+ 🦁 SmartZoo AI - Bilingual Animal Classification with AI Assistant
3
+ English & Urdu Support | Voice Input | Powered by Groq LLM + Helsinki-NLP
4
+ RAG Knowledge Base System Integrated
5
+ """
6
+
7
+ import streamlit as st
8
+ import tensorflow as tf
9
+ import numpy as np
10
+ import pickle
11
+ import os
12
+ import json
13
+ from groq import Groq
14
+ from PIL import Image
15
+ import plotly.graph_objects as go
16
+ from transformers import AutoTokenizer, AutoModelForSeq2SeqLM
17
+ import torch
18
+ import tempfile
19
+
20
  # ============================================================
21
+ # PAGE CONFIGURATION - DARK THEME
22
  # ============================================================
23
+ st.set_page_config(
24
+ page_title="SmartZoo AI - Bilingual Animal Classifier",
25
+ page_icon="🦁",
26
+ layout="wide",
27
+ initial_sidebar_state="expanded"
28
+ )
29
+
30
+ # ============================================================
31
+ # REDESIGNED CSS — Emerald Forest × Midnight Gold Theme
32
+ # ============================================================
33
+ st.markdown("""
34
+ <style>
35
+ @import url('https://fonts.googleapis.com/css2?family=Syne:wght@400;600;700;800&family=DM+Sans:ital,wght@0,300;0,400;0,500;1,300&display=swap');
36
+
37
+ :root {
38
+ --bg-base: #060d0a;
39
+ --bg-surface: #0c1a14;
40
+ --bg-elevated: #112219;
41
+ --bg-card: #0f1f17;
42
+ --accent-primary:#00e87a;
43
+ --accent-gold: #ffc84a;
44
+ --accent-teal: #00c4b4;
45
+ --accent-rose: #ff5e7d;
46
+ --text-primary: #e8f5ee;
47
+ --text-secondary:#7eb894;
48
+ --text-muted: #3d6651;
49
+ --border-glow: rgba(0,232,122,0.25);
50
+ --border-subtle: rgba(0,232,122,0.1);
51
+ --shadow-green: 0 0 40px rgba(0,232,122,0.08);
52
+ --shadow-gold: 0 0 40px rgba(255,200,74,0.12);
53
+ --radius-sm: 8px;
54
+ --radius-md: 16px;
55
+ --radius-lg: 24px;
56
+ --radius-xl: 32px;
57
+ }
58
+
59
+ * { box-sizing: border-box; }
60
+
61
+ html, body, .stApp {
62
+ background-color: var(--bg-base) !important;
63
+ font-family: 'DM Sans', sans-serif !important;
64
+ color: var(--text-primary) !important;
65
+ }
66
+
67
+ [data-testid="stSidebar"] {
68
+ background: var(--bg-surface) !important;
69
+ border-right: 1px solid var(--border-subtle) !important;
70
+ }
71
+
72
+ [data-testid="stSidebar"] * {
73
+ color: var(--text-primary) !important;
74
+ }
75
+
76
+ ::-webkit-scrollbar { width: 4px; }
77
+ ::-webkit-scrollbar-track { background: var(--bg-base); }
78
+ ::-webkit-scrollbar-thumb { background: var(--accent-primary); border-radius: 4px; }
79
+
80
+ .main-header {
81
+ position: relative;
82
+ overflow: hidden;
83
+ background: var(--bg-surface);
84
+ padding: 3rem 2.5rem;
85
+ border-radius: var(--radius-xl);
86
+ margin-bottom: 2rem;
87
+ text-align: center;
88
+ border: 1px solid var(--border-glow);
89
+ box-shadow: var(--shadow-green), inset 0 1px 0 rgba(0,232,122,0.1);
90
+ }
91
+
92
+ .main-header::before {
93
+ content: '';
94
+ position: absolute;
95
+ inset: 0;
96
+ background-image:
97
+ linear-gradient(rgba(0,232,122,0.04) 1px, transparent 1px),
98
+ linear-gradient(90deg, rgba(0,232,122,0.04) 1px, transparent 1px);
99
+ background-size: 32px 32px;
100
+ border-radius: var(--radius-xl);
101
+ pointer-events: none;
102
+ }
103
+
104
+ .main-header::after {
105
+ content: '';
106
+ position: absolute;
107
+ top: -60px; left: 50%;
108
+ transform: translateX(-50%);
109
+ width: 320px; height: 200px;
110
+ background: radial-gradient(ellipse, rgba(0,232,122,0.18) 0%, transparent 70%);
111
+ pointer-events: none;
112
+ }
113
+
114
+ .main-header h1 {
115
+ position: relative;
116
+ font-family: 'Syne', sans-serif;
117
+ font-weight: 800;
118
+ font-size: clamp(2rem, 5vw, 3.5rem);
119
+ color: var(--text-primary);
120
+ letter-spacing: -0.02em;
121
+ margin-bottom: 0.4rem;
122
+ }
123
+
124
+ .main-header h1 .heading-text {
125
+ background: linear-gradient(135deg, #ffffff 30%, var(--accent-primary) 100%);
126
+ -webkit-background-clip: text;
127
+ -webkit-text-fill-color: transparent;
128
+ background-clip: text;
129
+ }
130
+
131
+ .main-header p {
132
+ position: relative;
133
+ color: var(--text-secondary);
134
+ font-size: clamp(0.9rem, 2vw, 1.15rem);
135
+ font-weight: 300;
136
+ margin: 0;
137
+ }
138
+
139
+ .bilingual-badge {
140
+ position: relative;
141
+ display: inline-flex;
142
+ align-items: center;
143
+ gap: 0.4rem;
144
+ background: linear-gradient(135deg, var(--accent-primary), var(--accent-teal));
145
+ color: #060d0a;
146
+ padding: 0.4rem 1.2rem;
147
+ border-radius: 100px;
148
+ margin: 0.8rem 0 0.5rem;
149
+ font-size: 0.85rem;
150
+ font-weight: 600;
151
+ letter-spacing: 0.02em;
152
+ box-shadow: 0 4px 20px rgba(0,232,122,0.35);
153
+ }
154
+
155
+ /* ── RAG Section ─────────────────────────────────── */
156
+ .rag-section {
157
+ background: var(--bg-surface);
158
+ border: 1px solid rgba(0,196,180,0.3);
159
+ border-radius: var(--radius-xl);
160
+ padding: 2rem 2rem 1.5rem;
161
+ margin: 1.5rem 0;
162
+ position: relative;
163
+ overflow: hidden;
164
+ }
165
+
166
+ .rag-section::before {
167
+ content: '';
168
+ position: absolute;
169
+ top: 0; left: 0; right: 0;
170
+ height: 3px;
171
+ background: linear-gradient(90deg, var(--accent-teal), var(--accent-primary), var(--accent-gold));
172
+ }
173
+
174
+ .rag-section::after {
175
+ content: '';
176
+ position: absolute;
177
+ top: -80px; right: -80px;
178
+ width: 260px; height: 260px;
179
+ background: radial-gradient(ellipse, rgba(0,196,180,0.08) 0%, transparent 70%);
180
+ pointer-events: none;
181
+ }
182
+
183
+ .rag-header {
184
+ display: flex;
185
+ align-items: center;
186
+ gap: 0.8rem;
187
+ margin-bottom: 0.4rem;
188
+ }
189
+
190
+ .rag-badge {
191
+ display: inline-flex;
192
+ align-items: center;
193
+ gap: 0.3rem;
194
+ background: rgba(0,196,180,0.15);
195
+ color: var(--accent-teal);
196
+ border: 1px solid rgba(0,196,180,0.35);
197
+ padding: 0.25rem 0.75rem;
198
+ border-radius: 100px;
199
+ font-size: 0.75rem;
200
+ font-weight: 700;
201
+ letter-spacing: 0.08em;
202
+ text-transform: uppercase;
203
+ }
204
+
205
+ .rag-result-box {
206
+ background: var(--bg-elevated);
207
+ border: 1px solid rgba(0,232,122,0.25);
208
+ border-left: 4px solid var(--accent-primary);
209
+ border-radius: var(--radius-md);
210
+ padding: 1.4rem 1.2rem;
211
+ margin: 1rem 0;
212
+ position: relative;
213
+ }
214
+
215
+ .rag-result-label {
216
+ font-family: 'Syne', sans-serif;
217
+ font-size: 0.75rem;
218
+ font-weight: 700;
219
+ letter-spacing: 0.1em;
220
+ text-transform: uppercase;
221
+ color: var(--accent-primary);
222
+ margin-bottom: 0.4rem;
223
+ }
224
+
225
+ .rag-result-field {
226
+ font-size: 0.78rem;
227
+ color: var(--text-secondary);
228
+ margin-bottom: 0.3rem;
229
+ }
230
+
231
+ .rag-result-answer {
232
+ font-size: 1rem;
233
+ color: var(--text-primary);
234
+ line-height: 1.6;
235
+ margin: 0;
236
+ }
237
+
238
+ .rag-result-footer {
239
+ font-size: 0.75rem;
240
+ color: var(--text-muted);
241
+ margin-top: 0.8rem;
242
+ padding-top: 0.6rem;
243
+ border-top: 1px dashed rgba(0,232,122,0.15);
244
+ }
245
+
246
+ .rag-sample-chip {
247
+ background: var(--bg-card);
248
+ border: 1px solid var(--border-subtle);
249
+ border-radius: 100px;
250
+ padding: 0.35rem 0.85rem;
251
+ font-size: 0.82rem;
252
+ color: var(--text-secondary);
253
+ cursor: pointer;
254
+ transition: all 0.2s;
255
+ display: inline-block;
256
+ }
257
+
258
+ .rag-sample-chip:hover {
259
+ border-color: var(--accent-teal);
260
+ color: var(--accent-teal);
261
+ }
262
+
263
+ .rag-kb-pills {
264
+ display: flex;
265
+ flex-wrap: wrap;
266
+ gap: 0.4rem;
267
+ margin: 0.6rem 0 1rem;
268
+ }
269
+
270
+ .rag-kb-pill {
271
+ background: rgba(0,196,180,0.08);
272
+ border: 1px solid rgba(0,196,180,0.2);
273
+ border-radius: 100px;
274
+ padding: 0.2rem 0.7rem;
275
+ font-size: 0.75rem;
276
+ color: var(--accent-teal);
277
+ font-weight: 500;
278
+ }
279
+
280
+ .rag-upload-hint {
281
+ background: rgba(255,200,74,0.07);
282
+ border: 1px solid rgba(255,200,74,0.2);
283
+ border-radius: var(--radius-md);
284
+ padding: 0.8rem 1rem;
285
+ margin-top: 1rem;
286
+ font-size: 0.85rem;
287
+ color: var(--accent-gold);
288
+ display: flex;
289
+ align-items: center;
290
+ gap: 0.5rem;
291
+ }
292
+
293
+ /* ── Feature Cards ──────────────────────────────── */
294
+ .feature-card {
295
+ background: var(--bg-card);
296
+ padding: 1.6rem 1.2rem;
297
+ border-radius: var(--radius-md);
298
+ text-align: center;
299
+ border: 1px solid var(--border-subtle);
300
+ transition: transform 0.25s ease, border-color 0.25s ease, box-shadow 0.25s ease;
301
+ height: 100%;
302
+ position: relative;
303
+ overflow: hidden;
304
+ }
305
+
306
+ .feature-card::before {
307
+ content: '';
308
+ position: absolute;
309
+ bottom: 0; left: 0; right: 0;
310
+ height: 2px;
311
+ background: linear-gradient(90deg, var(--accent-primary), var(--accent-teal));
312
+ transform: scaleX(0);
313
+ transform-origin: left;
314
+ transition: transform 0.3s ease;
315
+ }
316
+
317
+ .feature-card:hover {
318
+ transform: translateY(-6px);
319
+ border-color: var(--border-glow);
320
+ box-shadow: 0 12px 40px rgba(0,232,122,0.15);
321
+ }
322
+
323
+ .feature-card:hover::before { transform: scaleX(1); }
324
+
325
+ .feature-card h3 {
326
+ font-size: 2rem;
327
+ margin-bottom: 0.4rem;
328
+ }
329
+
330
+ .feature-card h3, .feature-card h4, .feature-card p, .feature-card small {
331
+ color: var(--text-primary) !important;
332
+ }
333
+
334
+ .feature-card p {
335
+ font-family: 'Syne', sans-serif;
336
+ font-weight: 600;
337
+ font-size: 0.9rem;
338
+ color: var(--accent-primary) !important;
339
+ margin-bottom: 0.3rem;
340
+ }
341
+
342
+ .feature-card small {
343
+ color: var(--text-secondary) !important;
344
+ font-size: 0.8rem;
345
+ line-height: 1.4;
346
+ }
347
+
348
+ /* ── Prediction Box ─────────────────────────────── */
349
+ .prediction-box {
350
+ background: var(--bg-elevated);
351
+ padding: 2rem 1.5rem;
352
+ border-radius: var(--radius-lg);
353
+ text-align: center;
354
+ margin: 1rem 0;
355
+ border: 1px solid var(--border-glow);
356
+ box-shadow: var(--shadow-green);
357
+ position: relative;
358
+ overflow: hidden;
359
+ }
360
+
361
+ .prediction-box::before {
362
+ content: '';
363
+ position: absolute;
364
+ top: 0; left: 0; right: 0;
365
+ height: 3px;
366
+ background: linear-gradient(90deg, var(--accent-primary), var(--accent-gold), var(--accent-teal));
367
+ }
368
+
369
+ .prediction-box h2 {
370
+ font-family: 'Syne', sans-serif;
371
+ font-weight: 800;
372
+ font-size: clamp(1.6rem, 3vw, 2.2rem);
373
+ background: linear-gradient(135deg, var(--accent-primary), var(--accent-gold));
374
+ -webkit-background-clip: text;
375
+ -webkit-text-fill-color: transparent;
376
+ background-clip: text;
377
+ margin: 0 0 0.4rem;
378
+ }
379
+
380
+ .prediction-box p {
381
+ color: var(--text-secondary) !important;
382
+ font-size: 1rem;
383
+ }
384
+
385
+ /* ── Chat Messages ──────────────────────────────── */
386
+ .user-message {
387
+ background: linear-gradient(135deg, #0a2518, #0f2e1f);
388
+ color: var(--text-primary);
389
+ padding: 0.9rem 1.1rem;
390
+ border-radius: var(--radius-md) var(--radius-sm) var(--radius-sm) var(--radius-md);
391
+ margin: 0.6rem 0;
392
+ text-align: right;
393
+ border: 1px solid rgba(0,232,122,0.2);
394
+ font-size: 0.95rem;
395
+ line-height: 1.5;
396
+ }
397
+
398
+ .assistant-message {
399
+ background: var(--bg-card);
400
+ color: var(--text-primary);
401
+ padding: 0.9rem 1.1rem;
402
+ border-radius: var(--radius-sm) var(--radius-md) var(--radius-md) var(--radius-sm);
403
+ margin: 0.6rem 0;
404
+ text-align: left;
405
+ border: 1px solid var(--border-subtle);
406
+ border-left: 3px solid var(--accent-primary);
407
+ font-size: 0.95rem;
408
+ line-height: 1.6;
409
+ }
410
+
411
+ .urdu-text {
412
+ font-family: 'Noto Nastaliq Urdu', 'Urdu Typesetting', 'Jameel Noori Nastaleeq', serif;
413
+ font-size: 1.05rem;
414
+ direction: rtl;
415
+ text-align: right;
416
+ color: var(--accent-gold);
417
+ margin-top: 0.6rem;
418
+ padding-top: 0.6rem;
419
+ border-top: 1px dashed rgba(255,200,74,0.3);
420
+ }
421
+
422
+ /* ── Info Boxes ─────────────────────────────────── */
423
+ .info-box {
424
+ background: var(--bg-card);
425
+ padding: 1.2rem 1.3rem;
426
+ border-radius: var(--radius-md);
427
+ margin: 0.6rem 0;
428
+ border: 1px solid var(--border-subtle);
429
+ border-left: 3px solid var(--accent-primary);
430
+ transition: border-color 0.2s;
431
+ }
432
+
433
+ .info-box:hover {
434
+ border-left-color: var(--accent-gold);
435
+ border-color: rgba(255,200,74,0.2);
436
+ }
437
+
438
+ .info-box h4 {
439
+ font-family: 'Syne', sans-serif;
440
+ font-size: 0.85rem;
441
+ font-weight: 700;
442
+ letter-spacing: 0.06em;
443
+ text-transform: uppercase;
444
+ color: var(--accent-primary) !important;
445
+ margin-bottom: 0.4rem;
446
+ }
447
+
448
+ .info-box p {
449
+ color: var(--text-primary) !important;
450
+ font-size: 0.92rem;
451
+ line-height: 1.5;
452
+ margin: 0;
453
+ }
454
+
455
+ /* ── Stat Cards ─────────────────────────────────── */
456
+ .stat-card {
457
+ background: var(--bg-card);
458
+ color: var(--text-primary);
459
+ padding: 1.4rem 1rem;
460
+ border-radius: var(--radius-md);
461
+ text-align: center;
462
+ border: 1px solid var(--border-subtle);
463
+ transition: transform 0.2s, box-shadow 0.2s;
464
+ position: relative;
465
+ overflow: hidden;
466
+ }
467
+
468
+ .stat-card::after {
469
+ content: '';
470
+ position: absolute;
471
+ bottom: 0; left: 0; right: 0;
472
+ height: 2px;
473
+ background: linear-gradient(90deg, var(--accent-primary), var(--accent-teal));
474
+ }
475
+
476
+ .stat-card:hover {
477
+ transform: translateY(-3px);
478
+ box-shadow: 0 8px 24px rgba(0,232,122,0.12);
479
+ }
480
+
481
+ .stat-card h3 {
482
+ font-family: 'Syne', sans-serif;
483
+ font-weight: 800;
484
+ font-size: clamp(1.6rem, 3vw, 2rem);
485
+ background: linear-gradient(135deg, var(--accent-primary), var(--accent-gold));
486
+ -webkit-background-clip: text;
487
+ -webkit-text-fill-color: transparent;
488
+ background-clip: text;
489
+ margin-bottom: 0.3rem;
490
+ }
491
+
492
+ .stat-card p {
493
+ color: var(--text-secondary) !important;
494
+ font-size: 0.82rem;
495
+ text-transform: uppercase;
496
+ letter-spacing: 0.08em;
497
+ font-weight: 500;
498
+ margin: 0;
499
+ }
500
+
501
+ /* ── Voice Section ──────────────────────────────── */
502
+ .voice-section {
503
+ background: var(--bg-elevated);
504
+ padding: 1.2rem;
505
+ border-radius: var(--radius-lg);
506
+ margin: 1rem 0;
507
+ border: 1px solid rgba(0,196,180,0.25);
508
+ box-shadow: 0 0 30px rgba(0,196,180,0.06);
509
+ }
510
+
511
+ /* ── Footer ─────────────────────────────────────── */
512
+ .footer {
513
+ text-align: center;
514
+ padding: 2.5rem 2rem;
515
+ background: var(--bg-surface);
516
+ border-radius: var(--radius-xl);
517
+ margin-top: 3rem;
518
+ border: 1px solid var(--border-subtle);
519
+ position: relative;
520
+ overflow: hidden;
521
+ }
522
+
523
+ .footer::before {
524
+ content: '';
525
+ position: absolute;
526
+ top: 0; left: 20%; right: 20%;
527
+ height: 1px;
528
+ background: linear-gradient(90deg, transparent, var(--accent-primary), transparent);
529
+ }
530
+
531
+ .footer p {
532
+ color: var(--text-secondary) !important;
533
+ margin: 0.3rem 0;
534
+ font-size: 0.9rem;
535
+ }
536
+
537
+ /* ── Buttons ─────────────────────────────────────── */
538
+ .stButton > button {
539
+ background: linear-gradient(135deg, var(--accent-primary) 0%, #00b85e 100%);
540
+ color: #060d0a !important;
541
+ border: none !important;
542
+ padding: 0.55rem 1.8rem !important;
543
+ border-radius: 100px !important;
544
+ font-weight: 700 !important;
545
+ font-family: 'DM Sans', sans-serif !important;
546
+ font-size: 0.9rem !important;
547
+ letter-spacing: 0.01em !important;
548
+ width: 100% !important;
549
+ transition: transform 0.2s, box-shadow 0.2s !important;
550
+ box-shadow: 0 4px 16px rgba(0,232,122,0.25) !important;
551
+ }
552
+
553
+ .stButton > button:hover {
554
+ transform: translateY(-2px) !important;
555
+ box-shadow: 0 8px 28px rgba(0,232,122,0.4) !important;
556
+ background: linear-gradient(135deg, #1fffa0 0%, #00d870 100%) !important;
557
+ }
558
+
559
+ .stButton > button:active {
560
+ transform: translateY(0) !important;
561
+ }
562
+
563
+ /* ── Text Input ──────────────────────────────────── */
564
+ .stTextInput > div > div > input {
565
+ background-color: var(--bg-card) !important;
566
+ color: var(--text-primary) !important;
567
+ border: 1px solid var(--border-glow) !important;
568
+ border-radius: var(--radius-md) !important;
569
+ font-family: 'DM Sans', sans-serif !important;
570
+ padding: 0.6rem 1rem !important;
571
+ font-size: 0.95rem !important;
572
+ transition: border-color 0.2s, box-shadow 0.2s !important;
573
+ }
574
+
575
+ .stTextInput > div > div > input:focus {
576
+ border-color: var(--accent-primary) !important;
577
+ box-shadow: 0 0 0 3px rgba(0,232,122,0.12) !important;
578
+ }
579
+
580
+ .stTextInput > div > div > input::placeholder {
581
+ color: var(--text-muted) !important;
582
+ }
583
+
584
+ /* ── Streamlit Overrides ─────────────────────────── */
585
+ .stMarkdown p, .stMarkdown li, .stMarkdown span {
586
+ color: var(--text-primary) !important;
587
+ }
588
+
589
+ h1, h2, h3, h4, h5, h6 {
590
+ font-family: 'Syne', sans-serif !important;
591
+ color: var(--text-primary) !important;
592
+ }
593
+
594
+ .stMarkdown h3 {
595
+ font-weight: 700 !important;
596
+ letter-spacing: -0.01em !important;
597
+ color: var(--text-primary) !important;
598
+ margin-top: 1.5rem !important;
599
+ }
600
+
601
+ .stRadio label, .stRadio div {
602
+ color: var(--text-primary) !important;
603
+ }
604
+
605
+ .stRadio [data-testid="stMarkdownContainer"] p {
606
+ color: var(--text-secondary) !important;
607
+ font-size: 0.85rem !important;
608
+ }
609
+
610
+ .stAlert {
611
+ background: var(--bg-elevated) !important;
612
+ border-radius: var(--radius-md) !important;
613
+ border: 1px solid var(--border-subtle) !important;
614
+ }
615
+
616
+ [data-testid="stFileUploader"] {
617
+ background: var(--bg-card) !important;
618
+ border: 1px dashed var(--border-glow) !important;
619
+ border-radius: var(--radius-md) !important;
620
+ transition: border-color 0.2s !important;
621
+ }
622
+
623
+ [data-testid="stFileUploader"]:hover {
624
+ border-color: var(--accent-primary) !important;
625
+ }
626
+
627
+ [data-testid="stAudioInput"] {
628
+ background: var(--bg-elevated) !important;
629
+ border-radius: var(--radius-md) !important;
630
+ border: 1px solid rgba(0,196,180,0.2) !important;
631
+ padding: 0.5rem !important;
632
+ }
633
+
634
+ .stSpinner > div { border-top-color: var(--accent-primary) !important; }
635
+
636
+ .js-plotly-plot .plotly {
637
+ border-radius: var(--radius-md) !important;
638
+ }
639
+
640
+ hr {
641
+ border: none !important;
642
+ border-top: 1px solid var(--border-subtle) !important;
643
+ margin: 1.5rem 0 !important;
644
+ }
645
+
646
+ @media (max-width: 1024px) {
647
+ .main-header { padding: 2rem 1.5rem; }
648
+ .feature-card { padding: 1.2rem 1rem; }
649
+ .stat-card { padding: 1rem 0.8rem; }
650
+ .stat-card h3 { font-size: 1.5rem; }
651
+ .rag-section { padding: 1.5rem 1.2rem; }
652
+ }
653
+
654
+ @media (max-width: 768px) {
655
+ .main-header { padding: 1.6rem 1rem; border-radius: var(--radius-lg); }
656
+ .main-header h1 { font-size: 2rem; }
657
+ .main-header p { font-size: 0.9rem; }
658
+ .bilingual-badge { font-size: 0.78rem; padding: 0.3rem 0.9rem; }
659
+ .feature-card { padding: 1rem 0.8rem; border-radius: var(--radius-sm); }
660
+ .feature-card h3 { font-size: 1.6rem; }
661
+ .prediction-box { padding: 1.4rem 1rem; }
662
+ .prediction-box h2 { font-size: 1.5rem; }
663
+ .info-box { padding: 1rem; }
664
+ .info-box h4 { font-size: 0.8rem; }
665
+ .stat-card h3 { font-size: 1.4rem; }
666
+ .stat-card p { font-size: 0.75rem; }
667
+ .user-message, .assistant-message { font-size: 0.88rem; padding: 0.75rem 0.9rem; }
668
+ .urdu-text { font-size: 0.95rem; }
669
+ .footer { padding: 1.5rem 1rem; border-radius: var(--radius-lg); }
670
+ .footer p { font-size: 0.82rem; }
671
+ .stButton > button { font-size: 0.85rem !important; padding: 0.5rem 1rem !important; }
672
+ .rag-section { padding: 1.2rem 1rem; border-radius: var(--radius-lg); }
673
+ }
674
+
675
+ @media (max-width: 480px) {
676
+ .main-header h1 { font-size: 1.6rem; }
677
+ .prediction-box h2 { font-size: 1.2rem; }
678
+ .feature-card p { font-size: 0.8rem; }
679
+ .rag-result-answer { font-size: 0.9rem; }
680
+ }
681
+ </style>
682
+ """, unsafe_allow_html=True)
683
+
684
+
685
+ # ============================================================
686
+ # LOAD ANIMAL DATABASE FROM JSON
687
+ # ============================================================
688
+ @st.cache_data
689
+ def load_animal_database():
690
+ """
691
+ Load animal database from animals_data.json
692
+
693
+ Required JSON structure (array of objects):
694
+ [
695
+ {
696
+ "name": "Lion", ← required, used for matching
697
+ "scientific_name": "Panthera leo", ← required
698
+ "habitat": "Savannas of Africa", ← required
699
+ "habitat_ur": "افریقہ کے سوانا", ← optional Urdu field
700
+ "diet": "Carnivore - large mammals", ← required
701
+ "diet_ur": "گوشت خور", ← optional Urdu field
702
+ "conservation": "Vulnerable", ← required
703
+ "conservation_ur": "خطرے سے دوچار", ← optional Urdu field
704
+ "lifespan": "10-14 years", ← required
705
+ "lifespan_ur": "10-14 سال", ← optional Urdu field
706
+ "fun_fact": "Lions live in prides", ← required
707
+ "fun_fact_ur": "شیر گروپوں میں رہتے ہیں", ← optional Urdu field
708
+ "speed": "80 km/h", ← optional
709
+ "speed_ur": "80 کلومیٹر", ← optional Urdu
710
+ "weight": "120-250 kg", ← optional
711
+ "weight_ur": "120-250 کلوگرام", ← optional Urdu
712
+ "region": "Sub-Saharan Africa", ← optional
713
+ "region_ur": "جنوبی افریقہ", ← optional Urdu
714
+ "tags": ["big cat","predator","africa"] ← required, used for RAG search
715
+ },
716
+ ...
717
+ ]
718
+ """
719
+ try:
720
+ with open("animals_data.json", "r", encoding="utf-8") as f:
721
+ animals = json.load(f)
722
+ return animals
723
+ except Exception as e:
724
+ st.warning(f"Could not load animals_data.json: {e}")
725
+ return []
726
+
727
+
728
+ def find_animal_in_json(animal_name, animals):
729
+ """Find animal in JSON database by name matching"""
730
+ if not animals:
731
+ return None
732
+
733
+ animal_name_lower = animal_name.strip().lower()
734
+
735
+ for animal in animals:
736
+ if animal["name"].lower() == animal_name_lower:
737
+ return animal
738
+
739
+ best_match = None
740
+ best_score = 0
741
+
742
+ for animal in animals:
743
+ name = animal["name"].lower()
744
+ if animal_name_lower in name or name in animal_name_lower:
745
+ score = len(set(animal_name_lower.split()) & set(name.split()))
746
+ if score > best_score:
747
+ best_score = score
748
+ best_match = animal
749
+
750
+ if "tags" in animal:
751
+ for tag in animal["tags"]:
752
+ if tag.lower() in animal_name_lower:
753
+ if best_match is None:
754
+ best_match = animal
755
+ break
756
+
757
+ return best_match
758
+
759
+
760
+ # ============================================================
761
+ # RAG RETRIEVAL FUNCTION
762
+ # ============================================================
763
+ def rag_retrieve(query, animals_json):
764
+ """
765
+ Retrieve the best matching animal and relevant field from JSON knowledge base.
766
+ Returns (matched_animal, field_label, answer_en, answer_ur) or None.
767
+
768
+ This is the core RAG 'Retrieval' step:
769
+ - animals_data.json acts as the Vector Store / Knowledge Base
770
+ - Keyword matching acts as the retriever
771
+ - Groq LLM then 'Augments' and 'Generates' the final answer
772
+ """
773
+ if not animals_json:
774
+ return None, None, None, None
775
+
776
+ query_lower = query.lower()
777
+ matched_animal = None
778
+
779
+ # Step 1 — find animal by name or tag in query
780
+ for animal in animals_json:
781
+ name_match = animal["name"].lower() in query_lower
782
+ tag_match = any(tag.lower() in query_lower for tag in animal.get("tags", []))
783
+ if name_match or tag_match:
784
+ matched_animal = animal
785
+ break
786
+
787
+ if not matched_animal:
788
+ return None, None, None, None
789
+
790
+ # Step 2 — determine which field the user is asking about
791
+ diet_kws = ["eat", "food", "diet", "feed", "prey", "کھاتا", "خوراک", "غذا"]
792
+ habitat_kws = ["live", "habitat", "where", "home", "found", "رہتا", "رہائش", "کہاں", "مسکن"]
793
+ lifespan_kws= ["long", "lifespan", "age", "old", "live for", "زندہ", "عمر", "کتنا جیتا", "زندگی"]
794
+ conserv_kws = ["endanger", "conservation", "extinct", "threat", "status",
795
+ "معدوم", "تحفظ", "خطرہ", "محفوظ"]
796
+ speed_kws = ["fast", "speed", "run", "رفتار", "تیز"]
797
+ weight_kws = ["weight", "heavy", "weigh", "وزن", "بھاری"]
798
+
799
+ if any(k in query_lower for k in diet_kws):
800
+ field_label = "Diet / خوراک"
801
+ answer_en = matched_animal.get("diet", "N/A")
802
+ answer_ur = matched_animal.get("diet_ur", answer_en)
803
+ elif any(k in query_lower for k in habitat_kws):
804
+ field_label = "Habitat / رہائش گاہ"
805
+ answer_en = matched_animal.get("habitat", "N/A")
806
+ answer_ur = matched_animal.get("habitat_ur", answer_en)
807
+ elif any(k in query_lower for k in lifespan_kws):
808
+ field_label = "Lifespan / زندگی کی مدت"
809
+ answer_en = matched_animal.get("lifespan", "N/A")
810
+ answer_ur = matched_animal.get("lifespan_ur", answer_en)
811
+ elif any(k in query_lower for k in conserv_kws):
812
+ field_label = "Conservation Status / تحفظ کی حیثیت"
813
+ answer_en = matched_animal.get("conservation", "N/A")
814
+ answer_ur = matched_animal.get("conservation_ur", answer_en)
815
+ elif any(k in query_lower for k in speed_kws):
816
+ field_label = "Speed / رفتار"
817
+ answer_en = matched_animal.get("speed", "N/A")
818
+ answer_ur = matched_animal.get("speed_ur", answer_en)
819
+ elif any(k in query_lower for k in weight_kws):
820
+ field_label = "Weight / وزن"
821
+ answer_en = matched_animal.get("weight", "N/A")
822
+ answer_ur = matched_animal.get("weight_ur", answer_en)
823
  else:
824
+ field_label = "Fun Fact / دلچسپ حقیقت"
825
+ answer_en = matched_animal.get("fun_fact", "N/A")
826
+ answer_ur = matched_animal.get("fun_fact_ur", answer_en)
827
 
828
+ return matched_animal, field_label, answer_en, answer_ur
 
 
 
 
 
 
829
 
 
830
 
831
+ # ============================================================
832
+ # INITIALIZE TRANSLATION MODEL
833
+ # ============================================================
834
+ @st.cache_resource
835
+ def load_translation_model():
836
+ try:
837
+ model_name = "Helsinki-NLP/opus-mt-en-ur"
838
+ tokenizer = AutoTokenizer.from_pretrained(model_name)
839
+ model = AutoModelForSeq2SeqLM.from_pretrained(model_name)
840
+ return tokenizer, model
841
+ except Exception as e:
842
+ return None, None
843
+
844
+
845
+ def translate_to_urdu(text, tokenizer, model):
846
+ if tokenizer is None or model is None:
847
+ return None
848
+ try:
849
+ inputs = tokenizer(text, return_tensors="pt", truncation=True, max_length=512)
850
+ with torch.no_grad():
851
+ outputs = model.generate(**inputs, max_length=200)
852
+ return tokenizer.decode(outputs[0], skip_special_tokens=True)
853
+ except:
854
+ return None
855
+
856
+
857
+ # ============================================================
858
+ # INITIALIZE GROQ CLIENT
859
+ # ============================================================
860
+ @st.cache_resource
861
+ def init_groq_client():
862
+ api_key = os.environ.get("GROQ_API_KEY")
863
+ if api_key is None:
864
+ try:
865
+ api_key = st.secrets["GROQ_API_KEY"]
866
+ except:
867
+ api_key = None
868
+ if api_key:
869
+ return Groq(api_key=api_key)
870
+ return None
871
+
872
+
873
+ # ============================================================
874
+ # LOAD MODEL
875
+ # ============================================================
876
+ @st.cache_resource
877
+ def load_model_and_labels():
878
+ try:
879
+ model = tf.keras.models.load_model("animal_mobilenet_model.keras")
880
+ with open("labels.pkl", "rb") as f:
881
+ labels = pickle.load(f)
882
+ class_names = list(labels.keys())
883
+ return model, labels, class_names
884
+ except Exception as e:
885
+ return None, None, None
886
+
887
+
888
+ # ============================================================
889
+ # FALLBACK ANIMAL INFORMATION DATABASE
890
+ # ============================================================
891
+ animal_info_fallback = {
892
+ "Lion": {
893
+ "habitat": "Savannas and grasslands of Africa",
894
+ "habitat_ur": "افریقہ کے سوانا اور گھاس کے میدان",
895
+ "diet": "Carnivore - primarily large mammals",
896
+ "diet_ur": "گوشت خور - بنیادی طور پر بڑے جانور",
897
+ "conservation": "Vulnerable - population decreasing",
898
+ "conservation_ur": "خطرے سے دوچار - آبادی کم ہو رہی ہے",
899
+ "lifespan": "10-14 years in wild",
900
+ "lifespan_ur": "جنگل میں 10-14 سال",
901
+ "fun_fact": "Lions live in social groups called prides",
902
+ "fun_fact_ur": "شیر سماجی گروپوں میں رہتے ہیں",
903
+ "scientific_name": "Panthera leo"
904
+ },
905
+ "Tiger": {
906
+ "habitat": "Rainforests and grasslands of Asia",
907
+ "habitat_ur": "ایشیا کے برساتی جنگلات",
908
+ "diet": "Carnivore - deer, wild boar",
909
+ "diet_ur": "گوشت خور - ہرن، جنگلی سور",
910
+ "conservation": "Endangered - only 3,900 remain",
911
+ "conservation_ur": "خطرے سے دوچار - صرف 3,900 باقی",
912
+ "lifespan": "8-10 years in wild",
913
+ "lifespan_ur": "جنگل میں 8-10 سال",
914
+ "fun_fact": "Each tiger has unique stripe patterns",
915
+ "fun_fact_ur": "ہر شیر کے پٹیوں کے منفرد نمونے",
916
+ "scientific_name": "Panthera tigris"
917
+ },
918
+ "Elephant": {
919
+ "habitat": "Savannas and forests of Africa and Asia",
920
+ "habitat_ur": "افریقہ اور ایشیا کے جنگلات",
921
+ "diet": "Herbivore - grasses, fruits, bark",
922
+ "diet_ur": "سبزی خور - گھاس، پھل، چھال",
923
+ "conservation": "Endangered",
924
+ "conservation_ur": "خطرے سے دوچار",
925
+ "lifespan": "60-70 years",
926
+ "lifespan_ur": "60-70 سال",
927
+ "fun_fact": "Elephants can recognize themselves in mirrors",
928
+ "fun_fact_ur": "ہاتھی آئینے میں خود کو پہچان سکتے ہیں",
929
+ "scientific_name": "Loxodonta africana"
930
+ },
931
+ "Giraffe": {
932
+ "habitat": "Savannas of Africa",
933
+ "habitat_ur": "افریقہ کے سوانا",
934
+ "diet": "Herbivore - acacia leaves",
935
+ "diet_ur": "سبزی خور - ببول کے پتے",
936
+ "conservation": "Vulnerable",
937
+ "conservation_ur": "خطرے سے دوچار",
938
+ "lifespan": "20-25 years",
939
+ "lifespan_ur": "20-25 سال",
940
+ "fun_fact": "Giraffes have 7 neck vertebrae like humans",
941
+ "fun_fact_ur": "زرافوں کی گردن میں 7 vertebrae",
942
+ "scientific_name": "Giraffa camelopardalis"
943
+ }
944
+ }
945
+
946
+
947
+ def get_fallback_animal_info(animal_name, language="english"):
948
+ for key in animal_info_fallback:
949
+ if key.lower() in animal_name.lower():
950
+ info = animal_info_fallback[key]
951
+ if language == "urdu":
952
+ return {
953
+ "habitat": info.get("habitat_ur", info["habitat"]),
954
+ "diet": info.get("diet_ur", info["diet"]),
955
+ "conservation": info.get("conservation_ur", info["conservation"]),
956
+ "lifespan": info.get("lifespan_ur", info["lifespan"]),
957
+ "fun_fact": info.get("fun_fact_ur", info["fun_fact"]),
958
+ "scientific_name": info["scientific_name"]
959
+ }
960
+ return info
961
+ return {
962
+ "habitat": "Information not available",
963
+ "diet": "Information not available",
964
+ "conservation": "Information not available",
965
+ "lifespan": "Information not available",
966
+ "fun_fact": "This amazing animal needs our protection",
967
+ "scientific_name": "Information not available"
968
+ }
969
+
970
+
971
+ # ============================================================
972
+ # LLM CHAT FUNCTION
973
+ # ============================================================
974
+ def get_llm_response(client, animal_name, user_question, is_urdu=False,
975
+ tokenizer=None, model=None, retrieved_context=None):
976
+ """
977
+ RAG-enhanced LLM response.
978
+ retrieved_context: if provided, injected into the system prompt so the
979
+ LLM augments a retrieved fact rather than hallucinating from scratch.
980
+ """
981
+ if client is None:
982
+ return "⚠️ Groq API key not configured. Please add your API key to continue.", None
983
+
984
+ context_block = ""
985
+ if retrieved_context:
986
+ context_block = f"\n\nRetrieved fact from knowledge base: {retrieved_context}\nUse this fact as your primary source. Expand on it naturally."
987
+
988
+ if is_urdu:
989
+ system_prompt = f"""آپ SmartZoo AI اسسٹنٹ ہیں، ایک سنجیدہ ماہرِ حیاتِ وحش۔
990
+ آپ {animal_name} کے بارے میں بات کر رہے ہیں۔{context_block}
991
+ صرف درست، سائنسی، اور تعلیمی معلومات دیں۔
992
+ مذاق، طنز، یا غیر ضروری تبصرہ بالکل نہ کریں۔
993
+ جواب مختصر رکھیں — زیادہ سے زیادہ 2 جملے۔
994
+ صرف ان موضوعات پر بات کریں: رہائش گاہ، خوراک، رویہ، یا تحفظ کی حیثیت۔
995
+ جواب صرف اردو میں دیں۔"""
996
+ try:
997
+ chat_completion = client.chat.completions.create(
998
+ messages=[
999
+ {"role": "system", "content": system_prompt},
1000
+ {"role": "user", "content": user_question}
1001
+ ],
1002
+ model="llama-3.3-70b-versatile",
1003
+ temperature=0.1,
1004
+ max_tokens=300,
1005
+ )
1006
+ return chat_completion.choices[0].message.content.strip(), None
1007
+ except Exception as e:
1008
+ return f"⚠️ خرابی: {str(e)}", None
1009
+ else:
1010
+ system_prompt = f"""You are SmartZoo AI Assistant, a serious wildlife expert.
1011
+ You are discussing {animal_name}. Provide ONLY factual, accurate, educational information.{context_block}
1012
+ DO NOT include jokes, sarcasm, or humorous content.
1013
+ Keep responses concise (1-2 sentences max).
1014
+ Focus ONLY on: habitat, diet, behavior, or conservation status."""
1015
+ try:
1016
+ chat_completion = client.chat.completions.create(
1017
+ messages=[
1018
+ {"role": "system", "content": system_prompt},
1019
+ {"role": "user", "content": user_question}
1020
+ ],
1021
+ model="llama-3.3-70b-versatile",
1022
+ temperature=0.1,
1023
+ max_tokens=200,
1024
  )
1025
+ return chat_completion.choices[0].message.content.strip(), None
1026
+ except Exception as e:
1027
+ return f"⚠️ Error: {str(e)}", None
1028
+
1029
+
1030
+ # ============================================================
1031
+ # SPEECH-TO-TEXT
1032
+ # ============================================================
1033
+ def speech_to_text_groq(client, audio_bytes):
1034
+ try:
1035
+ if hasattr(audio_bytes, 'read'):
1036
+ audio_data = audio_bytes.read()
1037
  else:
1038
+ audio_data = audio_bytes
1039
+
1040
+ with tempfile.NamedTemporaryFile(delete=False, suffix=".wav") as tmp_file:
1041
+ tmp_file.write(audio_data)
1042
+ tmp_file_path = tmp_file.name
1043
+
1044
+ with open(tmp_file_path, "rb") as file:
1045
+ transcription = client.audio.transcriptions.create(
1046
+ file=(tmp_file_path, file.read()),
1047
+ model="whisper-large-v3",
1048
+ response_format="json",
1049
+ language="en",
1050
  )
1051
 
1052
+ os.unlink(tmp_file_path)
1053
+ return transcription.text
1054
+ except Exception as e:
1055
+ return None
1056
+
1057
+
1058
+ # ============================================================
1059
+ # PRE-PROCESSING FUNCTIONS
1060
+ # ============================================================
1061
+ def preprocess_image(uploaded_file):
1062
+ img = Image.open(uploaded_file)
1063
+ img = img.resize((224, 224))
1064
+ img_array = np.array(img) / 255.0
1065
+ img_array = np.expand_dims(img_array, axis=0)
1066
+ return img, img_array
1067
+
1068
+
1069
+ def create_confidence_chart(probabilities, class_names, top_n=5):
1070
+ top_indices = np.argsort(probabilities)[-top_n:][::-1]
1071
+ top_names = [class_names[i] for i in top_indices]
1072
+ top_probs = [probabilities[i] * 100 for i in top_indices]
1073
+
1074
+ fig = go.Figure(data=[
1075
+ go.Bar(
1076
+ x=top_probs,
1077
+ y=top_names,
1078
+ orientation='h',
1079
+ marker=dict(
1080
+ color=top_probs,
1081
+ colorscale=[[0, '#0f2e1f'], [0.5, '#00b85e'], [1, '#00e87a']],
1082
+ showscale=True,
1083
+ colorbar=dict(tickfont=dict(color='#7eb894'))
1084
+ ),
1085
+ text=[f"{p:.1f}%" for p in top_probs],
1086
+ textposition='outside',
1087
+ textfont=dict(color='#e8f5ee')
1088
+ )
1089
+ ])
1090
+
1091
+ fig.update_layout(
1092
+ title=dict(text="Top Predictions Confidence Score",
1093
+ font=dict(color='#e8f5ee', family='Syne', size=14)),
1094
+ xaxis_title=dict(text="Confidence (%)", font=dict(color='#7eb894')),
1095
+ yaxis_title=dict(text="Animal Species", font=dict(color='#7eb894')),
1096
+ height=400,
1097
+ paper_bgcolor='rgba(0,0,0,0)',
1098
+ plot_bgcolor='rgba(0,0,0,0)',
1099
+ font=dict(color='#e8f5ee', family='DM Sans'),
1100
+ xaxis=dict(gridcolor='rgba(0,232,122,0.07)', color='#7eb894'),
1101
+ yaxis=dict(gridcolor='rgba(0,232,122,0.07)', color='#7eb894'),
1102
+ margin=dict(l=10, r=10, t=40, b=10)
1103
+ )
1104
+ return fig, top_names, top_probs
1105
+
1106
+
1107
+ def get_suggested_questions(animal_name, language="english"):
1108
+ if language == "urdu":
1109
+ return [
1110
+ f"{animal_name} کیا کھاتا ہے؟",
1111
+ f"{animal_name} کہاں رہتا ہے؟",
1112
+ f"{animal_name} کے بارے میں ایک دلچسپ حقیقت بتائیں",
1113
+ f"کیا {animal_name} معدوم ہونے کے خطرے میں ہے؟",
1114
+ f"{animal_name} کتنی دیر زندہ رہتا ہے؟"
1115
+ ]
1116
+ return [
1117
+ f"What does {animal_name} eat?",
1118
+ f"Where does {animal_name} live?",
1119
+ f"Tell me an interesting fact about {animal_name}",
1120
+ f"Is {animal_name} endangered?",
1121
+ f"How long does {animal_name} live?"
1122
+ ]
1123
+
1124
+
1125
+ # ============================================================
1126
+ # MAIN APP
1127
+ # ============================================================
1128
+ def main():
1129
+ # ── Header ──────────────────────────────────────────────
1130
+ st.markdown("""
1131
+ <div class="main-header">
1132
+ <h1>🦁 <span class="heading-text">SmartZoo AI</span></h1>
1133
+ <p>Bilingual Animal Classification with AI-Powered Chat Assistant</p>
1134
+ <div class="bilingual-badge">🎤 Voice Input | 🇵🇰 English | اردو 🇵🇰</div>
1135
+ <p style="font-size: 0.9rem;">Powered by MobileNetV2 + Groq LLM + Helsinki-NLP Translation</p>
1136
+ </div>
1137
+ """, unsafe_allow_html=True)
1138
+
1139
+ # ── Sidebar ──────────────────────────────────────────────
1140
+ with st.sidebar:
1141
+ st.markdown("### 🌐 Language / زبان")
1142
+ language = st.radio(
1143
+ "Select Language / زبان منتخب کریں",
1144
+ ["English", "اردو (Urdu)"],
1145
+ index=0
1146
  )
1147
 
1148
+ st.markdown("---")
1149
+ st.markdown("### 🏆 SmartZoo AI Features")
1150
+
1151
+ if language == "English":
1152
+ st.info("""
1153
+ ✅ Real-time Animal Recognition
1154
+ ✅ AI Chat Assistant (Groq LLM)
1155
+ ✅ Voice Input (Whisper)
1156
+ ✅ Bilingual Support (English/Urdu)
1157
+ ✅ RAG Knowledge Base System
1158
+ ✅ Conservation Information
1159
+ ✅ Interactive Confidence Charts
1160
+ """)
1161
+ else:
1162
+ st.info("""
1163
+ ✅ حقیقی وقت میں جانوروں کی شناخت
1164
+ ✅ AI چیٹ اسسٹنٹ (Groq LLM)
1165
+ ✅ آواز سے سوال (Whisper)
1166
+ ✅ دو لسانی سپورٹ (انگریزی/اردو)
1167
+ ✅ RAG علمی ذخیرہ سسٹم
1168
+ ✅ تحفظ کی معلومات
1169
+ ✅ انٹرایکٹو اعتماد کے چارٹ
1170
+ """)
1171
+
1172
+ is_urdu = (language == "اردو (Urdu)")
1173
+
1174
+ # ── Load resources ───────────────────────────────────────
1175
+ model, labels, class_names = load_model_and_labels()
1176
+ groq_client = init_groq_client()
1177
+ tokenizer, translation_model = load_translation_model()
1178
+ animals_json = load_animal_database()
1179
+
1180
+ if model is None:
1181
+ st.error("⚠️ Model not loaded. Please check your files.")
1182
+ return
1183
+
1184
+ # ── Features Section ─────────────────────────────────────
1185
+ if is_urdu:
1186
+ st.markdown("### 🌟 خصوصیات")
1187
+ else:
1188
+ st.markdown("### 🌟 Features")
1189
+
1190
+ col1, col2, col3, col4 = st.columns(4)
1191
+
1192
  if is_urdu:
1193
+ features = [
1194
+ ("📸", "فوری شناخت", "جانوروں کی فوری شناخت"),
1195
+ ("🤖", "AI چیٹ اسسٹنٹ", "جانوروں کے بارے میں سوالات کریں"),
1196
+ ("🎤", "آواز سے سوال", ائیکروفون سے سوال کریں"),
1197
+ ("📚", "RAG سسٹم", "علمی ذخیرے سے جواب")
 
1198
  ]
1199
  else:
1200
+ features = [
1201
+ ("📸", "Real-time Recognition", "Instant animal identification"),
1202
+ ("🤖", "AI Chat Assistant", "Ask questions about animals"),
1203
+ ("🎤", "Voice Input", "Ask by speaking"),
1204
+ ("📚", "RAG System", "Knowledge base retrieval")
 
1205
  ]
1206
 
1207
+ for col, (emoji, title, desc) in zip([col1, col2, col3, col4], features):
1208
+ with col:
1209
+ st.markdown(f"""
1210
+ <div class="feature-card">
1211
+ <h3>{emoji}</h3>
1212
+ <p>{title}</p>
1213
+ <small>{desc}</small>
1214
+ </div>
1215
+ """, unsafe_allow_html=True)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1216
 
1217
+ # ============================================================
1218
+ # RAG KNOWLEDGE BASE SECTION
1219
+ # Shown ONLY when no image is uploaded auto-hides on upload
1220
+ # Connects to: animals_data.json (see load_animal_database docstring)
1221
+ # ============================================================
1222
+
1223
+ # We need to know if the file uploader has a file BEFORE rendering RAG.
1224
+ # Streamlit evaluates widgets top-to-bottom, so we use session_state to
1225
+ # track whether an image has been uploaded in any previous run.
1226
+ if "has_uploaded_file" not in st.session_state:
1227
+ st.session_state.has_uploaded_file = False
1228
+
1229
+ # Render RAG section only when no image is uploaded
1230
+ if not st.session_state.has_uploaded_file:
1231
+
1232
+ st.markdown("---")
1233
+
1234
+ # Build knowledge base pills from JSON
1235
+ kb_animals = [a["name"] for a in animals_json[:16]] if animals_json else [
1236
+ "Lion", "Tiger", "Elephant", "Giraffe"
1237
+ ]
1238
+ kb_pills_html = "".join(
1239
+ f'<span class="rag-kb-pill">🐾 {name}</span>' for name in kb_animals
1240
+ )
1241
+
1242
+ if is_urdu:
1243
+ st.markdown("""
1244
+ <div class="rag-section">
1245
+ <div class="rag-header">
1246
+ <span style="font-family:'Syne',sans-serif;font-weight:800;font-size:1.3rem;color:#e8f5ee;">
1247
+ 📚 RAG علمی ذخیرہ سسٹم
1248
+ </span>
1249
+ <span class="rag-badge">🔍 Retrieval Augmented Generation</span>
1250
+ </div>
1251
+ <p style="color:#7eb894;font-size:0.9rem;margin:0.2rem 0 0.8rem;">
1252
+ تصویر اپ لوڈ کرنے سے پہلے، ہمارے علمی ذخیرے سے براہ راست جانوروں کے بارے میں سوال کریں۔
1253
+ تصویر اپ لوڈ ہوتے ہی یہ سیکشن خود بخود چھپ جائے گا۔
1254
+ </p>
1255
+ """, unsafe_allow_html=True)
1256
+ else:
1257
+ st.markdown(f"""
1258
+ <div class="rag-section">
1259
+ <div class="rag-header">
1260
+ <span style="font-family:'Syne',sans-serif;font-weight:800;font-size:1.3rem;color:#e8f5ee;">
1261
+ 📚 RAG Knowledge Base System
1262
+ </span>
1263
+ <span class="rag-badge">🔍 Retrieval Augmented Generation</span>
1264
+ </div>
1265
+ <p style="color:#7eb894;font-size:0.9rem;margin:0.2rem 0 0.8rem;">
1266
+ Before uploading an image, query our animal knowledge base directly using RAG.
1267
+ This section <strong style="color:#00e87a;">automatically hides</strong> once you upload an image above.
1268
+ </p>
1269
+ <p style="color:#3d6651;font-size:0.8rem;margin:0 0 0.5rem;">
1270
+ 📂 Knowledge base source: <code style="color:#00c4b4;background:rgba(0,196,180,0.1);
1271
+ padding:0.1rem 0.4rem;border-radius:4px;">animals_data.json</code>
1272
+ &nbsp;·&nbsp; {len(animals_json)} animals loaded
1273
+ </p>
1274
+ <div class="rag-kb-pills">{kb_pills_html}</div>
1275
+ """, unsafe_allow_html=True)
1276
+
1277
+ st.markdown("</div>", unsafe_allow_html=True)
1278
+
1279
+ # ── RAG Query Input ──────────────────────────────────
1280
+ if is_urdu:
1281
+ st.markdown("#### 🔍 علمی ذخیرے سے سوال کریں:")
1282
+ rag_placeholder = "مثال: شیر کہاں رہتا ہے؟ یا ہاتھی کیا کھاتا ہے؟"
1283
+ rag_btn_label = "🔍 علمی ذخیرہ تلاش کریں"
1284
+ rag_samples_label = "**⚡ فوری نمونے:**"
1285
+ rag_samples = [
1286
+ "شیر کہاں رہتا ہے؟",
1287
+ "ہاتھی کیا کھاتا ہے؟",
1288
+ "زرافہ کتنا جیتا ہے؟",
1289
+ "ببر شیر معدوم ہے کیا؟",
1290
+ "شیر کی رفتار کتنی ہے؟",
1291
+ "ہاتھی کا وزن کتنا ہے؟"
1292
+ ]
1293
+ else:
1294
+ st.markdown("#### 🔍 Query the Knowledge Base:")
1295
+ rag_placeholder = "e.g. Where does a lion live? | What does an elephant eat? | Is tiger endangered?"
1296
+ rag_btn_label = "🔍 Search Knowledge Base"
1297
+ rag_samples_label = "**⚡ Quick samples — click to try:**"
1298
+ rag_samples = [
1299
+ "Where does a lion live?",
1300
+ "What does an elephant eat?",
1301
+ "How long does a giraffe live?",
1302
+ "Is the tiger endangered?",
1303
+ "How fast can a lion run?",
1304
+ "How heavy is an elephant?"
1305
+ ]
1306
+
1307
+ rag_col1, rag_col2 = st.columns([3, 1])
1308
+ with rag_col1:
1309
+ rag_query_input = st.text_input(
1310
+ "", placeholder=rag_placeholder, key="rag_query_input",
1311
+ label_visibility="collapsed"
1312
+ )
1313
+ with rag_col2:
1314
+ rag_search_clicked = st.button(
1315
+ rag_btn_label, key="rag_search_btn", use_container_width=True
1316
+ )
1317
+
1318
+ # ── Sample query chips ───────────────────────────────
1319
+ st.markdown(rag_samples_label)
1320
+ sample_cols = st.columns(3)
1321
+ for idx, sq in enumerate(rag_samples):
1322
+ with sample_cols[idx % 3]:
1323
+ if st.button(sq, key=f"rag_sample_{idx}", use_container_width=True):
1324
+ st.session_state["rag_active_query"] = sq
1325
 
1326
+ # ── Resolve the active query ──���──────────────────────
1327
+ active_rag_query = st.session_state.get("rag_active_query", None)
1328
+ final_rag_query = None
1329
+
1330
+ if rag_search_clicked and rag_query_input.strip():
1331
+ final_rag_query = rag_query_input.strip()
1332
+ st.session_state["rag_active_query"] = None # clear chip state
1333
+ elif active_rag_query:
1334
+ final_rag_query = active_rag_query
1335
+
1336
+ # ── RAG Pipeline: Retrieve → Augment → Generate ──────
1337
+ if final_rag_query:
1338
+ with st.spinner(
1339
+ "🔍 Searching knowledge base..." if not is_urdu
1340
+ else "🔍 علمی ذخیرے میں تلاش ہو رہی ہے..."
1341
+ ):
1342
+ matched_animal, field_label, answer_en, answer_ur = rag_retrieve(
1343
+ final_rag_query, animals_json
1344
+ )
1345
+
1346
+ if matched_animal:
1347
+ display_answer = answer_ur if is_urdu else answer_en
1348
+ animal_name_disp = matched_animal["name"]
1349
+ sci_name = matched_animal.get("scientific_name", "")
1350
 
1351
+ # ── Retrieved fact box ───────────────────────
1352
  st.markdown(f"""
1353
+ <div class="rag-result-box">
1354
+ <div class="rag-result-label">
1355
+ 📚 RAG RESULT &nbsp;·&nbsp; {animal_name_disp}
1356
+ &nbsp;<i style="font-weight:400;color:#7eb894;font-size:0.85rem;">
1357
+ ({sci_name})</i>
1358
+ </div>
1359
+ <div class="rag-result-field">{field_label}</div>
1360
+ <p class="rag-result-answer">{display_answer}</p>
1361
+ <div class="rag-result-footer">
1362
+ ✅ Retrieved from <strong>animals_data.json</strong>
1363
+ &nbsp;·&nbsp; Step 1 of RAG: Retrieval complete
1364
+ </div>
1365
  </div>
1366
  """, unsafe_allow_html=True)
1367
 
1368
+ # ── Groq LLM augments and generates ─────────
1369
  if groq_client:
1370
+ with st.spinner(
1371
+ "🤖 AI augmenting answer..." if not is_urdu
1372
+ else "🤖 AI جواب کو وسیع کر رہا ہے..."
1373
+ ):
1374
+ ai_response, _ = get_llm_response(
1375
+ groq_client,
1376
+ animal_name_disp,
1377
+ final_rag_query,
1378
+ is_urdu,
1379
+ tokenizer,
1380
+ translation_model,
1381
+ retrieved_context=display_answer # inject retrieved fact
1382
  )
 
 
 
 
1383
 
1384
+ if is_urdu:
1385
+ st.markdown(
1386
+ f'<div class="assistant-message">'
1387
+ f'🤖 AI جواب (RAG): {ai_response}'
1388
+ f'</div>',
1389
+ unsafe_allow_html=True
1390
+ )
1391
+ else:
1392
+ st.markdown(
1393
+ f'<div class="assistant-message">'
1394
+ f'🤖 AI (RAG-augmented): {ai_response}'
1395
+ f'</div>',
1396
+ unsafe_allow_html=True
1397
+ )
1398
+ else:
1399
+ st.warning(
1400
+ "⚠️ Groq API key not configured — showing retrieved fact only."
1401
+ if not is_urdu
1402
+ else "⚠️ Groq API key نہیں ملی — صرف بنیادی نتیجہ دکھایا جا رہا ہے۔"
1403
+ )
1404
+
1405
+ # Hint to upload image
1406
+ if is_urdu:
1407
+ st.markdown("""
1408
+ <div class="rag-upload-hint">
1409
+ ⬆️ اوپر تصویر اپ لوڈ کریں تاکہ AI خودبخود جانور پہچانے اور یہ سیکشن بند ہو جائے۔
1410
+ </div>
1411
+ """, unsafe_allow_html=True)
1412
+ else:
1413
+ st.markdown("""
1414
+ <div class="rag-upload-hint">
1415
+ ⬆️ Upload an image above to let AI automatically identify the animal
1416
+ — this RAG section will hide automatically.
1417
+ </div>
1418
+ """, unsafe_allow_html=True)
1419
 
1420
  else:
1421
  if is_urdu:
1422
+ st.warning(
1423
+ "⚠️ علمی ذخیرے میں یہ جانور نہیں ملا۔ "
1424
+ "براہ کرم Lion، Tiger، Elephant جیسے معروف نام استعمال کریں۔"
1425
+ )
1426
  else:
1427
+ st.warning(
1428
+ "⚠️ Animal not found in knowledge base. "
1429
+ "Try names like: Lion, Tiger, Elephant, Giraffe."
1430
+ )
1431
+
1432
+ st.markdown("---")
1433
+
1434
+ # ============================================================
1435
+ # IMAGE UPLOAD SECTION
1436
+ # ============================================================
1437
+ col1, col2 = st.columns([1, 1])
1438
+
1439
+ with col1:
1440
+ if is_urdu:
1441
+ st.markdown("### 📤 جانور کی تصویر اپ لوڈ کریں")
1442
+ upload_text = "تصویر منتخب کریں..."
1443
+ help_text = "جانور کی واضح تصویر اپ لوڈ کریں"
1444
+ else:
1445
+ st.markdown("### 📤 Upload Animal Image")
1446
+ upload_text = "Choose an image..."
1447
+ help_text = "Upload a clear image of an animal"
1448
+
1449
+ uploaded_file = st.file_uploader(
1450
+ upload_text,
1451
+ type=['jpg', 'jpeg', 'png', 'webp'],
1452
+ help=help_text
1453
+ )
1454
+
1455
+ # Track upload state so RAG section knows to hide
1456
+ if uploaded_file is not None:
1457
+ st.session_state.has_uploaded_file = True
1458
+ else:
1459
+ st.session_state.has_uploaded_file = False
1460
+
1461
+ if uploaded_file:
1462
+ img, img_array = preprocess_image(uploaded_file)
1463
+ caption = "اپ لوڈ کردہ تصویر" if is_urdu else "Uploaded Image"
1464
+ st.image(img, caption=caption, use_container_width=True)
1465
+
1466
+ with col2:
1467
+ if uploaded_file:
1468
+ if is_urdu:
1469
+ st.markdown("### 🔍 درجہ بندی کے نتائج")
1470
+ else:
1471
+ st.markdown("### 🔍 Classification Results")
1472
+
1473
+ with st.spinner(
1474
+ "🦁 Analyzing image..." if not is_urdu
1475
+ else "🦁 تصویر کا تجزیہ ہو رہا ہے..."
1476
+ ):
1477
+ predictions = model.predict(img_array, verbose=0)[0]
1478
+ fig, top_names, top_probs = create_confidence_chart(predictions, class_names)
1479
+
1480
+ top_animal = top_names[0]
1481
+ top_confidence = top_probs[0]
1482
+
1483
+ st.markdown(f"""
1484
+ <div class="prediction-box">
1485
+ <h2>🐾 {top_animal}</h2>
1486
+ <p>{'Confidence' if not is_urdu else 'اعتماد'}: {top_confidence:.2f}%</p>
1487
+ </div>
1488
+ """, unsafe_allow_html=True)
1489
+
1490
+ st.plotly_chart(fig, use_container_width=True)
1491
+
1492
+ # ============================================================
1493
+ # ANIMAL INFORMATION SECTION (after upload)
1494
+ # ============================================================
1495
+ if uploaded_file:
1496
+ top_animal = top_names[0]
1497
+ animal_data = find_animal_in_json(top_animal, animals_json)
1498
+
1499
+ st.markdown("---")
1500
+ if is_urdu:
1501
+ st.markdown(f"### ℹ️ {top_animal} کے بارے میں")
1502
+ else:
1503
+ st.markdown(f"### ℹ️ About {top_animal}")
1504
+
1505
+ if animal_data:
1506
+ if is_urdu:
1507
+ habitat = animal_data.get("habitat_ur", animal_data.get("habitat", "N/A"))
1508
+ diet = animal_data.get("diet_ur", animal_data.get("diet", "N/A"))
1509
+ conservation = animal_data.get("conservation_ur", animal_data.get("conservation", "N/A"))
1510
+ lifespan = animal_data.get("lifespan_ur", animal_data.get("lifespan", "N/A"))
1511
+ fun_fact = animal_data.get("fun_fact_ur", animal_data.get("fun_fact", "N/A"))
1512
+ scientific_name = animal_data.get("scientific_name", "N/A")
1513
+ speed = animal_data.get("speed_ur", animal_data.get("speed", None)) if "speed" in animal_data else None
1514
+ weight = animal_data.get("weight_ur", animal_data.get("weight", None)) if "weight" in animal_data else None
1515
+ region = animal_data.get("region_ur", animal_data.get("region", None)) if "region" in animal_data else None
1516
+ else:
1517
+ habitat = animal_data.get("habitat", "N/A")
1518
+ diet = animal_data.get("diet", "N/A")
1519
+ conservation = animal_data.get("conservation", "N/A")
1520
+ lifespan = animal_data.get("lifespan", "N/A")
1521
+ fun_fact = animal_data.get("fun_fact", "N/A")
1522
+ scientific_name = animal_data.get("scientific_name", "N/A")
1523
+ speed = animal_data.get("speed", None)
1524
+ weight = animal_data.get("weight", None)
1525
+ region = animal_data.get("region", None)
1526
+
1527
+ c1, c2, c3 = st.columns(3)
1528
+ with c1:
1529
+ st.markdown(f'<div class="info-box"><h4>{"🏠 Habitat" if not is_urdu else "🏠 رہائش گاہ"}</h4><p>{habitat}</p></div>', unsafe_allow_html=True)
1530
+ st.markdown(f'<div class="info-box"><h4>{"🍽️ Diet" if not is_urdu else "🍽️ خوراک"}</h4><p>{diet}</p></div>', unsafe_allow_html=True)
1531
+ with c2:
1532
+ st.markdown(f'<div class="info-box"><h4>{"⚠️ Conservation" if not is_urdu else "⚠️ تحفظ کی حیثیت"}</h4><p>{conservation}</p></div>', unsafe_allow_html=True)
1533
+ st.markdown(f'<div class="info-box"><h4>{"📅 Lifespan" if not is_urdu else "📅 زندگی کی مدت"}</h4><p>{lifespan}</p></div>', unsafe_allow_html=True)
1534
+ with c3:
1535
+ st.markdown(f'<div class="info-box"><h4>{"🔬 Scientific Name" if not is_urdu else "🔬 سائنسی نام"}</h4><p><i>{scientific_name}</i></p></div>', unsafe_allow_html=True)
1536
+ st.markdown(f'<div class="info-box"><h4>{"✨ Fun Fact" if not is_urdu else "✨ دلچسپ حقیقت"}</h4><p>{fun_fact}</p></div>', unsafe_allow_html=True)
1537
+
1538
+ if speed or weight or region:
1539
+ st.markdown("---")
1540
+ c1, c2, c3 = st.columns(3)
1541
+ if speed:
1542
+ with c1:
1543
+ st.markdown(f'<div class="info-box"><h4>⚡ {"Speed" if not is_urdu else "رفتار"}</h4><p>{speed}</p></div>', unsafe_allow_html=True)
1544
+ if weight:
1545
+ with c2:
1546
+ st.markdown(f'<div class="info-box"><h4>⚖️ {"Weight" if not is_urdu else "وزن"}</h4><p>{weight}</p></div>', unsafe_allow_html=True)
1547
+ if region:
1548
+ with c3:
1549
+ st.markdown(f'<div class="info-box"><h4>🌍 {"Region" if not is_urdu else "علاقہ"}</h4><p>{region}</p></div>', unsafe_allow_html=True)
1550
+
1551
+ else:
1552
+ lang_code = "urdu" if is_urdu else "english"
1553
+ adf = get_fallback_animal_info(top_animal, lang_code)
1554
+ c1, c2, c3 = st.columns(3)
1555
+ with c1:
1556
+ st.markdown(f'<div class="info-box"><h4>{"🏠 Habitat" if not is_urdu else "🏠 رہائش گاہ"}</h4><p>{adf["habitat"]}</p></div>', unsafe_allow_html=True)
1557
+ st.markdown(f'<div class="info-box"><h4>{"🍽️ Diet" if not is_urdu else "🍽️ خوراک"}</h4><p>{adf["diet"]}</p></div>', unsafe_allow_html=True)
1558
+ with c2:
1559
+ st.markdown(f'<div class="info-box"><h4>{"⚠️ Conservation" if not is_urdu else "⚠️ تحفظ کی حیثیت"}</h4><p>{adf["conservation"]}</p></div>', unsafe_allow_html=True)
1560
+ st.markdown(f'<div class="info-box"><h4>{"📅 Lifespan" if not is_urdu else "📅 زندگی کی مدت"}</h4><p>{adf["lifespan"]}</p></div>', unsafe_allow_html=True)
1561
+ with c3:
1562
+ st.markdown(f'<div class="info-box"><h4>{"🔬 Scientific Name" if not is_urdu else "🔬 سائنسی نام"}</h4><p><i>{adf["scientific_name"]}</i></p></div>', unsafe_allow_html=True)
1563
+ st.markdown(f'<div class="info-box"><h4>{"✨ Fun Fact" if not is_urdu else "✨ دلچسپ حقیقت"}</h4><p>{adf["fun_fact"]}</p></div>', unsafe_allow_html=True)
1564
+
1565
+ # ============================================================
1566
+ # CHAT SECTION
1567
+ # ============================================================
1568
+ st.markdown("---")
1569
+ if is_urdu:
1570
+ st.markdown(f"### 🤖 {top_animal} کے بارے میں AI اسسٹنٹ سے پوچھیں")
1571
+ else:
1572
+ st.markdown(f"### 🤖 Ask AI Assistant About {top_animal}")
1573
+
1574
+ if "messages" not in st.session_state:
1575
+ if is_urdu:
1576
+ welcome_msg = f"👋 السلام علیکم! میں {top_animal} کے بارے میں آپ کا AI اسسٹنٹ ہوں۔ اس حیرت انگیز جانور کے بارے میں کچھ بھی پوچھیں!"
1577
+ else:
1578
+ welcome_msg = f"👋 Hi! I'm your AI assistant for {top_animal}. Ask me anything about this amazing animal!"
1579
+ st.session_state.messages = [{"role": "assistant", "content": welcome_msg}]
1580
+
1581
+ for message in st.session_state.messages:
1582
+ if message["role"] == "user":
1583
+ label = "🧑‍💻 آپ:" if is_urdu else "🧑‍💻 You:"
1584
+ st.markdown(f'<div class="user-message">{label} {message["content"]}</div>', unsafe_allow_html=True)
1585
+ else:
1586
+ label = "🤖 اسسٹنٹ:" if is_urdu else "🤖 Assistant:"
1587
+ st.markdown(f'<div class="assistant-message">{label} {message["content"]}</div>', unsafe_allow_html=True)
1588
+
1589
+ if is_urdu:
1590
+ st.markdown("#### 💡 پوچھنے کے لیے تجویز کردہ سوالات:")
1591
+ else:
1592
+ st.markdown("#### 💡 Try asking:")
1593
+
1594
+ suggested_questions = get_suggested_questions(top_animal, "urdu" if is_urdu else "english")
1595
+ cols = st.columns(3)
1596
+ for idx, question in enumerate(suggested_questions[:5]):
1597
+ with cols[idx % 3]:
1598
+ if st.button(question, key=f"suggested_{idx}", use_container_width=True):
1599
+ with st.spinner("🤔 Thinking..." if not is_urdu else "🤔 سوچ رہا ہوں..."):
1600
+ st.session_state.messages.append({"role": "user", "content": question})
1601
+ response, _ = get_llm_response(
1602
+ groq_client, top_animal, question,
1603
+ is_urdu, tokenizer, translation_model
1604
+ )
1605
+ st.session_state.messages.append({"role": "assistant", "content": response})
1606
+ st.rerun()
1607
+
1608
+ if is_urdu:
1609
+ st.markdown("#### ✍️ یا اپنا سوال لکھیں:")
1610
+ placeholder = f"{top_animal} کے بارے میں کچھ پوچھیں..."
1611
+ else:
1612
+ st.markdown("#### ✍️ Or type your question:")
1613
+ placeholder = f"Ask something about {top_animal}..."
1614
+
1615
+ text_question = st.text_input("", placeholder=placeholder, key="text_input")
1616
+
1617
+ if st.button("Send Question" if not is_urdu else "سوال بھیجیں",
1618
+ key="send_text_btn", use_container_width=True):
1619
+ if text_question:
1620
+ with st.spinner("🤔 Thinking..." if not is_urdu else "🤔 سوچ رہا ہوں..."):
1621
+ st.session_state.messages.append({"role": "user", "content": text_question})
1622
+ response, _ = get_llm_response(
1623
+ groq_client, top_animal, text_question,
1624
+ is_urdu, tokenizer, translation_model
1625
+ )
1626
+ st.session_state.messages.append({"role": "assistant", "content": response})
1627
+ st.rerun()
1628
+
1629
+ # ============================================================
1630
+ # VOICE INPUT SECTION
1631
+ # ============================================================
1632
+ st.markdown("---")
1633
+ if is_urdu:
1634
+ st.markdown("### 🎤 آواز سے سوال پوچھیں")
1635
+ st.markdown("نیچے دیے گئے مائیکروفون پر کلک کریں، ریکارڈ کریں، پھر 'Process Voice' بٹن دبائیں")
1636
+ else:
1637
+ st.markdown("### 🎤 Ask Question by Voice")
1638
+ st.markdown("Click the microphone below, record, then click 'Process Voice' button")
1639
+
1640
+ audio_value = st.audio_input(
1641
+ "Record your voice" if not is_urdu else "اپنی آواز ریکارڈ کریں"
1642
+ )
1643
+
1644
+ if "stored_audio" not in st.session_state:
1645
+ st.session_state.stored_audio = None
1646
+
1647
+ if audio_value:
1648
+ st.session_state.stored_audio = audio_value
1649
+ st.success("✅ Audio recorded! Click 'Process Voice' to send.")
1650
+
1651
+ vc1, vc2 = st.columns(2)
1652
+ with vc1:
1653
+ if st.button(
1654
+ "🎤 Process Voice" if not is_urdu else "🎤 آواز پروسیس کریں",
1655
+ key="process_voice_btn", use_container_width=True
1656
+ ):
1657
+ if st.session_state.stored_audio:
1658
+ with st.spinner(
1659
+ "🎤 Processing your voice..." if not is_urdu
1660
+ else "🎤 آواز پر کارروائی ہو رہی ہے..."
1661
+ ):
1662
+ if groq_client:
1663
+ transcribed_text = speech_to_text_groq(
1664
+ groq_client, st.session_state.stored_audio
1665
+ )
1666
+ if transcribed_text:
1667
+ st.success(f"📝 Recognized: {transcribed_text}")
1668
+ st.session_state.messages.append(
1669
+ {"role": "user", "content": transcribed_text}
1670
+ )
1671
+ response, _ = get_llm_response(
1672
+ groq_client, top_animal, transcribed_text,
1673
+ is_urdu, tokenizer, translation_model
1674
+ )
1675
+ st.session_state.messages.append(
1676
+ {"role": "assistant", "content": response}
1677
+ )
1678
+ st.session_state.stored_audio = None
1679
+ st.rerun()
1680
+ else:
1681
+ st.error("Failed to transcribe. Please try again.")
1682
+ else:
1683
+ st.error("Groq client not configured. Please add GROQ_API_KEY")
1684
+ else:
1685
+ st.warning(
1686
+ "No audio recorded. Please record your voice first."
1687
+ if not is_urdu
1688
+ else "کوئی آواز ریکارڈ نہیں کی گئی۔ براہ کرم پہلے آواز ریکارڈ کریں۔"
1689
+ )
1690
+
1691
+ with vc2:
1692
+ if st.button(
1693
+ "🗑️ Clear Audio" if not is_urdu else "🗑️ آواز صاف کریں",
1694
+ key="clear_audio_btn", use_container_width=True
1695
+ ):
1696
+ st.session_state.stored_audio = None
1697
+ st.rerun()
1698
+
1699
+ st.markdown("---")
1700
+ if st.button(
1701
+ "🗑️ Clear Chat History" if not is_urdu else "🗑️ چیٹ ہسٹری صاف کریں",
1702
+ key="clear_chat_btn", use_container_width=True
1703
+ ):
1704
+ if is_urdu:
1705
+ welcome_msg = f"👋 السلام علیکم! میں {top_animal} کے بارے میں آپ کا AI اسسٹنٹ ہوں۔ اس حیرت انگیز جانور کے بارے میں کچھ بھی پوچھیں!"
1706
+ else:
1707
+ welcome_msg = f"👋 Hi! I'm your AI assistant for {top_animal}. Ask me anything about this amazing animal!"
1708
+ st.session_state.messages = [{"role": "assistant", "content": welcome_msg}]
1709
+ st.rerun()
1710
+
1711
+ # ============================================================
1712
+ # STATISTICS SECTION
1713
+ # ============================================================
1714
+ st.markdown("---")
1715
+ if is_urdu:
1716
+ st.markdown("### 📊 ماڈل کے اعدادوشمار")
1717
+ else:
1718
+ st.markdown("### 📊 Model Statistics")
1719
+
1720
+ sc1, sc2, sc3, sc4 = st.columns(4)
1721
+ with sc1:
1722
+ st.markdown(f'<div class="stat-card"><h3>🎯 85%</h3><p>{"Model Accuracy" if not is_urdu else "ماڈل کی درستگی"}</p></div>', unsafe_allow_html=True)
1723
+ with sc2:
1724
+ st.markdown(f'<div class="stat-card"><h3>{len(class_names)}+</h3><p>{"Animal Classes" if not is_urdu else "جانوروں کی کلاسیں"}</p></div>', unsafe_allow_html=True)
1725
+ with sc3:
1726
+ st.markdown(f'<div class="stat-card"><h3>10k+</h3><p>{"Training Images" if not is_urdu else "تربیتی تصاویر"}</p></div>', unsafe_allow_html=True)
1727
+ with sc4:
1728
+ st.markdown(f'<div class="stat-card"><h3>&lt;0.1s</h3><p>{"Inference Time" if not is_urdu else "تشخیص کا وقت"}</p></div>', unsafe_allow_html=True)
1729
+
1730
+ # ============================================================
1731
+ # FOOTER
1732
+ # ============================================================
1733
+ if is_urdu:
1734
+ st.markdown("""
1735
+ <div class="footer">
1736
+ <p>🐾 SmartZoo AI - ٹیکنالوجی کے ذریعے جنگلی حیات کا تحفظ 🐾</p>
1737
+ <p>TensorFlow، MobileNetV2، Groq LLM، Helsinki-NLP اور RAG سسٹم کے ذریعے تقویت یافتہ</p>
1738
+ <p style="font-size: 0.8rem;">⚠️ نوٹ: ماڈل کی درستگی تصویر کے معیار کے لحاظ سے مختلف ہو سکتی ہے۔</p>
1739
+ </div>
1740
+ """, unsafe_allow_html=True)
1741
+ else:
1742
+ st.markdown("""
1743
+ <div class="footer">
1744
+ <p>🐾 SmartZoo AI - Protecting Wildlife Through Technology 🐾</p>
1745
+ <p>Powered by TensorFlow · MobileNetV2 · Groq LLM · Helsinki-NLP · RAG Knowledge Base</p>
1746
+ <p style="font-size: 0.8rem;">⚠️ Note: Model accuracy may vary based on image quality.</p>
1747
+ </div>
1748
+ """, unsafe_allow_html=True)
1749
+
1750
 
1751
+ if __name__ == "__main__":
1752
+ main()