MBilal-72 commited on
Commit
f4007a0
Β·
verified Β·
1 Parent(s): 2e6803f

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +439 -349
app.py CHANGED
@@ -1,5 +1,5 @@
1
  """
2
- Main Streamlit Application - GEO SEO AI Optimizer
3
  Entry point for the application with UI components
4
  """
5
 
@@ -8,12 +8,12 @@ import os
8
  import tempfile
9
  import json
10
  from typing import Dict, Any, List
11
- import time # Add this if not present
12
 
13
  # Import our custom modules
14
  from utils.parser import PDFParser, TextParser, WebpageParser
15
  from utils.scorer import GEOScorer
16
- from utils.optimizer import ContentOptimizer
17
  from utils.chunker import VectorChunker
18
  from utils.export import ResultExporter
19
 
@@ -46,11 +46,9 @@ class GEOSEOApp:
46
  temperature=0.1
47
  )
48
 
49
- # Updated embeddings initialization without the `device` parameter
50
  self.embeddings = HuggingFaceEmbeddings(
51
  model_name="sentence-transformers/all-MiniLM-L6-v2",
52
- model_kwargs={"device": "cpu"},
53
- cache_folder="./hf_cache",
54
  )
55
 
56
  def setup_parsers(self):
@@ -60,10 +58,13 @@ class GEOSEOApp:
60
  self.webpage_parser = WebpageParser()
61
 
62
  def setup_components(self):
63
- """Initialize processing components"""
64
  self.geo_scorer = GEOScorer(self.llm)
65
- self.content_optimizer = ContentOptimizer(self.llm)
66
  self.vector_chunker = VectorChunker(self.embeddings)
 
 
 
 
67
  self.result_exporter = ResultExporter()
68
 
69
  def run(self):
@@ -75,7 +76,7 @@ class GEOSEOApp:
75
  )
76
 
77
  st.title("πŸš€ GEO SEO AI Optimizer")
78
- st.markdown("*Optimize your content for AI search engines and LLM systems*")
79
 
80
  # Sidebar
81
  self.render_sidebar()
@@ -83,7 +84,7 @@ class GEOSEOApp:
83
  # Main tabs
84
  tab1, tab2, tab3 = st.tabs([
85
  "🌐 Website GEO Analysis",
86
- "πŸ”§ Content Enhancement",
87
  "πŸ“„ Document Q&A",
88
  ])
89
 
@@ -91,7 +92,7 @@ class GEOSEOApp:
91
  self.render_website_analysis_tab()
92
 
93
  with tab2:
94
- self.render_content_enhancement_tab()
95
 
96
  with tab3:
97
  self.render_document_qa_tab()
@@ -100,7 +101,7 @@ class GEOSEOApp:
100
  """Render sidebar with information and controls"""
101
  st.sidebar.title("πŸ› οΈ GEO Tools")
102
  st.sidebar.markdown("- πŸ“„ Document Q&A with RAG")
103
- st.sidebar.markdown("- πŸ”§ Content Enhancement")
104
  st.sidebar.markdown("- 🌐 Website GEO Analysis")
105
  st.sidebar.markdown("- πŸ“Š AI-First SEO Scoring")
106
 
@@ -115,299 +116,337 @@ class GEOSEOApp:
115
  st.sidebar.markdown("**Query Intent Matching**: How well content matches user queries")
116
  st.sidebar.markdown("**Conversational Readiness**: Suitability for AI chat responses")
117
  st.sidebar.markdown("**Citation Worthiness**: Probability of being cited by AI")
 
 
118
 
119
  st.sidebar.markdown("---")
120
- st.sidebar.markdown("### ℹ️ Components")
121
- st.sidebar.markdown("- **Parser**: Extract content from various sources")
122
- st.sidebar.markdown("- **Scorer**: Analyze GEO performance")
123
- st.sidebar.markdown("- **Optimizer**: Enhance content for AI")
124
- st.sidebar.markdown("- **Chunker**: Create vector embeddings")
125
- st.sidebar.markdown("- **Exporter**: Generate reports")
126
-
127
- def render_document_qa_tab(self):
128
- """Render Document Q&A tab"""
129
- st.header("πŸ“„ Document Question Answering")
130
- st.markdown("Upload documents or paste text to ask questions using RAG.")
131
-
132
- # File upload
133
- uploaded_file = st.file_uploader("Upload a PDF file", type=["pdf"])
134
-
135
- # Text input
136
- pasted_text = st.text_area("Or paste text directly:", height=150)
137
-
138
- # Question input
139
- user_query = st.text_input("Ask a question about the content:")
140
-
141
- # Submit button
142
- if st.button("πŸ” Ask Question", key="qa_submit"):
143
- if not user_query.strip():
144
- st.warning("Please enter a question.")
145
- return
146
-
147
- try:
148
- # Parse content
149
- documents = []
150
-
151
- if uploaded_file:
152
- with st.spinner("Processing PDF..."):
153
- # Save uploaded file temporarily
154
- temp_path = self.save_uploaded_file(uploaded_file)
155
- documents = self.pdf_parser.parse(temp_path)
156
- os.unlink(temp_path) # Clean up
157
-
158
- elif pasted_text.strip():
159
- with st.spinner("Processing text..."):
160
- documents = self.text_parser.parse(pasted_text)
161
-
162
- else:
163
- st.warning("Please upload a PDF or paste some text.")
164
- return
165
-
166
- # Create vector store and answer question
167
- with st.spinner("Creating embeddings and searching..."):
168
- qa_chain = self.vector_chunker.create_qa_chain(documents, self.llm)
169
- result = qa_chain({"query": user_query})
170
-
171
- # Display results
172
- st.markdown("### πŸ’¬ Answer")
173
- st.write(result["result"])
174
-
175
- # Show sources
176
- with st.expander("πŸ“„ Source Documents"):
177
- for i, doc in enumerate(result.get("source_documents", [])):
178
- st.write(f"**Source {i+1}:**")
179
- content = doc.page_content
180
- st.write(content[:500] + "..." if len(content) > 500 else content)
181
- if hasattr(doc, 'metadata') and doc.metadata:
182
- st.write(f"*Metadata: {doc.metadata}*")
183
- st.write("---")
184
-
185
- except Exception as e:
186
- st.error(f"An error occurred: {str(e)}")
187
 
188
- def render_content_enhancement_tab(self):
189
- """Render Content Enhancement tab with optimization type selector"""
190
- st.header("πŸ”§ Content Enhancement")
191
- st.markdown("Analyze and optimize your content for better AI/LLM performance.")
192
 
193
  # Content input
194
  input_text = st.text_area(
195
  "Enter content to analyze and enhance:",
196
  height=200,
197
- key="enhancement_input"
 
198
  )
199
 
200
- # Optimization type selector
201
- st.markdown("### βš™οΈ Optimization Settings")
202
  col1, col2 = st.columns(2)
203
 
204
  with col1:
205
  optimization_type = st.selectbox(
206
- "Select Optimization Type:",
207
  options=[
208
- "standard",
209
- "seo",
210
- # "competitive",
211
- # "voice_search",
212
- # "batch_optimize",
213
- # "content_variations",
214
- "readability_analysis",
215
- # "entity_extraction"
216
  ],
217
  format_func=lambda x: {
218
- "standard": "πŸ”§ Standard Enhancement",
219
- "seo": "🌐 SEO-Focused Optimization",
220
- # "competitive": "πŸ“Š Competitive Analysis",
221
- # "voice_search": "🎀 Voice Search Optimization",
222
- # "batch_optimize": "πŸ“¦ Batch Optimization",
223
- # "content_variations": "πŸ”„ Content Variations",
224
- "readability_analysis": "πŸ“– Readability Analysis",
225
- # "entity_extraction": "🏷️ Entity Extraction"
226
  }[x],
227
  index=0,
228
- help="Choose the type of optimization to apply to your content"
229
  )
230
 
231
  with col2:
232
  # Additional options based on optimization type
233
- if optimization_type in ["standard", "seo", "competitive", "readability_analysis"]:
234
  analyze_only = st.checkbox("Analysis only (no rewriting)", value=False)
235
- include_keywords = st.checkbox("Include keyword suggestions", value=True)
236
- # elif optimization_type == "batch_optimize":
237
- # st.info("For batch optimization, separate multiple content pieces with '---' in the text area above")
238
- # elif optimization_type == "content_variations":
239
- # num_variations = st.slider("Number of variations", min_value=1, max_value=5, value=3)
 
 
 
 
240
  else:
241
  analyze_only = False
242
- include_keywords = True
243
- # num_variations = 3
244
 
245
  # Show description based on optimization type
246
  optimization_descriptions = {
247
- "standard": "General content enhancement focusing on clarity, structure, and AI answerability.",
248
- "seo": "SEO-focused optimization for AI search engines with semantic keyword analysis.",
249
- # "competitive": "Competitive analysis against AI search best practices with gap identification.",
250
- # "voice_search": "Optimization for voice search and conversational AI systems.",
251
- # "batch_optimize": "Process multiple content pieces simultaneously.",
252
- # "content_variations": "Generate multiple optimized variations of the same content.",
253
- "readability_analysis": "Detailed readability analysis specifically for AI systems.",
254
- # "entity_extraction": "Extract key entities, topics, and concepts for optimization insights."
255
  }
256
 
257
  st.info(f"**{optimization_descriptions[optimization_type]}**")
258
 
 
 
 
 
 
 
259
  # Submit button
260
- if st.button("πŸš€ Process Content", key="enhancement_submit"):
261
  if not input_text.strip():
262
  st.warning("Please enter some content to analyze.")
263
  return
264
 
265
  try:
266
- with st.spinner(f"Processing content with {optimization_type} optimization..."):
267
- # Handle different optimization types
268
- if optimization_type == "standard":
269
- result = self.content_optimizer.optimize_content(
270
  input_text,
271
- analyze_only=analyze_only,
272
- include_keywords=include_keywords,
273
- optimization_type="standard"
274
  )
275
 
276
- elif optimization_type == "seo":
277
- result = self.content_optimizer.optimize_content(
278
  input_text,
279
- analyze_only=analyze_only,
280
- include_keywords=include_keywords,
281
- optimization_type="seo"
282
  )
283
 
284
- # elif optimization_type == "competitive":
285
- # result = self.content_optimizer.optimize_content(
286
- # input_text,
287
- # optimization_type="competitive"
288
- # )
289
-
290
- # elif optimization_type == "voice_search":
291
- # result = self.content_optimizer.optimize_for_voice_search(input_text)
292
 
293
- # elif optimization_type == "batch_optimize":
294
- # # Split content by '---' separator
295
- # content_pieces = [piece.strip() for piece in input_text.split('---') if piece.strip()]
296
- # if len(content_pieces) > 1:
297
- # result = self.content_optimizer.batch_optimize_content(content_pieces)
298
- # else:
299
- # st.warning("For batch optimization, please separate content pieces with '---'")
300
- # return
301
 
302
- # elif optimization_type == "content_variations":
303
- # result = self.content_optimizer.generate_content_variations(
304
- # input_text,
305
- # num_variations=num_variations
306
- # )
307
-
308
- elif optimization_type == "readability_analysis":
309
- result = self.content_optimizer.analyze_content_readability(input_text)
310
 
311
- # elif optimization_type == "entity_extraction":
312
- # result = self.content_optimizer.extract_key_entities(input_text)
 
 
 
 
 
 
313
 
314
- if result.get("error"):
 
 
 
 
 
 
 
315
  st.error(f"Processing failed: {result['error']}")
316
  return
 
 
317
 
318
  # Display results based on optimization type
319
- self.display_enhancement_results(result, optimization_type, input_text)
320
 
321
  except Exception as e:
322
  st.error(f"An error occurred: {str(e)}")
323
 
324
- def display_enhancement_results(self, result, optimization_type, original_text):
325
- """Display results based on optimization type"""
326
- st.success(f"{optimization_type.title()} optimization completed successfully!")
327
-
328
- # if optimization_type == "batch_optimize":
329
- # self.display_batch_results(result)
330
- # elif optimization_type == "content_variations":
331
- # self.display_variation_results(result)
332
- if optimization_type == "readability_analysis":
333
- self.display_readability_results(result)
334
- # elif optimization_type == "entity_extraction":
335
- # self.display_entity_results(result)
336
- # elif optimization_type == "voice_search":
337
- # self.display_voice_search_results(result)
338
  else:
339
- self.display_standard_results(result, optimization_type)
340
 
341
  # Export functionality
342
- self.display_export_options(result, optimization_type, original_text)
343
 
344
- def display_standard_results(self, result, optimization_type):
345
- """Display results for standard, SEO, and competitive optimizations"""
346
- st.markdown("### πŸ“Š Analysis Results")
347
-
348
- # Show scores if available
349
- scores = result.get("scores", {})
350
- if scores:
 
 
351
  col1, col2, col3 = st.columns(3)
 
 
 
 
 
 
 
 
 
 
 
352
 
 
 
353
  with col1:
354
- clarity = scores.get("clarity", 0)
355
- st.metric("Clarity", f"{clarity}/10")
356
 
357
  with col2:
358
- structure = scores.get("structuredness", 0)
359
- st.metric("Structure", f"{structure}/10")
360
 
361
  with col3:
362
- answerability = scores.get("answerability", 0)
363
- st.metric("Answerability", f"{answerability}/10")
364
-
365
- # Show SEO analysis if available
366
- if "seo_analysis" in result:
367
- st.markdown("#### 🌐 SEO Analysis")
368
- seo_data = result["seo_analysis"]
369
- if "readability_score" in seo_data:
370
- st.metric("Readability Score", f"{seo_data['readability_score']}/10")
371
- if "semantic_gaps" in seo_data:
372
- st.write("**Semantic Gaps:**", ", ".join(seo_data["semantic_gaps"]))
373
-
374
- # Show competitive analysis if available
375
- if "competitive_analysis" in result:
376
- st.markdown("#### πŸ“Š Competitive Analysis")
377
- comp_data = result["competitive_analysis"]
378
- for key, value in comp_data.items():
379
- if isinstance(value, list):
380
- st.write(f"**{key.replace('_', ' ').title()}:**", ", ".join(value))
381
- else:
382
- st.write(f"**{key.replace('_', ' ').title()}:**", value)
383
 
384
- # Show keywords
385
- keywords = result.get("keywords", [])
386
- if keywords:
387
- st.markdown("#### πŸ”‘ Key Terms")
388
- st.write(", ".join(keywords))
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
389
 
390
  # Show optimized content
391
- optimized_content = result.get("optimized_text") or result.get("optimized_content", {}).get("enhanced_content", "")
392
  if optimized_content:
393
- st.markdown("#### ✨ Optimized Content")
394
- st.text_area(
395
- "Enhanced version:",
396
- value=optimized_content,
397
- height=200,
398
- key="optimized_output"
399
- )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
400
 
401
  # Show recommendations
402
  recommendations = result.get("recommendations", [])
403
  if recommendations:
404
- st.markdown("#### πŸ’‘ Recommendations")
405
  for i, rec in enumerate(recommendations, 1):
406
  st.write(f"**{i}.** {rec}")
 
 
 
 
 
 
 
 
 
 
407
 
408
- def display_batch_results(self, results):
409
- """Display batch optimization results"""
410
- st.markdown("### πŸ“¦ Batch Processing Results")
411
 
412
  successful_results = [r for r in results if not r.get('error')]
413
  failed_results = [r for r in results if r.get('error')]
@@ -428,28 +467,29 @@ class GEOSEOApp:
428
  if result.get('error'):
429
  st.error(f"Processing failed: {result['error']}")
430
  else:
431
- # Show scores
432
- scores = result.get("scores", {})
433
- if scores:
434
  col1, col2, col3 = st.columns(3)
435
  with col1:
436
- st.metric("Clarity", f"{scores.get('clarity', 0)}/10")
437
  with col2:
438
- st.metric("Structure", f"{scores.get('structuredness', 0)}/10")
439
  with col3:
440
- st.metric("Answerability", f"{scores.get('answerability', 0)}/10")
441
 
442
  # Show optimized content if available
443
- optimized = result.get("optimized_text", "")
444
- if optimized:
445
- with st.expander("View optimized content"):
446
- st.text_area("", value=optimized, height=150, key=f"batch_output_{idx}")
 
447
 
448
  st.write("---")
449
 
450
- def display_variation_results(self, variations):
451
- """Display content variation results"""
452
- st.markdown("### πŸ”„ Content Variations")
453
 
454
  for i, variation in enumerate(variations):
455
  if variation.get('error'):
@@ -457,19 +497,26 @@ class GEOSEOApp:
457
  continue
458
 
459
  variation_type = variation.get('variation_type', f'Variation {i+1}')
460
- st.markdown(f"#### {variation_type.title()} Version")
461
 
462
- # Show variation details
463
- target_use_case = variation.get('target_use_case', '')
464
- if target_use_case:
465
- st.info(f"**Target Use Case:** {target_use_case}")
 
 
466
 
467
- # Show key changes
468
- key_changes = variation.get('key_changes', [])
469
- if key_changes:
470
- st.write("**Key Changes:**")
471
- for change in key_changes:
472
- st.write(f"β€’ {change}")
 
 
 
 
 
473
 
474
  # Show optimized content
475
  optimized_content = variation.get('optimized_content', '')
@@ -477,118 +524,95 @@ class GEOSEOApp:
477
  st.text_area(
478
  f"{variation_type} content:",
479
  value=optimized_content,
480
- height=150,
481
- key=f"variation_{i}"
482
  )
483
 
484
  st.write("---")
485
 
486
- def display_readability_results(self, result):
487
- """Display readability analysis results"""
488
- st.markdown("### πŸ“– Readability Analysis")
489
-
490
- # Basic metrics
491
- basic_metrics = result.get('basic_metrics', {})
492
- if basic_metrics:
493
- st.markdown("#### πŸ“Š Basic Metrics")
494
  col1, col2, col3, col4 = st.columns(4)
495
 
496
  with col1:
497
- st.metric("Total Words", basic_metrics.get('total_words', 0))
498
  with col2:
499
- st.metric("Sentences", basic_metrics.get('total_sentences', 0))
500
  with col3:
501
- st.metric("Paragraphs", basic_metrics.get('total_paragraphs', 0))
502
  with col4:
503
- st.metric("AI Readability", f"{result.get('ai_readability_score', 0)}/10")
 
 
 
 
 
 
 
 
 
 
 
 
504
 
505
- # Complexity indicators
506
- complexity = result.get('complexity_indicators', {})
507
- if complexity:
508
- st.markdown("#### 🎯 Complexity Analysis")
509
  col1, col2 = st.columns(2)
510
 
511
  with col1:
512
- st.metric("Long Sentences", f"{complexity.get('long_sentences_percentage', 0):.1f}%")
 
 
 
 
513
  with col2:
514
- st.metric("Complex Words", f"{complexity.get('complex_words_percentage', 0):.1f}%")
515
-
516
- # Recommendations
517
- recommendations = result.get('recommendations', [])
518
- if recommendations:
519
- st.markdown("#### πŸ’‘ Readability Recommendations")
520
- for i, rec in enumerate(recommendations, 1):
 
 
 
521
  st.write(f"**{i}.** {rec}")
522
 
523
- def display_entity_results(self, result):
524
- """Display entity extraction results"""
525
- st.markdown("### 🏷️ Entity Analysis")
526
-
527
- # Named entities
528
- named_entities = result.get('named_entities', [])
529
- if named_entities:
530
- st.markdown("#### πŸ‘₯ Named Entities")
531
- st.write(", ".join(named_entities))
532
-
533
- # Key topics
534
- key_topics = result.get('key_topics', [])
535
- if key_topics:
536
- st.markdown("#### πŸ“‹ Key Topics")
537
- st.write(", ".join(key_topics))
538
-
539
- # Technical terms
540
- technical_terms = result.get('technical_terms', [])
541
- if technical_terms:
542
- st.markdown("#### πŸ”§ Technical Terms")
543
- st.write(", ".join(technical_terms))
544
-
545
- # Semantic keywords
546
- semantic_keywords = result.get('semantic_keywords', [])
547
- if semantic_keywords:
548
- st.markdown("#### πŸ” Semantic Keywords")
549
- st.write(", ".join(semantic_keywords))
550
-
551
- # Question opportunities
552
- questions = result.get('question_opportunities', [])
553
- if questions:
554
- st.markdown("#### ❓ Question Opportunities")
555
- for q in questions:
556
- st.write(f"β€’ {q}")
557
 
558
- # def display_voice_search_results(self, result):
559
- # """Display voice search optimization results"""
560
- # st.markdown("### 🎀 Voice Search Optimization")
561
-
562
- # # Conversational score
563
- # conv_score = result.get('conversational_score', 0)
564
- # if conv_score:
565
- # st.metric("Conversational Score", f"{conv_score}/10")
566
-
567
- # # Question-answer pairs
568
- # qa_pairs = result.get('question_answer_pairs', [])
569
- # if qa_pairs:
570
- # st.markdown("#### ❓ Question-Answer Pairs")
571
- # for qa in qa_pairs:
572
- # st.write(f"**Q:** {qa.get('question', '')}")
573
- # st.write(f"**A:** {qa.get('answer', '')}")
574
- # st.write("---")
575
-
576
- # # Featured snippet candidates
577
- # snippets = result.get('featured_snippet_candidates', [])
578
- # if snippets:
579
- # st.markdown("#### 🌟 Featured Snippet Candidates")
580
- # for i, snippet in enumerate(snippets, 1):
581
- # st.write(f"**{i}.** {snippet}")
582
-
583
- # # Voice optimized content
584
- # voice_content = result.get('voice_optimized_content', '')
585
- # if voice_content:
586
- # st.markdown("#### 🎀 Voice-Optimized Content")
587
- # st.text_area("Conversational version:", value=voice_content, height=200, key="voice_output")
588
-
589
- def display_export_options(self, result, optimization_type, original_text):
590
- """Display export options for results"""
591
- st.markdown("### πŸ“₯ Export Results")
592
 
593
  # Prepare export data
594
  export_data = {
@@ -596,19 +620,85 @@ class GEOSEOApp:
596
  'optimization_type': optimization_type,
597
  'original_text': original_text,
598
  'original_word_count': len(original_text.split()),
599
- 'results': result
 
 
600
  }
601
 
602
  # Serialize data to JSON
603
- export_json = json.dumps(export_data, indent=2)
604
 
605
- # Add download button for direct download
606
  st.download_button(
607
- label="πŸ“₯ Download Analysis Report",
608
  data=export_json,
609
- file_name=f"{optimization_type}_analysis_{int(time.time())}.json",
610
  mime="application/json"
611
  )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
612
 
613
  def render_website_analysis_tab(self):
614
  """Render Website GEO Analysis tab"""
 
1
  """
2
+ Main Streamlit Application - GEO SEO AI Optimizer with RAG-Enhanced Content Optimization
3
  Entry point for the application with UI components
4
  """
5
 
 
8
  import tempfile
9
  import json
10
  from typing import Dict, Any, List
11
+ import time
12
 
13
  # Import our custom modules
14
  from utils.parser import PDFParser, TextParser, WebpageParser
15
  from utils.scorer import GEOScorer
16
+ from utils.optimizer import ContentOptimizer # This will be your enhanced version
17
  from utils.chunker import VectorChunker
18
  from utils.export import ResultExporter
19
 
 
46
  temperature=0.1
47
  )
48
 
 
49
  self.embeddings = HuggingFaceEmbeddings(
50
  model_name="sentence-transformers/all-MiniLM-L6-v2",
51
+ model_kwargs={'device': 'cpu'}
 
52
  )
53
 
54
  def setup_parsers(self):
 
58
  self.webpage_parser = WebpageParser()
59
 
60
  def setup_components(self):
61
+ """Initialize processing components with RAG integration"""
62
  self.geo_scorer = GEOScorer(self.llm)
 
63
  self.vector_chunker = VectorChunker(self.embeddings)
64
+
65
+ # Enhanced content optimizer with RAG capabilities
66
+ self.content_optimizer = ContentOptimizer(self.llm, self.vector_chunker)
67
+
68
  self.result_exporter = ResultExporter()
69
 
70
  def run(self):
 
76
  )
77
 
78
  st.title("πŸš€ GEO SEO AI Optimizer")
79
+ st.markdown("*Optimize your content for AI search engines and LLM systems with RAG-enhanced analysis*")
80
 
81
  # Sidebar
82
  self.render_sidebar()
 
84
  # Main tabs
85
  tab1, tab2, tab3 = st.tabs([
86
  "🌐 Website GEO Analysis",
87
+ "πŸ”§ GEO Content Enhancement",
88
  "πŸ“„ Document Q&A",
89
  ])
90
 
 
92
  self.render_website_analysis_tab()
93
 
94
  with tab2:
95
+ self.render_geo_content_enhancement_tab()
96
 
97
  with tab3:
98
  self.render_document_qa_tab()
 
101
  """Render sidebar with information and controls"""
102
  st.sidebar.title("πŸ› οΈ GEO Tools")
103
  st.sidebar.markdown("- πŸ“„ Document Q&A with RAG")
104
+ st.sidebar.markdown("- πŸ”§ RAG-Enhanced Content Optimization")
105
  st.sidebar.markdown("- 🌐 Website GEO Analysis")
106
  st.sidebar.markdown("- πŸ“Š AI-First SEO Scoring")
107
 
 
116
  st.sidebar.markdown("**Query Intent Matching**: How well content matches user queries")
117
  st.sidebar.markdown("**Conversational Readiness**: Suitability for AI chat responses")
118
  st.sidebar.markdown("**Citation Worthiness**: Probability of being cited by AI")
119
+ st.sidebar.markdown("**Context Completeness**: How self-contained the content is")
120
+ st.sidebar.markdown("**Semantic Richness**: Depth of topic coverage")
121
 
122
  st.sidebar.markdown("---")
123
+ st.sidebar.markdown("### 🧠 RAG Enhancement")
124
+ st.sidebar.markdown("- **Knowledge Base**: GEO best practices")
125
+ st.sidebar.markdown("- **Contextual Analysis**: AI-informed optimization")
126
+ st.sidebar.markdown("- **Entity Extraction**: AI-powered entity recognition")
127
+ st.sidebar.markdown("- **Competitive Analysis**: Gap identification")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
128
 
129
+ def render_geo_content_enhancement_tab(self):
130
+ """Render GEO Content Enhancement tab with RAG integration"""
131
+ st.header("πŸ”§ GEO Content Enhancement with RAG")
132
+ st.markdown("Analyze and optimize your content using AI-powered Generative Engine Optimization with RAG-enhanced knowledge base.")
133
 
134
  # Content input
135
  input_text = st.text_area(
136
  "Enter content to analyze and enhance:",
137
  height=200,
138
+ key="geo_enhancement_input",
139
+ help="Paste your content here for GEO optimization using RAG-enhanced analysis"
140
  )
141
 
142
+ # GEO Optimization type selector
143
+ st.markdown("### βš™οΈ GEO Optimization Settings")
144
  col1, col2 = st.columns(2)
145
 
146
  with col1:
147
  optimization_type = st.selectbox(
148
+ "Select GEO Optimization Type:",
149
  options=[
150
+ "geo_standard",
151
+ "competitive_geo",
152
+ "geo_readability",
153
+ "geo_entity_extraction",
154
+ "geo_variations",
155
+ "geo_batch_optimize"
 
 
156
  ],
157
  format_func=lambda x: {
158
+ "geo_standard": "πŸ”§ Standard GEO Enhancement",
159
+ "competitive_geo": "πŸ“Š Competitive GEO Analysis",
160
+ "geo_readability": "πŸ“– GEO Readability Analysis",
161
+ "geo_entity_extraction": "🏷️ GEO Entity Extraction",
162
+ "geo_variations": "πŸ”„ GEO Content Variations",
163
+ "geo_batch_optimize": "πŸ“¦ Batch GEO Optimization"
 
 
164
  }[x],
165
  index=0,
166
+ help="Choose the type of GEO optimization powered by RAG analysis"
167
  )
168
 
169
  with col2:
170
  # Additional options based on optimization type
171
+ if optimization_type in ["geo_standard", "competitive_geo"]:
172
  analyze_only = st.checkbox("Analysis only (no rewriting)", value=False)
173
+ include_rag_context = st.checkbox("Include RAG context details", value=True)
174
+ elif optimization_type == "geo_variations":
175
+ num_variations = st.slider("Number of variations", min_value=1, max_value=3, value=2)
176
+ analyze_only = False
177
+ include_rag_context = True
178
+ elif optimization_type == "geo_batch_optimize":
179
+ st.info("For batch optimization, separate multiple content pieces with '---' divider")
180
+ analyze_only = False
181
+ include_rag_context = True
182
  else:
183
  analyze_only = False
184
+ include_rag_context = True
 
185
 
186
  # Show description based on optimization type
187
  optimization_descriptions = {
188
+ "geo_standard": "πŸ”§ RAG-enhanced GEO optimization focusing on AI search visibility, conversational readiness, and citation worthiness using knowledge base guidance.",
189
+ "competitive_geo": "πŸ“Š Competitive GEO analysis against best practices with gap identification and actionable recommendations using RAG context.",
190
+ "geo_readability": "πŸ“– Detailed readability analysis specifically optimized for AI systems and LLM consumption patterns.",
191
+ "geo_entity_extraction": "🏷️ AI-powered extraction of key entities, topics, and concepts relevant for GEO optimization.",
192
+ "geo_variations": "πŸ”„ Generate multiple GEO-optimized variations (FAQ, conversational, authoritative) using RAG knowledge.",
193
+ "geo_batch_optimize": "πŸ“¦ Process multiple content pieces simultaneously with consistent GEO optimization."
 
 
194
  }
195
 
196
  st.info(f"**{optimization_descriptions[optimization_type]}**")
197
 
198
+ # Knowledge base status
199
+ if hasattr(self.content_optimizer, 'geo_knowledge'):
200
+ st.success(f"βœ… RAG Knowledge Base Loaded: {len(self.content_optimizer.geo_knowledge)} GEO best practice documents")
201
+ else:
202
+ st.warning("⚠️ RAG Knowledge Base not available - falling back to standard optimization")
203
+
204
  # Submit button
205
+ if st.button("πŸš€ Process Content with GEO+RAG", key="geo_enhancement_submit"):
206
  if not input_text.strip():
207
  st.warning("Please enter some content to analyze.")
208
  return
209
 
210
  try:
211
+ with st.spinner(f"Processing content with {optimization_type} using RAG-enhanced GEO analysis..."):
212
+ # Handle different GEO optimization types
213
+ if optimization_type == "geo_standard":
214
+ result = self.content_optimizer.optimize_content_with_rag(
215
  input_text,
216
+ optimization_type="geo_standard",
217
+ analyze_only=analyze_only
 
218
  )
219
 
220
+ elif optimization_type == "competitive_geo":
221
+ result = self.content_optimizer.optimize_content_with_rag(
222
  input_text,
223
+ optimization_type="competitive_geo",
224
+ analyze_only=analyze_only
 
225
  )
226
 
227
+ elif optimization_type == "geo_readability":
228
+ result = self.content_optimizer.analyze_geo_readability(input_text)
 
 
 
 
 
 
229
 
230
+ elif optimization_type == "geo_entity_extraction":
231
+ result = self.content_optimizer.extract_geo_entities(input_text)
 
 
 
 
 
 
232
 
233
+ elif optimization_type == "geo_variations":
234
+ result = self.content_optimizer.generate_geo_variations(
235
+ input_text,
236
+ num_variations=num_variations
237
+ )
 
 
 
238
 
239
+ elif optimization_type == "geo_batch_optimize":
240
+ # Split content by '---' separator
241
+ content_pieces = [piece.strip() for piece in input_text.split('---') if piece.strip()]
242
+ if len(content_pieces) > 1:
243
+ result = self.content_optimizer.batch_optimize_with_rag(content_pieces)
244
+ else:
245
+ st.warning("For batch optimization, please separate content pieces with '---'")
246
+ return
247
 
248
+ if isinstance(result, list):
249
+ # Handle list results (variations, batch)
250
+ if any(r.get("error") for r in result):
251
+ failed_results = [r for r in result if r.get("error")]
252
+ st.error(f"Some processing failed: {len(failed_results)} out of {len(result)} items")
253
+ else:
254
+ st.success("All content processed successfully!")
255
+ elif result.get("error"):
256
  st.error(f"Processing failed: {result['error']}")
257
  return
258
+ else:
259
+ st.success(f"{optimization_type.replace('_', ' ').title()} completed successfully!")
260
 
261
  # Display results based on optimization type
262
+ self.display_geo_enhancement_results(result, optimization_type, input_text, include_rag_context)
263
 
264
  except Exception as e:
265
  st.error(f"An error occurred: {str(e)}")
266
 
267
+ def display_geo_enhancement_results(self, result, optimization_type, original_text, include_rag_context=True):
268
+ """Display results based on GEO optimization type"""
269
+
270
+ if optimization_type == "geo_batch_optimize":
271
+ self.display_geo_batch_results(result)
272
+ elif optimization_type == "geo_variations":
273
+ self.display_geo_variation_results(result)
274
+ elif optimization_type == "geo_readability":
275
+ self.display_geo_readability_results(result)
276
+ elif optimization_type == "geo_entity_extraction":
277
+ self.display_geo_entity_results(result)
 
 
 
278
  else:
279
+ self.display_standard_geo_results(result, optimization_type, include_rag_context)
280
 
281
  # Export functionality
282
+ self.display_geo_export_options(result, optimization_type, original_text)
283
 
284
+ def display_standard_geo_results(self, result, optimization_type, include_rag_context):
285
+ """Display results for standard and competitive GEO optimizations"""
286
+ st.markdown("### πŸ“Š GEO Analysis Results")
287
+
288
+ # Show GEO scores if available
289
+ geo_analysis = result.get("geo_analysis", {})
290
+ if geo_analysis:
291
+ st.markdown("#### 🎯 GEO Performance Metrics")
292
+
293
  col1, col2, col3 = st.columns(3)
294
+ with col1:
295
+ current_score = geo_analysis.get("current_geo_score", 0)
296
+ st.metric("Overall GEO Score", f"{current_score}/10")
297
+
298
+ with col2:
299
+ ai_visibility = geo_analysis.get("ai_search_visibility", 0)
300
+ st.metric("AI Search Visibility", f"{ai_visibility}/10")
301
+
302
+ with col3:
303
+ citation_worthy = geo_analysis.get("citation_worthiness", 0)
304
+ st.metric("Citation Worthiness", f"{citation_worthy}/10")
305
 
306
+ # Second row of metrics
307
+ col1, col2, col3 = st.columns(3)
308
  with col1:
309
+ query_matching = geo_analysis.get("query_intent_matching", 0)
310
+ st.metric("Query Intent Match", f"{query_matching}/10")
311
 
312
  with col2:
313
+ conversational = geo_analysis.get("conversational_readiness", 0)
314
+ st.metric("Conversational Ready", f"{conversational}/10")
315
 
316
  with col3:
317
+ context_complete = geo_analysis.get("context_completeness", 0)
318
+ st.metric("Context Complete", f"{context_complete}/10")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
319
 
320
+ # Show optimization opportunities
321
+ opportunities = result.get("optimization_opportunities", [])
322
+ if opportunities:
323
+ st.markdown("#### πŸš€ Optimization Opportunities")
324
+
325
+ high_priority = [opp for opp in opportunities if opp.get('priority') == 'high']
326
+ medium_priority = [opp for opp in opportunities if opp.get('priority') == 'medium']
327
+
328
+ if high_priority:
329
+ st.markdown("##### πŸ”΄ High Priority")
330
+ for opp in high_priority:
331
+ st.write(f"**{opp.get('type', 'Optimization')}**: {opp.get('description', '')}")
332
+ if opp.get('expected_impact'):
333
+ st.write(f"*Expected Impact: {opp.get('expected_impact')}*")
334
+ st.write("---")
335
+
336
+ if medium_priority:
337
+ st.markdown("##### 🟑 Medium Priority")
338
+ for opp in medium_priority:
339
+ st.write(f"**{opp.get('type', 'Optimization')}**: {opp.get('description', '')}")
340
+ if opp.get('expected_impact'):
341
+ st.write(f"*Expected Impact: {opp.get('expected_impact')}*")
342
+ st.write("---")
343
+
344
+ # Show GEO keywords and entities
345
+ geo_keywords = result.get("geo_keywords", {})
346
+ if geo_keywords:
347
+ st.markdown("#### πŸ”‘ GEO Keywords & Entities")
348
+
349
+ col1, col2 = st.columns(2)
350
+ with col1:
351
+ primary_entities = geo_keywords.get("primary_entities", [])
352
+ if primary_entities:
353
+ st.write("**Primary Entities:**")
354
+ st.write(", ".join(primary_entities))
355
+
356
+ semantic_terms = geo_keywords.get("semantic_terms", [])
357
+ if semantic_terms:
358
+ st.write("**Semantic Terms:**")
359
+ st.write(", ".join(semantic_terms))
360
+
361
+ with col2:
362
+ question_patterns = geo_keywords.get("question_patterns", [])
363
+ if question_patterns:
364
+ st.write("**Question Patterns:**")
365
+ for q in question_patterns:
366
+ st.write(f"β€’ {q}")
367
+
368
+ related_concepts = geo_keywords.get("related_concepts", [])
369
+ if related_concepts:
370
+ st.write("**Related Concepts:**")
371
+ st.write(", ".join(related_concepts))
372
 
373
  # Show optimized content
374
+ optimized_content = result.get("optimized_content", {})
375
  if optimized_content:
376
+ enhanced_text = optimized_content.get("enhanced_text", "")
377
+ if enhanced_text:
378
+ st.markdown("#### ✨ GEO-Optimized Content")
379
+ st.text_area(
380
+ "Enhanced version:",
381
+ value=enhanced_text,
382
+ height=250,
383
+ key="geo_optimized_output"
384
+ )
385
+
386
+ # Show structural improvements
387
+ structural_improvements = optimized_content.get("structural_improvements", [])
388
+ if structural_improvements:
389
+ st.markdown("**Structural Improvements:**")
390
+ for improvement in structural_improvements:
391
+ st.write(f"β€’ {improvement}")
392
+
393
+ # Show semantic enhancements
394
+ semantic_enhancements = optimized_content.get("semantic_enhancements", [])
395
+ if semantic_enhancements:
396
+ st.markdown("**Semantic Enhancements:**")
397
+ for enhancement in semantic_enhancements:
398
+ st.write(f"β€’ {enhancement}")
399
+
400
+ # Show competitive analysis if available
401
+ if "competitive_gaps" in result:
402
+ st.markdown("#### πŸ“Š Competitive GEO Analysis")
403
+ competitive_gaps = result["competitive_gaps"]
404
+
405
+ col1, col2 = st.columns(2)
406
+ with col1:
407
+ missing_questions = competitive_gaps.get("missing_question_patterns", [])
408
+ if missing_questions:
409
+ st.write("**Missing Question Patterns:**")
410
+ for q in missing_questions:
411
+ st.write(f"β€’ {q}")
412
+
413
+ entity_gaps = competitive_gaps.get("entity_gaps", [])
414
+ if entity_gaps:
415
+ st.write("**Entity Gaps:**")
416
+ st.write(", ".join(entity_gaps))
417
+
418
+ with col2:
419
+ semantic_opportunities = competitive_gaps.get("semantic_opportunities", [])
420
+ if semantic_opportunities:
421
+ st.write("**Semantic Opportunities:**")
422
+ st.write(", ".join(semantic_opportunities))
423
+
424
+ structural_weaknesses = competitive_gaps.get("structural_weaknesses", [])
425
+ if structural_weaknesses:
426
+ st.write("**Structural Weaknesses:**")
427
+ for weakness in structural_weaknesses:
428
+ st.write(f"β€’ {weakness}")
429
 
430
  # Show recommendations
431
  recommendations = result.get("recommendations", [])
432
  if recommendations:
433
+ st.markdown("#### πŸ’‘ GEO Recommendations")
434
  for i, rec in enumerate(recommendations, 1):
435
  st.write(f"**{i}.** {rec}")
436
+
437
+ # RAG context information
438
+ if include_rag_context and result.get("rag_enhanced"):
439
+ with st.expander("🧠 RAG Enhancement Details"):
440
+ st.write("**RAG Status:** βœ… Knowledge base successfully applied")
441
+ st.write(f"**Knowledge Sources:** {result.get('knowledge_sources', 'Multiple')} GEO best practice documents")
442
+ st.write(f"**Enhancement Type:** {result.get('optimization_type', 'Standard')}")
443
+
444
+ if result.get('parsing_error'):
445
+ st.warning(f"**Parsing Note:** {result['parsing_error']}")
446
 
447
+ def display_geo_batch_results(self, results):
448
+ """Display batch GEO optimization results"""
449
+ st.markdown("### πŸ“¦ Batch GEO Processing Results")
450
 
451
  successful_results = [r for r in results if not r.get('error')]
452
  failed_results = [r for r in results if r.get('error')]
 
467
  if result.get('error'):
468
  st.error(f"Processing failed: {result['error']}")
469
  else:
470
+ # Show GEO scores
471
+ geo_analysis = result.get("geo_analysis", {})
472
+ if geo_analysis:
473
  col1, col2, col3 = st.columns(3)
474
  with col1:
475
+ st.metric("GEO Score", f"{geo_analysis.get('current_geo_score', 0):.1f}")
476
  with col2:
477
+ st.metric("AI Visibility", f"{geo_analysis.get('ai_search_visibility', 0):.1f}")
478
  with col3:
479
+ st.metric("Citation Worthy", f"{geo_analysis.get('citation_worthiness', 0):.1f}")
480
 
481
  # Show optimized content if available
482
+ optimized_content = result.get("optimized_content", {})
483
+ enhanced_text = optimized_content.get("enhanced_text", "")
484
+ if enhanced_text:
485
+ with st.expander("View GEO-optimized content"):
486
+ st.text_area("", value=enhanced_text[:500] + "...", height=150, key=f"batch_geo_output_{idx}")
487
 
488
  st.write("---")
489
 
490
+ def display_geo_variation_results(self, variations):
491
+ """Display GEO content variation results"""
492
+ st.markdown("### πŸ”„ GEO Content Variations")
493
 
494
  for i, variation in enumerate(variations):
495
  if variation.get('error'):
 
497
  continue
498
 
499
  variation_type = variation.get('variation_type', f'Variation {i+1}')
500
+ st.markdown(f"#### {variation_type.replace('_', ' ').title()} Version")
501
 
502
+ # Show GEO improvements
503
+ geo_improvements = variation.get('geo_improvements', [])
504
+ if geo_improvements:
505
+ st.write("**GEO Improvements:**")
506
+ for improvement in geo_improvements:
507
+ st.write(f"β€’ {improvement}")
508
 
509
+ # Show target AI systems
510
+ target_ai_systems = variation.get('target_ai_systems', [])
511
+ if target_ai_systems:
512
+ st.write(f"**Optimized For:** {', '.join(target_ai_systems)}")
513
+
514
+ # Show expected benefits
515
+ expected_benefits = variation.get('expected_geo_benefits', [])
516
+ if expected_benefits:
517
+ st.write("**Expected GEO Benefits:**")
518
+ for benefit in expected_benefits:
519
+ st.write(f"β€’ {benefit}")
520
 
521
  # Show optimized content
522
  optimized_content = variation.get('optimized_content', '')
 
524
  st.text_area(
525
  f"{variation_type} content:",
526
  value=optimized_content,
527
+ height=200,
528
+ key=f"geo_variation_{i}"
529
  )
530
 
531
  st.write("---")
532
 
533
+ def display_geo_readability_results(self, result):
534
+ """Display GEO readability analysis results"""
535
+ st.markdown("### πŸ“– GEO Readability Analysis")
536
+
537
+ # Basic GEO metrics
538
+ geo_metrics = result.get('geo_readability_metrics', {})
539
+ if geo_metrics:
540
+ st.markdown("#### πŸ“Š GEO Content Metrics")
541
  col1, col2, col3, col4 = st.columns(4)
542
 
543
  with col1:
544
+ st.metric("Total Words", geo_metrics.get('total_words', 0))
545
  with col2:
546
+ st.metric("Questions", geo_metrics.get('questions_count', 0))
547
  with col3:
548
+ st.metric("Headings", geo_metrics.get('headings_count', 0))
549
  with col4:
550
+ st.metric("Lists", geo_metrics.get('lists_count', 0))
551
+
552
+ # Second row
553
+ col1, col2, col3, col4 = st.columns(4)
554
+ with col1:
555
+ st.metric("Entity Mentions", geo_metrics.get('entity_mentions', 0))
556
+ with col2:
557
+ st.metric("Data Points", geo_metrics.get('numeric_data_points', 0))
558
+ with col3:
559
+ st.metric("Paragraphs", geo_metrics.get('total_paragraphs', 0))
560
+ with col4:
561
+ geo_score = result.get('geo_readability_score', 0)
562
+ st.metric("GEO Readability", f"{geo_score}/10")
563
 
564
+ # AI optimization indicators
565
+ ai_indicators = result.get('ai_optimization_indicators', {})
566
+ if ai_indicators:
567
+ st.markdown("#### πŸ€– AI Optimization Indicators")
568
  col1, col2 = st.columns(2)
569
 
570
  with col1:
571
+ question_ratio = ai_indicators.get('question_ratio', 0)
572
+ st.metric("Question Ratio", f"{question_ratio:.2%}")
573
+ structure_score = ai_indicators.get('structure_score', 0)
574
+ st.metric("Structure Score", f"{structure_score:.1f}/10")
575
+
576
  with col2:
577
+ entity_density = ai_indicators.get('entity_density', 0)
578
+ st.metric("Entity Density", f"{entity_density:.2%}")
579
+ data_richness = ai_indicators.get('data_richness', 0)
580
+ st.metric("Data Richness", f"{data_richness:.2%}")
581
+
582
+ # GEO recommendations
583
+ geo_recommendations = result.get('geo_recommendations', [])
584
+ if geo_recommendations:
585
+ st.markdown("#### πŸ’‘ GEO Optimization Recommendations")
586
+ for i, rec in enumerate(geo_recommendations, 1):
587
  st.write(f"**{i}.** {rec}")
588
 
589
+ def display_geo_entity_results(self, result):
590
+ """Display GEO entity extraction results"""
591
+ st.markdown("### 🏷️ GEO Entity Analysis")
592
+
593
+ if result.get('error'):
594
+ st.error(f"Entity extraction failed: {result['error']}")
595
+ return
596
+
597
+ geo_entities = result.get('geo_entities', {})
598
+ if geo_entities:
599
+ # Display extracted entities
600
+ for entity_type, entity_data in geo_entities.items():
601
+ if entity_data:
602
+ st.markdown(f"#### {entity_type.replace('_', ' ').title()}")
603
+ st.write(entity_data)
604
+ st.write("---")
605
+
606
+ # Extraction metadata
607
+ extraction_success = result.get('extraction_success', False)
608
+ if extraction_success:
609
+ st.success("βœ… Entity extraction completed successfully")
610
+ st.write(f"**Content Length:** {result.get('content_length', 0)} characters")
611
+ st.write(f"**Extraction Method:** {result.get('extraction_method', 'Unknown')}")
 
 
 
 
 
 
 
 
 
 
 
612
 
613
+ def display_geo_export_options(self, result, optimization_type, original_text):
614
+ """Display export options for GEO results"""
615
+ st.markdown("### πŸ“₯ Export GEO Results")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
616
 
617
  # Prepare export data
618
  export_data = {
 
620
  'optimization_type': optimization_type,
621
  'original_text': original_text,
622
  'original_word_count': len(original_text.split()),
623
+ 'geo_results': result,
624
+ 'rag_enhanced': result.get('rag_enhanced', False) if not isinstance(result, list) else any(r.get('rag_enhanced', False) for r in result),
625
+ 'knowledge_sources': result.get('knowledge_sources', 0) if not isinstance(result, list) else 'multiple'
626
  }
627
 
628
  # Serialize data to JSON
629
+ export_json = json.dumps(export_data, indent=2, default=str)
630
 
631
+ # Add download button
632
  st.download_button(
633
+ label="πŸ“₯ Download GEO Analysis Report",
634
  data=export_json,
635
+ file_name=f"geo_{optimization_type}_analysis_{int(time.time())}.json",
636
  mime="application/json"
637
  )
638
+
639
+ # Keep existing methods for other tabs (render_document_qa_tab, render_website_analysis_tab, etc.)
640
+ # ... (rest of the methods remain the same as in your original code)
641
+
642
+ def render_document_qa_tab(self):
643
+ """Render Document Q&A tab"""
644
+ st.header("πŸ“„ Document Question Answering")
645
+ st.markdown("Upload documents or paste text to ask questions using RAG.")
646
+
647
+ # File upload
648
+ uploaded_file = st.file_uploader("Upload a PDF file", type=["pdf"])
649
+
650
+ # Text input
651
+ pasted_text = st.text_area("Or paste text directly:", height=150)
652
+
653
+ # Question input
654
+ user_query = st.text_input("Ask a question about the content:")
655
+
656
+ # Submit button
657
+ if st.button("πŸ” Ask Question", key="qa_submit"):
658
+ if not user_query.strip():
659
+ st.warning("Please enter a question.")
660
+ return
661
+
662
+ try:
663
+ # Parse content
664
+ documents = []
665
+
666
+ if uploaded_file:
667
+ with st.spinner("Processing PDF..."):
668
+ # Save uploaded file temporarily
669
+ temp_path = self.save_uploaded_file(uploaded_file)
670
+ documents = self.pdf_parser.parse(temp_path)
671
+ os.unlink(temp_path) # Clean up
672
+
673
+ elif pasted_text.strip():
674
+ with st.spinner("Processing text..."):
675
+ documents = self.text_parser.parse(pasted_text)
676
+
677
+ else:
678
+ st.warning("Please upload a PDF or paste some text.")
679
+ return
680
+
681
+ # Create vector store and answer question
682
+ with st.spinner("Creating embeddings and searching..."):
683
+ qa_chain = self.vector_chunker.create_qa_chain(documents, self.llm)
684
+ result = qa_chain({"query": user_query})
685
+
686
+ # Display results
687
+ st.markdown("### πŸ’¬ Answer")
688
+ st.write(result["result"])
689
+
690
+ # Show sources
691
+ with st.expander("πŸ“„ Source Documents"):
692
+ for i, doc in enumerate(result.get("source_documents", [])):
693
+ st.write(f"**Source {i+1}:**")
694
+ content = doc.page_content
695
+ st.write(content[:500] + "..." if len(content) > 500 else content)
696
+ if hasattr(doc, 'metadata') and doc.metadata:
697
+ st.write(f"*Metadata: {doc.metadata}*")
698
+ st.write("---")
699
+
700
+ except Exception as e:
701
+ st.error(f"An error occurred: {str(e)}")
702
 
703
  def render_website_analysis_tab(self):
704
  """Render Website GEO Analysis tab"""