New tools added to ToBeMigrated/ directory: ai_marketing_tools/: - ai_backlinker: AI-powered backlink generation - ai_google_ads_generator: Google Ads generation with templates ai_writers/: - ai_blog_faqs_writer: FAQ generation for blogs - ai_copywriter: Multiple copywriter frameworks (AIDA, PAS, 4C, 4R, etc.) - ai_finance_report_generator: Financial report generation - ai_story_illustrator: Story illustration - ai_story_video_generator: Story video generation - ai_story_writer: AI story writing - github_blogs: GitHub blog integration - speech_to_blog: Audio to blog conversion - twitter_writers: Twitter/X content generation - youtube_writers: YouTube content generation These tools are in ToBeMigrated/ for future migration to the main backend.
312 lines
13 KiB
Python
312 lines
13 KiB
Python
"""
|
|
Streamlit UI for FAQ Generator
|
|
|
|
This module provides a user-friendly interface for generating FAQs from various content sources.
|
|
"""
|
|
|
|
import streamlit as st
|
|
from pathlib import Path
|
|
from typing import Optional
|
|
import json
|
|
import requests
|
|
from bs4 import BeautifulSoup
|
|
import logging
|
|
import pyperclip
|
|
|
|
from .faqs_generator_blog import FAQGenerator, FAQConfig, TargetAudience, FAQStyle, SearchDepth
|
|
|
|
# Set up logging
|
|
logging.basicConfig(level=logging.INFO)
|
|
logger = logging.getLogger(__name__)
|
|
|
|
def copy_to_clipboard(text: str) -> None:
|
|
"""Copy text to clipboard and show success message."""
|
|
try:
|
|
pyperclip.copy(text)
|
|
st.success("Copied to clipboard!")
|
|
except Exception as e:
|
|
st.error(f"Failed to copy to clipboard: {str(e)}")
|
|
|
|
def fetch_url_content(url):
|
|
"""Fetch and extract content from a URL."""
|
|
try:
|
|
response = requests.get(url)
|
|
response.raise_for_status()
|
|
soup = BeautifulSoup(response.text, 'html.parser')
|
|
|
|
# Remove script and style elements
|
|
for script in soup(["script", "style"]):
|
|
script.decompose()
|
|
|
|
# Get text
|
|
text = soup.get_text()
|
|
|
|
# Break into lines and remove leading and trailing space
|
|
lines = (line.strip() for line in text.splitlines())
|
|
# Break multi-headlines into a line each
|
|
chunks = (phrase.strip() for line in lines for phrase in line.split(" "))
|
|
# Drop blank lines
|
|
text = '\n'.join(chunk for chunk in chunks if chunk)
|
|
|
|
return text
|
|
except Exception as e:
|
|
st.error(f"Error fetching URL content: {str(e)}")
|
|
return None
|
|
|
|
def main():
|
|
st.title("FAQ Generator")
|
|
st.markdown("Generate comprehensive FAQs from your content with research integration.")
|
|
|
|
# Initialize session state variables if they don't exist
|
|
if 'search_queries' not in st.session_state:
|
|
st.session_state.search_queries = []
|
|
if 'selected_queries' not in st.session_state:
|
|
st.session_state.selected_queries = []
|
|
if 'research_completed' not in st.session_state:
|
|
st.session_state.research_completed = False
|
|
if 'research_results' not in st.session_state:
|
|
st.session_state.research_results = {}
|
|
if 'faq_config' not in st.session_state:
|
|
st.session_state.faq_config = None
|
|
if 'generator' not in st.session_state:
|
|
st.session_state.generator = FAQGenerator()
|
|
if 'generated_faqs' not in st.session_state:
|
|
st.session_state.generated_faqs = None
|
|
if 'output_format' not in st.session_state:
|
|
st.session_state.output_format = "Preview"
|
|
|
|
# Sidebar for configuration
|
|
with st.sidebar:
|
|
st.header("Configuration")
|
|
|
|
# Basic settings
|
|
num_faqs = st.slider("Number of FAQs", 1, 20, 5)
|
|
target_audience = st.selectbox(
|
|
"Target Audience",
|
|
[audience.value for audience in TargetAudience]
|
|
)
|
|
faq_style = st.selectbox(
|
|
"FAQ Style",
|
|
[style.value for style in FAQStyle]
|
|
)
|
|
|
|
# Advanced settings
|
|
with st.expander("Advanced Settings"):
|
|
include_emojis = st.checkbox("Include Emojis", value=True)
|
|
include_code_examples = st.checkbox("Include Code Examples", value=True)
|
|
include_references = st.checkbox("Include References", value=True)
|
|
|
|
search_depth = st.selectbox(
|
|
"Search Depth",
|
|
[depth.value for depth in SearchDepth]
|
|
)
|
|
time_range = st.selectbox(
|
|
"Time Range",
|
|
["last_month", "last_6_months", "last_year", "all_time"]
|
|
)
|
|
language = st.text_input("Language", value="English")
|
|
|
|
# Main content area
|
|
content_type = st.radio(
|
|
"Content Source",
|
|
["Direct Input", "File Upload", "URL"]
|
|
)
|
|
|
|
content = ""
|
|
if content_type == "Direct Input":
|
|
content = st.text_area("Enter your content", height=300)
|
|
|
|
elif content_type == "URL":
|
|
url = st.text_input("Enter URL")
|
|
if url:
|
|
content = fetch_url_content(url)
|
|
if content:
|
|
st.text_area("Extracted Content", content, height=300)
|
|
|
|
# Step 1: Generate search queries
|
|
if content and not st.session_state.search_queries:
|
|
if st.button("Generate Search Queries"):
|
|
with st.spinner("Generating search queries..."):
|
|
search_queries = st.session_state.generator.generate_search_queries(content)
|
|
if search_queries:
|
|
st.session_state.search_queries = search_queries
|
|
st.session_state.selected_queries = [] # Reset selected queries
|
|
st.session_state.research_completed = False # Reset research status
|
|
st.session_state.research_results = {} # Reset research results
|
|
st.session_state.faq_config = None # Reset config
|
|
st.session_state.generated_faqs = None # Reset generated FAQs
|
|
st.success("Search queries generated successfully!")
|
|
|
|
# Step 2: Display and select search queries
|
|
if st.session_state.search_queries:
|
|
st.subheader("Select Search Queries")
|
|
st.info("Select the queries you want to use for web research. You can select multiple queries.")
|
|
|
|
# Create checkboxes for each search query
|
|
selected_queries = []
|
|
for query in st.session_state.search_queries:
|
|
if st.checkbox(query, key=f"query_{query}", value=query in st.session_state.selected_queries):
|
|
selected_queries.append(query)
|
|
|
|
# Update selected queries in session state
|
|
st.session_state.selected_queries = selected_queries
|
|
|
|
# Step 3: Do web research
|
|
if st.session_state.selected_queries and not st.session_state.research_completed:
|
|
if st.button("Do Web Research"):
|
|
try:
|
|
# Create config with selected queries
|
|
config = FAQConfig(
|
|
num_faqs=num_faqs,
|
|
target_audience=TargetAudience(target_audience),
|
|
faq_style=FAQStyle(faq_style),
|
|
include_emojis=include_emojis,
|
|
include_code_examples=include_code_examples,
|
|
include_references=include_references,
|
|
search_depth=SearchDepth(search_depth),
|
|
time_range=time_range,
|
|
language=language,
|
|
selected_search_queries=selected_queries
|
|
)
|
|
|
|
# Store config in session state
|
|
st.session_state.faq_config = config
|
|
|
|
# Update generator with config
|
|
st.session_state.generator.config = config
|
|
|
|
# Do research
|
|
with st.spinner("Conducting web research..."):
|
|
research_results = st.session_state.generator._conduct_research(content)
|
|
st.session_state.research_completed = True
|
|
st.session_state.research_results = research_results
|
|
st.success("Web research completed successfully!")
|
|
|
|
# Display research results
|
|
st.subheader("Research Results")
|
|
for query, results in research_results.items():
|
|
with st.expander(f"Results for: {query}"):
|
|
if isinstance(results, dict):
|
|
st.json(results)
|
|
else:
|
|
st.text(results)
|
|
|
|
except Exception as e:
|
|
st.error(f"Error during web research: {str(e)}")
|
|
st.error("Please try again with different search queries or adjust the search depth.")
|
|
|
|
# Step 4: Generate FAQs
|
|
if st.session_state.research_completed and st.session_state.research_results and st.session_state.faq_config:
|
|
if st.button("Generate FAQs"):
|
|
try:
|
|
# Update generator with stored config
|
|
st.session_state.generator.config = st.session_state.faq_config
|
|
|
|
# Generate FAQs
|
|
with st.spinner("Generating FAQs..."):
|
|
logger.info("Starting FAQ generation...")
|
|
faqs = st.session_state.generator.generate_faqs(content)
|
|
logger.info(f"Generated {len(faqs) if faqs else 0} FAQs")
|
|
|
|
if not faqs:
|
|
st.error("No FAQs were generated. Please try again.")
|
|
return
|
|
|
|
st.session_state.generated_faqs = faqs
|
|
st.success("FAQs generated successfully!")
|
|
|
|
except Exception as e:
|
|
logger.error(f"Error generating FAQs: {str(e)}")
|
|
st.error(f"Error generating FAQs: {str(e)}")
|
|
st.error("Please try again or adjust your settings.")
|
|
|
|
# Display generated FAQs if they exist
|
|
if st.session_state.generated_faqs:
|
|
st.subheader("Generated FAQs")
|
|
|
|
# Output format selection
|
|
output_format = st.radio(
|
|
"Output Format",
|
|
["Preview", "Markdown", "HTML", "JSON"],
|
|
key="output_format"
|
|
)
|
|
|
|
# Create columns for copy and download buttons
|
|
col1, col2 = st.columns(2)
|
|
|
|
if output_format == "Preview":
|
|
# Create a formatted text for copying
|
|
preview_text = ""
|
|
for i, faq in enumerate(st.session_state.generated_faqs, 1):
|
|
preview_text += f"{i}. {faq.question}\n"
|
|
preview_text += f"{faq.answer}\n\n"
|
|
if faq.code_example:
|
|
preview_text += f"Code Example:\n{faq.code_example}\n\n"
|
|
if faq.references:
|
|
preview_text += "References:\n"
|
|
for ref in faq.references:
|
|
preview_text += f"- {ref['source']}\n"
|
|
preview_text += "\n"
|
|
|
|
with col1:
|
|
if st.button("Copy to Clipboard", key="copy_preview"):
|
|
copy_to_clipboard(preview_text)
|
|
|
|
# Display the FAQs
|
|
for i, faq in enumerate(st.session_state.generated_faqs, 1):
|
|
with st.expander(f"{i}. {faq.question}"):
|
|
st.markdown(faq.answer)
|
|
if faq.code_example:
|
|
st.code(faq.code_example)
|
|
if faq.references:
|
|
st.markdown("**References:**")
|
|
for ref in faq.references:
|
|
st.markdown(f"- {ref['source']}")
|
|
|
|
elif output_format == "Markdown":
|
|
markdown_output = st.session_state.generator.to_markdown()
|
|
st.code(markdown_output, language="markdown")
|
|
|
|
with col1:
|
|
if st.button("Copy to Clipboard", key="copy_markdown"):
|
|
copy_to_clipboard(markdown_output)
|
|
with col2:
|
|
st.download_button(
|
|
"Download Markdown",
|
|
markdown_output,
|
|
file_name="faqs.md",
|
|
mime="text/markdown"
|
|
)
|
|
|
|
elif output_format == "HTML":
|
|
html_output = st.session_state.generator.to_html()
|
|
st.code(html_output, language="html")
|
|
|
|
with col1:
|
|
if st.button("Copy to Clipboard", key="copy_html"):
|
|
copy_to_clipboard(html_output)
|
|
with col2:
|
|
st.download_button(
|
|
"Download HTML",
|
|
html_output,
|
|
file_name="faqs.html",
|
|
mime="text/html"
|
|
)
|
|
|
|
elif output_format == "JSON":
|
|
json_output = json.dumps([faq.__dict__ for faq in st.session_state.generated_faqs], indent=2)
|
|
st.code(json_output, language="json")
|
|
|
|
with col1:
|
|
if st.button("Copy to Clipboard", key="copy_json"):
|
|
copy_to_clipboard(json_output)
|
|
with col2:
|
|
st.download_button(
|
|
"Download JSON",
|
|
json_output,
|
|
file_name="faqs.json",
|
|
mime="application/json"
|
|
)
|
|
|
|
if __name__ == "__main__":
|
|
main() |