KUNAL SHAW commited on
Commit
01e771f
·
1 Parent(s): 71c1218

Fix chat_input - must be at root level

Browse files
Files changed (1) hide show
  1. streamlit_app/app.py +124 -349
streamlit_app/app.py CHANGED
@@ -1,413 +1,188 @@
1
  """
2
  Streamlit App for RAG Chatbot - Agentic AI eBook
3
-
4
- This is the main UI for the RAG chatbot. It provides:
5
- - Chat interface for asking questions
6
- - Configuration sidebar (API keys, top_k, etc.)
7
- - Display of retrieved chunks and confidence scores
8
- - Raw JSON response viewer
9
-
10
- Usage:
11
- streamlit run streamlit_app/app.py
12
-
13
- For Hugging Face Spaces deployment:
14
- - Set secrets in Space settings for PINECONE_API_KEY, OPENAI_API_KEY
15
- - Or let users input keys in the sidebar
16
  """
17
 
18
  import os
19
  import sys
20
- import json
21
  import streamlit as st
22
  from dotenv import load_dotenv
23
 
24
- # Add parent directory to path for imports
25
  sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
26
 
27
- # Load environment variables from the project root .env file
28
  env_path = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), '.env')
29
  load_dotenv(env_path)
30
 
31
  # Import RAG pipeline
32
  from app.rag_pipeline import RAGPipeline
33
 
34
- # ============================================================================
35
- # Page Configuration
36
- # ============================================================================
37
-
38
  st.set_page_config(
39
  page_title="Agentic AI eBook Chatbot",
40
  page_icon="🤖",
41
- layout="wide",
42
- initial_sidebar_state="expanded"
43
  )
44
 
45
- # Custom CSS for better styling
46
  st.markdown("""
47
  <style>
48
- /* Main container styling */
49
- .main-header {
50
- font-size: 2.5rem;
51
- font-weight: bold;
52
- color: #1E88E5;
53
  text-align: center;
54
- margin-bottom: 1rem;
55
- }
56
-
57
- /* Answer card styling */
58
- .answer-card {
59
- background-color: #f0f7ff;
60
- border-left: 4px solid #1E88E5;
61
- padding: 1rem;
62
- border-radius: 0 8px 8px 0;
63
- margin: 1rem 0;
64
- }
65
-
66
- /* Confidence badge styling */
67
- .confidence-badge {
68
- display: inline-block;
69
- padding: 0.25rem 0.75rem;
70
- border-radius: 1rem;
71
- font-weight: bold;
72
- font-size: 0.9rem;
73
- }
74
-
75
- .confidence-high {
76
- background-color: #c8e6c9;
77
- color: #2e7d32;
78
  }
79
-
80
- .confidence-medium {
81
- background-color: #fff3e0;
82
- color: #ef6c00;
83
- }
84
-
85
- .confidence-low {
86
- background-color: #ffcdd2;
87
- color: #c62828;
88
- }
89
-
90
- /* Chunk card styling */
91
- .chunk-card {
92
- background-color: #fafafa;
93
- border: 1px solid #e0e0e0;
94
- padding: 0.75rem;
95
- border-radius: 8px;
96
- margin: 0.5rem 0;
97
- }
98
-
99
- /* Footer styling */
100
- .footer {
101
  text-align: center;
102
- color: #666;
103
- font-size: 0.8rem;
104
- margin-top: 2rem;
105
- padding-top: 1rem;
106
- border-top: 1px solid #e0e0e0;
107
  }
 
 
 
 
108
  </style>
109
  """, unsafe_allow_html=True)
110
 
111
-
112
- # ============================================================================
113
- # Session State Initialization
114
- # ============================================================================
115
-
116
  if "messages" not in st.session_state:
117
  st.session_state.messages = []
118
-
119
  if "pipeline" not in st.session_state:
120
  st.session_state.pipeline = None
121
 
122
- if "last_response" not in st.session_state:
123
- st.session_state.last_response = None
124
-
125
-
126
- # ============================================================================
127
- # Sidebar Configuration
128
- # ============================================================================
129
-
130
  with st.sidebar:
131
- st.header("⚙️ Configuration")
132
-
133
- st.markdown("---")
134
 
135
- # API Keys section
136
- st.subheader("🔑 API Keys")
137
-
138
- # Pinecone API Key
139
- pinecone_key = st.text_input(
140
- "Pinecone API Key",
141
- type="password",
142
- value=os.getenv("PINECONE_API_KEY", ""),
143
- help="Required for vector search. Get your key at pinecone.io"
144
- )
145
-
146
- # Pinecone Index Name
147
- index_name = st.text_input(
148
- "Pinecone Index Name",
149
- value=os.getenv("PINECONE_INDEX", "agentic-ai-ebook"),
150
- help="Name of your Pinecone index"
151
- )
152
-
153
- # OpenAI API Key (optional)
154
- openai_key = st.text_input(
155
- "OpenAI API Key (optional)",
156
- type="password",
157
- value=os.getenv("OPENAI_API_KEY", ""),
158
- help="For LLM-powered answers. Leave empty if using Groq."
159
- )
160
-
161
- # Groq API Key (optional - FREE!)
162
- groq_key = st.text_input(
163
- "Groq API Key (FREE LLM)",
164
- type="password",
165
- value=os.getenv("GROQ_API_KEY", ""),
166
- help="Free LLM alternative! Get key at console.groq.com"
167
- )
168
 
169
  st.markdown("---")
170
-
171
- # Retrieval settings
172
- st.subheader("🔍 Retrieval Settings")
173
-
174
- top_k = st.slider(
175
- "Number of chunks to retrieve (top_k)",
176
- min_value=1,
177
- max_value=10,
178
- value=6,
179
- help="More chunks = more context but potentially more noise"
180
- )
181
-
182
- use_llm = st.checkbox(
183
- "Use LLM for answer generation",
184
- value=True,
185
- help="Uncheck to always use extractive mode"
186
- )
187
-
188
- local_mode = st.checkbox(
189
- "Local Mode (no Pinecone)",
190
- value=False,
191
- help="Use local vector storage instead of Pinecone"
192
- )
193
 
194
  st.markdown("---")
195
-
196
- # Initialize/Reinitialize button
197
- if st.button("🔄 Initialize Pipeline", use_container_width=True):
198
- with st.spinner("Initializing RAG pipeline..."):
199
  try:
200
  st.session_state.pipeline = RAGPipeline(
201
- pinecone_api_key=pinecone_key if pinecone_key else None,
 
 
202
  openai_api_key=openai_key if openai_key else None,
203
  groq_api_key=groq_key if groq_key else None,
204
- index_name=index_name,
205
- local_only=local_mode,
206
- top_k=top_k
207
  )
208
- st.success("✅ Pipeline initialized!")
209
  except Exception as e:
210
- st.error(f"❌ Error: {str(e)}")
211
-
212
- # Status indicator
213
- st.markdown("---")
214
- st.subheader("📊 Status")
215
 
216
  if st.session_state.pipeline:
217
- st.success("Pipeline: Ready")
218
- if st.session_state.pipeline.groq_client:
219
- st.info("Mode: Groq LLM (FREE)")
220
- elif st.session_state.pipeline.openai_client:
221
- st.info("Mode: OpenAI LLM")
222
- else:
223
- st.warning("Mode: Extractive (no LLM)")
224
  else:
225
- st.warning("Pipeline: Not initialized")
226
- st.caption("Click 'Initialize Pipeline' to start")
227
 
228
-
229
- # ============================================================================
230
- # Main Content Area
231
- # ============================================================================
 
 
 
 
 
 
 
 
 
 
 
 
232
 
233
  # Header
234
- st.markdown('<div class="main-header">🤖 Agentic AI eBook Chatbot</div>', unsafe_allow_html=True)
235
-
236
- st.markdown("""
237
- <p style="text-align: center; color: #666;">
238
- Ask questions about the Agentic AI eBook. Answers are strictly grounded in the document.
239
- </p>
240
- """, unsafe_allow_html=True)
241
-
242
- st.markdown("---")
243
-
244
- # Check if pipeline is initialized
245
- if not st.session_state.pipeline:
246
- st.info("👈 Please configure your API keys and click 'Initialize Pipeline' in the sidebar to start.")
247
-
248
- # Show sample queries
249
- st.subheader("📝 Sample Questions to Try")
250
- sample_queries = [
251
- "What is the definition of 'agentic AI' described in the eBook?",
252
- "List the three risks of agentic systems the eBook mentions.",
253
- "What are the recommended safeguards for deploying agentic AI?",
254
- "How does the eBook distinguish between autonomous agents and traditional automation?",
255
- "What future research directions does the eBook propose?"
 
256
  ]
257
-
258
- for query in sample_queries:
259
- st.markdown(f"- {query}")
260
-
261
- else:
262
- # Chat interface
263
- col1, col2 = st.columns([2, 1])
264
-
265
- with col1:
266
- st.subheader("💬 Chat")
267
-
268
- # Display chat history
269
- chat_container = st.container()
270
-
271
- with chat_container:
272
- for message in st.session_state.messages:
273
- with st.chat_message(message["role"]):
274
- st.write(message["content"])
275
-
276
- # Chat input
277
- user_input = st.chat_input("Ask a question about the Agentic AI eBook...")
278
-
279
- if user_input:
280
- # Add user message to chat
281
- st.session_state.messages.append({"role": "user", "content": user_input})
282
-
283
- # Display user message
284
- with st.chat_message("user"):
285
- st.write(user_input)
286
-
287
- # Get response from pipeline
288
- with st.chat_message("assistant"):
289
- with st.spinner("Searching document and generating answer..."):
290
- try:
291
- response = st.session_state.pipeline.query(
292
- user_input,
293
- top_k=top_k,
294
- use_llm=use_llm
295
- )
296
-
297
- # Store response for display
298
- st.session_state.last_response = response
299
-
300
- # Display answer
301
- answer = response.get("final_answer", "No answer generated")
302
- st.write(answer)
303
-
304
- # Display confidence
305
- confidence = response.get("confidence", 0.0)
306
- if confidence >= 0.7:
307
- conf_class = "confidence-high"
308
- elif confidence >= 0.4:
309
- conf_class = "confidence-medium"
310
- else:
311
- conf_class = "confidence-low"
312
-
313
- st.markdown(
314
- f'<span class="confidence-badge {conf_class}">Confidence: {confidence:.3f}</span>',
315
- unsafe_allow_html=True
316
- )
317
-
318
- # Add assistant message to chat
319
- st.session_state.messages.append({
320
- "role": "assistant",
321
- "content": answer
322
- })
323
-
324
- except Exception as e:
325
- st.error(f"Error: {str(e)}")
326
- st.session_state.messages.append({
327
- "role": "assistant",
328
- "content": f"Error: {str(e)}"
329
- })
330
-
331
- # Clear chat button
332
- if st.button("🗑️ Clear Chat", use_container_width=True):
333
- st.session_state.messages = []
334
- st.session_state.last_response = None
335
- st.rerun()
336
-
337
- with col2:
338
- st.subheader("📚 Retrieved Chunks")
339
 
340
- if st.session_state.last_response:
341
- response = st.session_state.last_response
 
 
 
342
  chunks = response.get("retrieved_chunks", [])
 
343
 
344
- if chunks:
345
- for i, chunk in enumerate(chunks):
346
- with st.expander(
347
- f"Chunk {i+1} (Page {chunk.get('page', '?')}, Score: {chunk.get('score', 0):.3f})",
348
- expanded=(i == 0)
349
- ):
350
- st.markdown(f"**ID:** `{chunk.get('id', 'unknown')}`")
351
- st.markdown(f"**Page:** {chunk.get('page', 'unknown')}")
352
- st.markdown(f"**Relevance Score:** {chunk.get('score', 0):.4f}")
353
- st.markdown("**Text:**")
354
- st.text_area(
355
- "Chunk text",
356
- value=chunk.get("text", ""),
357
- height=150,
358
- label_visibility="collapsed",
359
- key=f"chunk_{i}"
360
- )
361
- else:
362
- st.info("No chunks retrieved yet. Ask a question!")
363
-
364
- # Raw JSON viewer
365
- st.markdown("---")
366
- with st.expander("🔍 Show Raw JSON Response"):
367
- st.json(response)
368
- else:
369
- st.info("Ask a question to see retrieved chunks.")
370
-
371
 
372
- # ============================================================================
373
  # Footer
374
- # ============================================================================
375
-
376
  st.markdown("---")
377
  st.markdown("""
378
- <div class="footer">
379
- <p>
380
- <strong>Built for AI Engineer Intern Assignment</strong><br>
381
- Answers are strictly grounded in the Agentic AI eBook.<br>
382
- Using: LangGraph • Pinecone • Sentence-Transformers • Streamlit
383
- </p>
384
  </div>
385
  """, unsafe_allow_html=True)
386
-
387
-
388
- # ============================================================================
389
- # Auto-initialize if env vars are set
390
- # ============================================================================
391
-
392
- # Try to auto-initialize on first load if env vars are present
393
- if st.session_state.pipeline is None:
394
- env_pinecone = os.getenv("PINECONE_API_KEY")
395
- env_groq = os.getenv("GROQ_API_KEY")
396
- if env_pinecone:
397
- try:
398
- st.session_state.pipeline = RAGPipeline(
399
- pinecone_api_key=env_pinecone,
400
- openai_api_key=os.getenv("OPENAI_API_KEY"),
401
- groq_api_key=env_groq,
402
- index_name=os.getenv("PINECONE_INDEX", "agentic-ai-ebook"),
403
- local_only=False
404
- )
405
- # Debug: show which LLM is being used
406
- if st.session_state.pipeline.groq_client:
407
- st.sidebar.success("✅ Groq LLM connected!")
408
- elif st.session_state.pipeline.openai_client:
409
- st.sidebar.info("ℹ️ OpenAI LLM connected")
410
- else:
411
- st.sidebar.warning("⚠️ No LLM - using extractive mode")
412
- except Exception as e:
413
- st.sidebar.error(f"Auto-init failed: {e}")
 
1
  """
2
  Streamlit App for RAG Chatbot - Agentic AI eBook
3
+ Modern ChatGPT-style UI with chat_input at root level
 
 
 
 
 
 
 
 
 
 
 
 
4
  """
5
 
6
  import os
7
  import sys
 
8
  import streamlit as st
9
  from dotenv import load_dotenv
10
 
11
+ # Add parent directory to path
12
  sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
13
 
14
+ # Load environment variables
15
  env_path = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), '.env')
16
  load_dotenv(env_path)
17
 
18
  # Import RAG pipeline
19
  from app.rag_pipeline import RAGPipeline
20
 
21
+ # Page config
 
 
 
22
  st.set_page_config(
23
  page_title="Agentic AI eBook Chatbot",
24
  page_icon="🤖",
25
+ layout="centered"
 
26
  )
27
 
28
+ # CSS styling
29
  st.markdown("""
30
  <style>
31
+ .main-title {
 
 
 
 
32
  text-align: center;
33
+ font-size: 2.2rem;
34
+ font-weight: 700;
35
+ color: #667eea;
36
+ margin-bottom: 0.5rem;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
37
  }
38
+ .subtitle {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
39
  text-align: center;
40
+ color: #888;
41
+ margin-bottom: 1.5rem;
 
 
 
42
  }
43
+ .conf-high { background: #d4edda; color: #155724; padding: 0.2rem 0.6rem; border-radius: 10px; font-size: 0.8rem; }
44
+ .conf-medium { background: #fff3cd; color: #856404; padding: 0.2rem 0.6rem; border-radius: 10px; font-size: 0.8rem; }
45
+ .conf-low { background: #f8d7da; color: #721c24; padding: 0.2rem 0.6rem; border-radius: 10px; font-size: 0.8rem; }
46
+ .source-chip { background: #e9ecef; color: #495057; padding: 0.15rem 0.5rem; border-radius: 10px; font-size: 0.75rem; margin: 0.1rem; display: inline-block; }
47
  </style>
48
  """, unsafe_allow_html=True)
49
 
50
+ # Session state
 
 
 
 
51
  if "messages" not in st.session_state:
52
  st.session_state.messages = []
 
53
  if "pipeline" not in st.session_state:
54
  st.session_state.pipeline = None
55
 
56
+ # Sidebar
 
 
 
 
 
 
 
57
  with st.sidebar:
58
+ st.header("⚙️ Settings")
 
 
59
 
60
+ pinecone_key = st.text_input("Pinecone API Key", type="password", value=os.getenv("PINECONE_API_KEY", ""))
61
+ index_name = st.text_input("Pinecone Index", value=os.getenv("PINECONE_INDEX", "agentic-ai-ebook"))
62
+ groq_key = st.text_input("Groq API Key (FREE)", type="password", value=os.getenv("GROQ_API_KEY", ""))
63
+ openai_key = st.text_input("OpenAI Key (optional)", type="password", value=os.getenv("OPENAI_API_KEY", ""))
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
64
 
65
  st.markdown("---")
66
+ top_k = st.slider("Chunks to retrieve", 1, 10, 6)
67
+ use_llm = st.checkbox("Use LLM", value=True)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
68
 
69
  st.markdown("---")
70
+ if st.button("🚀 Initialize Pipeline", type="primary", use_container_width=True):
71
+ with st.spinner("Initializing..."):
 
 
72
  try:
73
  st.session_state.pipeline = RAGPipeline(
74
+ pinecone_api_key=pinecone_key,
75
+ index_name=index_name,
76
+ namespace="agentic-ai",
77
  openai_api_key=openai_key if openai_key else None,
78
  groq_api_key=groq_key if groq_key else None,
79
+ local_only=False
 
 
80
  )
81
+ st.success("✅ Ready!")
82
  except Exception as e:
83
+ st.error(f"❌ {str(e)}")
 
 
 
 
84
 
85
  if st.session_state.pipeline:
86
+ st.success("Pipeline Ready")
 
 
 
 
 
 
87
  else:
88
+ st.warning(" Not Initialized")
 
89
 
90
+ # Auto-initialize if env vars exist
91
+ if st.session_state.pipeline is None:
92
+ pk = os.getenv("PINECONE_API_KEY", "")
93
+ gk = os.getenv("GROQ_API_KEY", "")
94
+ if pk and gk:
95
+ try:
96
+ st.session_state.pipeline = RAGPipeline(
97
+ pinecone_api_key=pk,
98
+ index_name=os.getenv("PINECONE_INDEX", "agentic-ai-ebook"),
99
+ namespace="agentic-ai",
100
+ openai_api_key=os.getenv("OPENAI_API_KEY", ""),
101
+ groq_api_key=gk,
102
+ local_only=False
103
+ )
104
+ except:
105
+ pass
106
 
107
  # Header
108
+ st.markdown('<h1 class="main-title">🤖 Agentic AI Chatbot</h1>', unsafe_allow_html=True)
109
+ st.markdown('<p class="subtitle">Ask questions about the Agentic AI eBook • Grounded answers only</p>', unsafe_allow_html=True)
110
+
111
+ # Display chat messages
112
+ for msg in st.session_state.messages:
113
+ with st.chat_message(msg["role"]):
114
+ st.write(msg["content"])
115
+ if msg["role"] == "assistant" and "confidence" in msg:
116
+ conf = msg["confidence"]
117
+ conf_class = "conf-high" if conf >= 0.7 else ("conf-medium" if conf >= 0.4 else "conf-low")
118
+ st.markdown(f'<span class="{conf_class}">Confidence: {conf:.0%}</span>', unsafe_allow_html=True)
119
+ if msg.get("sources"):
120
+ sources_html = " ".join([f'<span class="source-chip">📄 P.{s}</span>' for s in msg["sources"][:5]])
121
+ st.markdown(f"Sources: {sources_html}", unsafe_allow_html=True)
122
+
123
+ # Sample questions if empty
124
+ if not st.session_state.messages:
125
+ st.markdown("### 💡 Try asking:")
126
+ samples = [
127
+ "What is the definition of agentic AI?",
128
+ "What are the key characteristics of agentic systems?",
129
+ "What risks does the eBook mention?",
130
+ "What safeguards are recommended?"
131
  ]
132
+ cols = st.columns(2)
133
+ for i, q in enumerate(samples):
134
+ with cols[i % 2]:
135
+ if st.button(q, key=f"s{i}", use_container_width=True):
136
+ if st.session_state.pipeline:
137
+ st.session_state.messages.append({"role": "user", "content": q})
138
+ st.rerun()
139
+ else:
140
+ st.warning("Initialize pipeline first!")
141
+
142
+ # Clear button
143
+ if st.session_state.messages:
144
+ if st.button("🗑️ Clear Chat"):
145
+ st.session_state.messages = []
146
+ st.rerun()
147
+
148
+ # Chat input - MUST be at root level (not inside any container/column)
149
+ user_input = st.chat_input("Ask a question about the Agentic AI eBook...")
150
+
151
+ if user_input:
152
+ if st.session_state.pipeline is None:
153
+ st.warning("⚠️ Please initialize the pipeline first (sidebar → Initialize)")
154
+ else:
155
+ # Add user message
156
+ st.session_state.messages.append({"role": "user", "content": user_input})
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
157
 
158
+ # Get response
159
+ try:
160
+ response = st.session_state.pipeline.query(user_input, top_k=top_k, use_llm=use_llm)
161
+ answer = response.get("final_answer", "No answer found.")
162
+ confidence = response.get("confidence", 0.0)
163
  chunks = response.get("retrieved_chunks", [])
164
+ sources = sorted(set([c.get("page", "?") for c in chunks]))
165
 
166
+ st.session_state.messages.append({
167
+ "role": "assistant",
168
+ "content": answer,
169
+ "confidence": confidence,
170
+ "sources": sources
171
+ })
172
+ except Exception as e:
173
+ st.session_state.messages.append({
174
+ "role": "assistant",
175
+ "content": f"Error: {str(e)}",
176
+ "confidence": 0,
177
+ "sources": []
178
+ })
179
+
180
+ st.rerun()
 
 
 
 
 
 
 
 
 
 
 
 
181
 
 
182
  # Footer
 
 
183
  st.markdown("---")
184
  st.markdown("""
185
+ <div style="text-align: center; color: #888; font-size: 0.8rem;">
186
+ Built with LangGraph • Pinecone • Groq • Streamlit
 
 
 
 
187
  </div>
188
  """, unsafe_allow_html=True)