Alpha108/GenerativeEngineOptimization
0
1"""2Main Streamlit Application - GEO SEO AI Optimizer3Entry point for the application with UI components4"""5 6import streamlit as st7import os8import tempfile9import json10from typing import Dict, Any, List11 12# Import our custom modules13from utils.parser import PDFParser, TextParser, WebpageParser14from utils.scorer import GEOScorer15from utils.optimizer import ContentOptimizer16from utils.chunker import VectorChunker17from utils.export import ResultExporter18from utils.lang_utils import detect_language, translate_text19from rag_utils import create_vectorstore_from_text, create_rag_chain20 21# Import LangChain components22from langchain_groq import ChatGroq23from langchain_community.embeddings import HuggingFaceEmbeddings24 25from langdetect import detect26from deep_translator import GoogleTranslator27def detect_and_translate_to_english(text: str) -> str:28 try:29 lang = detect(text)30 if lang != "en":31 st.warning(f"Detected Language: {lang}. Translating to English...")32 translated_text = GoogleTranslator(source='auto', target='en').translate(text)33 return translated_text34 else:35 return text36 except Exception as e:37 st.error(f"Translation failed: {e}")38 return text39 40 41# Assume `translated_content` is your PDF or webpage content in text format (after translation)42 43 44class GEOSEOApp:45 """Main application class that orchestrates all components"""46 47 48 def __init__(self):49 self.setup_config()50 self.setup_models()51 self.setup_parsers()52 self.setup_components()53 54 55 def setup_config(self):56 """Initialize configuration and API keys"""57 self.groq_api_key = os.getenv("GROQ_API_KEY", "your-groq-api-key")58 self.hf_api_key = os.getenv("HUGGINGFACE_API_KEY", "your-huggingface-api-key")59 60 # Create data directory if it doesn't exist61 os.makedirs("data/uploaded_files", exist_ok=True)62 63 def setup_models(self):64 """Initialize LLM and embedding models"""65 self.llm = ChatGroq(66 api_key=self.groq_api_key,67 model_name="llama3-8b-8192",68 temperature=0.169 )70 71 self.embeddings = HuggingFaceEmbeddings(72 model_name="sentence-transformers/all-MiniLM-L6-v2",73 model_kwargs={"device": "cpu"},74 cache_folder="./hf_cache",75 )76 77 def setup_parsers(self):78 """Initialize content parsers"""79 self.pdf_parser = PDFParser()80 self.text_parser = TextParser()81 self.webpage_parser = WebpageParser()82 83 def setup_components(self):84 """Initialize processing components"""85 self.geo_scorer = GEOScorer(self.llm)86 self.content_optimizer = ContentOptimizer(self.llm)87 self.vector_chunker = VectorChunker(self.embeddings)88 self.result_exporter = ResultExporter()89 90 def run(self):91 """Main application runner"""92 st.set_page_config(93 page_title="GEO SEO AI Optimizer", 94 page_icon="๐", 95 layout="wide"96 )97 98 st.title("๐ GEO SEO AI Optimizer")99 st.markdown("*Optimize your content for AI search engines and LLM systems*")100 101 # Sidebar102 self.render_sidebar()103 104 # Main tabs105 tab1, tab2, tab3,tab4 = st.tabs([106 "๐ Website GEO Analysis",107 "๐ง Content Enhancement", 108 "๐ Document Q&A", 109 "๐ Translation"110 ])111 112 with tab1:113 self.render_website_analysis_tab()114 115 with tab2:116 self.render_content_enhancement_tab()117 118 with tab3:119 self.render_document_qa_tab()120 with tab4:121 self.render_multilingual_tab()122 123 def render_sidebar(self):124 """Render sidebar with information and controls"""125 st.sidebar.title("๐ ๏ธ GEO Tools")126 st.sidebar.markdown("- ๐ Document Q&A with RAG")127 st.sidebar.markdown("- ๐ง Content Enhancement")128 st.sidebar.markdown("- ๐ Website GEO Analysis")129 st.sidebar.markdown("- ๐ AI-First SEO Scoring")130 131 st.sidebar.markdown("---")132 st.sidebar.markdown("### ๐ง Configuration")133 st.sidebar.markdown("Set your API keys:")134 st.sidebar.code("export GROQ_API_KEY='your-key'")135 136 st.sidebar.markdown("---")137 st.sidebar.markdown("### ๐ GEO Metrics")138 st.sidebar.markdown("**AI Search Visibility**: How likely AI engines will surface your content")139 st.sidebar.markdown("**Query Intent Matching**: How well content matches user queries")140 st.sidebar.markdown("**Conversational Readiness**: Suitability for AI chat responses")141 st.sidebar.markdown("**Citation Worthiness**: Probability of being cited by AI")142 143 st.sidebar.markdown("---")144 st.sidebar.markdown("### โน๏ธ Components")145 st.sidebar.markdown("- **Parser**: Extract content from various sources")146 st.sidebar.markdown("- **Scorer**: Analyze GEO performance")147 st.sidebar.markdown("- **Optimizer**: Enhance content for AI")148 st.sidebar.markdown("- **Chunker**: Create vector embeddings")149 st.sidebar.markdown("- **Exporter**: Generate reports")150 151 def render_document_qa_tab(self):152 """Render Document Q&A tab"""153 st.header("๐ Document Question Answering")154 st.markdown("Upload documents or paste text to ask questions using RAG.")155 156 # File upload157 uploaded_file = st.file_uploader("Upload a PDF file", type=["pdf"])158 159 # Text input160 pasted_text = st.text_area("Or paste text directly:", height=150)161 162 # Question input163 user_query = st.text_input("Ask a question about the content:")164 165 # Submit button166 if st.button("๐ Ask Question", key="qa_submit"):167 if not user_query.strip():168 st.warning("Please enter a question.")169 return170 171 try:172 # Parse content173 documents = []174 175 if uploaded_file:176 with st.spinner("Processing PDF..."):177 temp_path = self.save_uploaded_file(uploaded_file)178 documents = self.pdf_parser.parse(temp_path)179 os.unlink(temp_path)180 181 # ๐ง Translate each document if needed182 for doc in documents:183 doc.page_content = detect_and_translate_to_english(doc.page_content)184 185 186 elif pasted_text.strip():187 with st.spinner("Processing text..."):188 translated_text = detect_and_translate_to_english(pasted_text)189 documents = self.text_parser.parse(translated_text)190 191 192 else:193 st.warning("Please upload a PDF or paste some text.")194 return195 196 # Create vector store and answer question197 with st.spinner("Creating embeddings and searching..."):198 # Create new vectorstore and update RAG199 vectorstore = create_vectorstore_from_text(documents, self.embeddings)200 st.session_state.rag_chain = create_rag_chain(self.llm, vectorstore)201 202 result = st.session_state.rag_chain.invoke({"query": user_query})203 204 205 # Display results206 st.markdown("### ๐ฌ Answer")207 st.write(result["result"])208 209 # Show sources210 with st.expander("๐ Source Documents"):211 for i, doc in enumerate(result.get("source_documents", [])):212 st.write(f"**Source {i+1}:**")213 content = doc.page_content214 st.write(content[:500] + "..." if len(content) > 500 else content)215 if hasattr(doc, 'metadata') and doc.metadata:216 st.write(f"*Metadata: {doc.metadata}*")217 st.write("---")218 219 except Exception as e:220 st.error(f"An error occurred: {str(e)}")221 222 def render_content_enhancement_tab(self):223 """Render Content Enhancement tab"""224 st.header("๐ง Content Enhancement")225 st.markdown("Analyze and optimize your content for better AI/LLM performance.")226 227 # Content input228 input_text = st.text_area(229 "Enter content to analyze and enhance:", 230 height=200, 231 key="enhancement_input"232 )233 234 # Analysis options235 col1, col2 = st.columns(2)236 with col1:237 analyze_only = st.checkbox("Analysis only (no rewriting)", value=False)238 with col2:239 include_keywords = st.checkbox("Include keyword suggestions", value=True)240 241 # Submit button242 if st.button("๐ง Analyze & Enhance", key="enhancement_submit"):243 if not input_text.strip():244 st.warning("Please enter some content to analyze.")245 return246 247 try:248 with st.spinner("Analyzing content..."):249 # Run content analysis and optimization250 result = self.content_optimizer.optimize_content(251 input_text,252 analyze_only=analyze_only,253 include_keywords=include_keywords254 )255 256 if result.get("error"):257 st.error(f"Analysis failed: {result['error']}")258 return259 260 # Display results261 if analyze_only:262 st.success("Content analysis completed successfully!") 263 st.markdown("### ๐ Analysis Results")264 265 # Show scores266 scores = result.get("scores", {})267 if scores:268 col1, col2, col3 = st.columns(3)269 270 with col1:271 clarity = scores.get("clarity", 0)272 st.metric("Clarity", f"{clarity}/10")273 274 with col2:275 structure = scores.get("structuredness", 0)276 st.metric("Structure", f"{structure}/10")277 278 with col3:279 answerability = scores.get("answerability", 0)280 st.metric("Answerability", f"{answerability}/10")281 282 # Show keywords283 keywords = result.get("keywords", [])284 if keywords:285 st.markdown("#### ๐ Key Terms")286 st.write(", ".join(keywords))287 288 # Show optimized content289 optimized_text = result.get("optimized_text", "")290 if optimized_text:291 st.markdown("#### โจ Optimized Content")292 st.text_area(293 "Enhanced version:", 294 value=optimized_text, 295 height=200, 296 key="optimized_output"297 ) 298 299 # โ
Optional RAG-based Q&A on the analyzed content300 st.markdown("### ๐ฌ Ask a question about the analyzed content:")301 user_query = st.text_input("Enter your question:", key="enhancement_q")302 303 if user_query:304 from langchain.docstore.document import Document305 new_doc = Document(page_content=optimized_text or input_text)306 vectorstore = create_vectorstore_from_text([new_doc], self.embeddings)307 st.session_state.rag_chain = create_rag_chain(self.llm, vectorstore)308 309 result = st.session_state.rag_chain.invoke({"query": user_query})310 st.success("Answer:")311 st.write(result["result"])312 313 # Export option314 if st.button("๐ฅ Export Results"):315 export_data = self.result_exporter.export_enhancement_results(result)316 st.download_button(317 label="Download Analysis Report",318 data=json.dumps(export_data, indent=2),319 file_name=f"content_analysis_{int(time.time())}.json",320 mime="application/json"321 )322 323 except Exception as e:324 st.error(f"An error occurred: {str(e)}")325 326 327 def render_website_analysis_tab(self):328 """Render Website GEO Analysis tab"""329 st.header("๐ Website GEO Analysis")330 st.markdown("Analyze websites for Generative Engine Optimization (GEO) performance.")331 332 # URL input333 col1, col2 = st.columns([3, 1])334 with col1:335 website_url = st.text_input("Enter website URL:", placeholder="https://example.com")336 with col2:337 max_pages = st.selectbox("Pages to analyze:", [1, 3, 5], index=0)338 339 # Analysis options340 col1, col2 = st.columns(2)341 with col1:342 include_subpages = st.checkbox("Include subpages", value=False)343 with col2:344 detailed_analysis = st.checkbox("Detailed analysis", value=True)345 346 # Submit button347 if st.button("๐ Analyze Website", key="website_analyze"):348 if not website_url.strip():349 st.warning("Please enter a website URL.")350 return351 352 try:353 # Normalize URL354 if not website_url.startswith(('http://', 'https://')):355 website_url = 'https://' + website_url356 357 with st.spinner(f"Analyzing website: {website_url}"):358 # Parse website content359 pages_data = self.webpage_parser.parse_website(360 website_url, 361 max_pages=max_pages,362 include_subpages=include_subpages363 )364 if not pages_data:365 st.error("Could not extract content from the website.")366 return367 368 st.success(f"Successfully extracted content from {len(pages_data)} page(s)")369 370 # Analyze GEO scores371 with st.spinner("Calculating GEO scores..."):372 geo_results = []373 for i, page_data in enumerate(pages_data):374 with st.spinner(f"Analyzing page {i+1}/{len(pages_data)}..."):375 analysis = self.geo_scorer.analyze_page_geo(376 page_data['content'],377 page_data['title'],378 detailed=detailed_analysis379 )380 381 if not analysis.get('error'):382 analysis['page_data'] = page_data383 geo_results.append(analysis)384 else:385 st.warning(f"Could not analyze page {i+1}: {analysis['error']}")386 387 if not geo_results:388 st.error("Could not analyze any pages from the website.")389 return390 391 # Combine all page content for RAG392 combined_content = "\n\n".join([page['content'] for page in pages_data])393 from langchain.docstore.document import Document394 doc = Document(page_content=combined_content)395 396 vectorstore = create_vectorstore_from_text([doc], self.embeddings)397 st.session_state.rag_chain = create_rag_chain(self.llm, vectorstore)398 399 # RAG-based Q&A400 st.markdown("### ๐ฌ Ask a question about the website:")401 user_query = st.text_input("Ask here:", key="website_q")402 403 if user_query:404 result = st.session_state.rag_chain.invoke({"query": user_query})405 st.success("Answer:")406 st.write(result["result"])407 408 # Display results409 self.display_geo_results(geo_results, website_url)410 411 # Export functionality412 st.markdown("### ๐ฅ Export Results")413 if st.button("๐ Generate Full Report"):414 report_data = self.result_exporter.export_geo_results(415 geo_results, 416 website_url417 )418 st.download_button(419 label="Download GEO Report",420 data=json.dumps(report_data, indent=2),421 file_name=f"geo_analysis_{website_url.replace('https://', '').replace('/', '_')}.json",422 mime="application/json"423 )424 425 except Exception as e:426 st.error(f"An error occurred during website analysis: {str(e)}")427 428 def render_multilingual_tab(self):429 st.markdown("### ๐ Multilingual Translator")430 st.write("Detect language and translate text into a target language.")431 432 text = st.text_area("Enter text:")433 if text:434 detected_lang = detect_language(text)435 st.write(f"Detected Language: **{detected_lang}**")436 437 target = st.selectbox("Select target language", ["en", "fr", "es", "de", "ur", "hi", "zh", "ar", "ru"])438 if st.button("Translate"):439 result = translate_text(text, target)440 st.success("Translation:")441 st.write(result)442 443 444 def display_geo_results(self, geo_results: List[Dict], website_url: str):445 """Display GEO analysis results"""446 st.markdown("## ๐ GEO Analysis Results")447 448 # Calculate average scores449 avg_scores = self.calculate_average_scores(geo_results)450 overall_avg = sum(avg_scores.values()) / len(avg_scores) if avg_scores else 0451 452 # Main score display453 col1, col2, col3 = st.columns([1, 2, 1])454 with col2:455 st.metric(456 "Overall GEO Score", 457 f"{overall_avg:.1f}/10",458 delta=f"{overall_avg - 7.0:.1f}" if overall_avg != 7.0 else None459 )460 461 # Individual metrics462 st.markdown("### ๐ Detailed GEO Metrics")463 464 # First row of metrics465 col1, col2, col3, col4 = st.columns(4)466 metrics_row1 = [467 ("AI Search Visibility", "ai_search_visibility"),468 ("Query Intent Match", "query_intent_matching"),469 ("Factual Accuracy", "factual_accuracy"),470 ("Conversational Ready", "conversational_readiness")471 ]472 473 for i, (display_name, key) in enumerate(metrics_row1):474 with [col1, col2, col3, col4][i]:475 score = avg_scores.get(key, 0)476 st.metric(display_name, f"{score:.1f}")477 478 # Second row of metrics479 col1, col2, col3, col4 = st.columns(4)480 metrics_row2 = [481 ("Semantic Richness", "semantic_richness"),482 ("Context Complete", "context_completeness"),483 ("Citation Worthy", "citation_worthiness"),484 ("Multi-Query Cover", "multi_query_coverage")485 ]486 487 for i, (display_name, key) in enumerate(metrics_row2):488 with [col1, col2, col3, col4][i]:489 score = avg_scores.get(key, 0)490 st.metric(display_name, f"{score:.1f}")491 492 # Recommendations493 self.display_recommendations(geo_results)494 495 # Detailed page analysis496 with st.expander("๐ Detailed Page Analysis"):497 for i, analysis in enumerate(geo_results):498 page_data = analysis.get('page_data', {})499 st.markdown(f"#### Page {i+1}: {page_data.get('title', 'Unknown Title')}")500 st.write(f"**URL**: {page_data.get('url', 'Unknown')}")501 st.write(f"**Word Count**: {page_data.get('word_count', 0)}")502 503 # Show topics and entities if available504 if 'primary_topics' in analysis:505 st.write(f"**Topics**: {', '.join(analysis['primary_topics'])}")506 507 if 'entities' in analysis:508 st.write(f"**Entities**: {', '.join(analysis['entities'])}")509 510 # Show page-specific scores511 if 'geo_scores' in analysis:512 scores = analysis['geo_scores']513 score_text = ", ".join([f"{k}: {v:.1f}" for k, v in scores.items()])514 st.write(f"**Scores**: {score_text}")515 516 st.write("---")517 518 519 def display_recommendations(self, geo_results: List[Dict]):520 """Display optimization recommendations"""521 st.markdown("### ๐ก Optimization Recommendations")522 523 # Collect all recommendations524 all_recommendations = []525 all_opportunities = []526 527 for analysis in geo_results:528 all_recommendations.extend(analysis.get('recommendations', []))529 all_opportunities.extend(analysis.get('optimization_opportunities', []))530 531 # Remove duplicates and display532 unique_recommendations = list(set(all_recommendations))533 534 if unique_recommendations:535 for i, rec in enumerate(unique_recommendations[:5], 1):536 st.write(f"**{i}.** {rec}")537 538 # Priority opportunities539 if all_opportunities:540 st.markdown("#### ๐ Priority Optimizations")541 542 high_priority = [opp for opp in all_opportunities if opp.get('priority') == 'high']543 medium_priority = [opp for opp in all_opportunities if opp.get('priority') == 'medium']544 545 if high_priority:546 st.markdown("##### ๐ด High Priority")547 for opp in high_priority[:3]:548 st.write(f"**{opp.get('type', 'Optimization')}**: {opp.get('description', 'No description')}")549 550 if medium_priority:551 st.markdown("##### ๐ก Medium Priority")552 for opp in medium_priority[:3]:553 st.write(f"**{opp.get('type', 'Optimization')}**: {opp.get('description', 'No description')}")554 555 def calculate_average_scores(self, geo_results: List[Dict]) -> Dict[str, float]:556 """Calculate average GEO scores across all pages"""557 if not geo_results:558 return {}559 560 # Get all score keys from the first result561 score_keys = list(geo_results[0].get('geo_scores', {}).keys())562 avg_scores = {}563 564 for key in score_keys:565 scores = [566 result['geo_scores'][key] 567 for result in geo_results 568 if 'geo_scores' in result and key in result['geo_scores']569 ]570 avg_scores[key] = sum(scores) / len(scores) if scores else 0571 572 return avg_scores573 574 def save_uploaded_file(self, uploaded_file) -> str:575 """Save uploaded file to temporary location"""576 with tempfile.NamedTemporaryFile(delete=False, suffix=".pdf") as tmp_file:577 tmp_file.write(uploaded_file.read())578 return tmp_file.name579 580def main():581 """Main entry point"""582 if "rag_chain" not in st.session_state:583 st.session_state.rag_chain = None584 585 app = GEOSEOApp()586 app.run()587if __name__ == "__main__":588 main()589 