Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
|
@@ -1,7 +1,12 @@
|
|
| 1 |
-
# File:
|
| 2 |
import gradio as gr
|
| 3 |
import google.generativeai as genai
|
| 4 |
import textstat
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 5 |
|
| 6 |
# Humanizer helper
|
| 7 |
def humanize_text(text):
|
|
@@ -11,78 +16,167 @@ def humanize_text(text):
|
|
| 11 |
"It is important to note": "Here's what you need to know",
|
| 12 |
"Furthermore": "Also",
|
| 13 |
"However": "But",
|
| 14 |
-
"Therefore": "So"
|
|
|
|
|
|
|
|
|
|
| 15 |
}
|
|
|
|
|
|
|
| 16 |
for old, new in replacements.items():
|
|
|
|
| 17 |
text = text.replace(old, new)
|
|
|
|
|
|
|
| 18 |
return text
|
| 19 |
|
| 20 |
# Readability analysis
|
| 21 |
def analyze_readability(text):
|
| 22 |
-
|
| 23 |
-
|
| 24 |
-
|
| 25 |
-
|
| 26 |
-
#
|
| 27 |
-
|
| 28 |
-
|
| 29 |
-
|
| 30 |
-
"
|
| 31 |
-
|
| 32 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 33 |
|
| 34 |
# Main generation function
|
| 35 |
-
def generate_article(
|
|
|
|
| 36 |
try:
|
| 37 |
-
|
| 38 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 39 |
|
| 40 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 41 |
prompt = f"""
|
| 42 |
-
You are
|
| 43 |
-
|
| 44 |
-
|
| 45 |
-
|
| 46 |
-
|
| 47 |
-
|
| 48 |
-
|
| 49 |
-
|
| 50 |
-
|
| 51 |
-
|
| 52 |
-
|
| 53 |
-
|
| 54 |
-
|
| 55 |
-
|
| 56 |
-
|
| 57 |
-
|
| 58 |
-
|
| 59 |
-
|
| 60 |
-
|
| 61 |
-
|
| 62 |
-
|
| 63 |
-
|
| 64 |
-
|
| 65 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 66 |
"""
|
| 67 |
|
| 68 |
-
|
| 69 |
-
model = genai.GenerativeModel('gemini-1.5-pro')
|
| 70 |
response = model.generate_content(prompt)
|
|
|
|
| 71 |
|
| 72 |
-
|
| 73 |
-
humanized = humanize_text(response.text)
|
| 74 |
|
| 75 |
-
|
| 76 |
-
humanized += "\n\n" + analyze_readability(humanized)
|
| 77 |
-
|
| 78 |
if include_titles:
|
| 79 |
-
titles = generate_title_variations()
|
| 80 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 81 |
|
| 82 |
-
return
|
| 83 |
|
| 84 |
except Exception as e:
|
| 85 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 86 |
# Enhanced theme
|
| 87 |
custom_theme = gr.themes.Soft(
|
| 88 |
primary_hue="blue",
|
|
@@ -98,70 +192,113 @@ custom_theme = gr.themes.Soft(
|
|
| 98 |
)
|
| 99 |
|
| 100 |
# Gradio Interface
|
| 101 |
-
with gr.Blocks(title="SEO Article Generator", theme=custom_theme) as demo:
|
| 102 |
gr.Markdown("# SEO-to-Article Generator ๐")
|
| 103 |
-
gr.Markdown("
|
| 104 |
|
| 105 |
with gr.Row():
|
| 106 |
with gr.Column(scale=1):
|
| 107 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 108 |
|
| 109 |
-
|
| 110 |
-
label="
|
| 111 |
-
|
| 112 |
-
|
| 113 |
)
|
| 114 |
|
| 115 |
-
|
| 116 |
-
label="
|
| 117 |
-
|
| 118 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 119 |
)
|
| 120 |
|
| 121 |
-
|
| 122 |
-
label="
|
| 123 |
-
choices=["
|
| 124 |
-
value="
|
| 125 |
)
|
| 126 |
|
| 127 |
-
|
| 128 |
-
label="
|
| 129 |
-
choices=["
|
| 130 |
-
value="
|
| 131 |
)
|
| 132 |
|
| 133 |
-
|
| 134 |
-
label="
|
| 135 |
-
|
| 136 |
-
|
| 137 |
)
|
| 138 |
|
| 139 |
-
|
| 140 |
-
label="
|
| 141 |
-
|
| 142 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 143 |
)
|
| 144 |
|
| 145 |
-
|
| 146 |
-
|
| 147 |
|
| 148 |
with gr.Column(scale=2):
|
| 149 |
-
|
| 150 |
-
label="
|
| 151 |
-
placeholder=
|
| 152 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 153 |
)
|
| 154 |
|
| 155 |
-
submit_btn = gr.Button("โจ Generate Article", variant="primary")
|
| 156 |
|
| 157 |
-
|
| 158 |
|
| 159 |
submit_btn.click(
|
| 160 |
fn=generate_article,
|
| 161 |
-
inputs=[
|
| 162 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 163 |
)
|
|
|
|
|
|
|
| 164 |
|
| 165 |
# Launch app
|
| 166 |
if __name__ == "__main__":
|
| 167 |
-
demo.launch()
|
|
|
|
| 1 |
+
# File: app_SeoPlan2Article.py
|
| 2 |
import gradio as gr
|
| 3 |
import google.generativeai as genai
|
| 4 |
import textstat
|
| 5 |
+
import os
|
| 6 |
+
# from dotenv import load_dotenv # Optional: if you want to use .env files for API key
|
| 7 |
+
|
| 8 |
+
# Optional: Load environment variables if you use a .env file
|
| 9 |
+
# load_dotenv()
|
| 10 |
|
| 11 |
# Humanizer helper
|
| 12 |
def humanize_text(text):
|
|
|
|
| 16 |
"It is important to note": "Here's what you need to know",
|
| 17 |
"Furthermore": "Also",
|
| 18 |
"However": "But",
|
| 19 |
+
"Therefore": "So",
|
| 20 |
+
"In the realm of": "When it comes to",
|
| 21 |
+
"The landscape of": "Thinking about",
|
| 22 |
+
"It's crucial to": "You'll want to"
|
| 23 |
}
|
| 24 |
+
# Using regex for case-insensitive replacement and whole word matching for some
|
| 25 |
+
import re
|
| 26 |
for old, new in replacements.items():
|
| 27 |
+
# Basic replacement for most
|
| 28 |
text = text.replace(old, new)
|
| 29 |
+
# Case-insensitive for some common ones if needed
|
| 30 |
+
# text = re.sub(r'\b' + re.escape(old) + r'\b', new, text, flags=re.IGNORECASE)
|
| 31 |
return text
|
| 32 |
|
| 33 |
# Readability analysis
|
| 34 |
def analyze_readability(text):
|
| 35 |
+
try:
|
| 36 |
+
flesch = textstat.flesch_kincaid_grade(text)
|
| 37 |
+
# Robust sentence splitting and counting
|
| 38 |
+
sentences = [s for s in text.split('.') if s.strip()]
|
| 39 |
+
num_sentences = len(sentences) if len(sentences) > 0 else 1 # Avoid division by zero
|
| 40 |
+
words = text.split()
|
| 41 |
+
num_words = len(words)
|
| 42 |
+
sentence_length = round(num_words / num_sentences, 1) if num_sentences > 0 else 0
|
| 43 |
+
return f"Readability Analysis:\n- Flesch-Kincaid Grade Level: {flesch}\n- Average Sentence Length: {sentence_length} words"
|
| 44 |
+
except Exception as e:
|
| 45 |
+
return f"Readability Analysis: Error ({e})"
|
| 46 |
+
|
| 47 |
+
|
| 48 |
+
# Dynamic Title variations
|
| 49 |
+
def generate_title_variations(api_key, primary_keyword, tone, pov):
|
| 50 |
+
if not primary_keyword:
|
| 51 |
+
return ["Please provide a primary keyword for title generation."]
|
| 52 |
+
try:
|
| 53 |
+
# genai.configure(api_key=api_key) # Configure if not already configured globally
|
| 54 |
+
model = genai.GenerativeModel('gemini-1.5-pro-latest') # Using latest
|
| 55 |
+
prompt = f"""
|
| 56 |
+
Generate 3 compelling, SEO-friendly H1 title variations for an article about "{primary_keyword}".
|
| 57 |
+
The tone should be {tone} and from a {pov} perspective.
|
| 58 |
+
Keep them concise and engaging.
|
| 59 |
+
Format as a numbered list. Ensure each title is on a new line starting with number and period.
|
| 60 |
+
Example:
|
| 61 |
+
1. Title Variation 1
|
| 62 |
+
2. Title Variation 2
|
| 63 |
+
3. Title Variation 3
|
| 64 |
+
"""
|
| 65 |
+
response = model.generate_content(prompt)
|
| 66 |
+
titles = []
|
| 67 |
+
# Improved parsing for Gemini's potential list format
|
| 68 |
+
for line in response.text.strip().split('\n'):
|
| 69 |
+
if '. ' in line and line.strip()[0].isdigit(): # Check if line starts with "1. ", "2. " etc.
|
| 70 |
+
try:
|
| 71 |
+
titles.append(line.split('. ', 1)[1])
|
| 72 |
+
except IndexError:
|
| 73 |
+
titles.append(line) # Fallback if split fails unexpectedly
|
| 74 |
+
elif line.strip(): # Add non-empty lines if parsing fails
|
| 75 |
+
titles.append(line.strip())
|
| 76 |
+
|
| 77 |
+
return titles if titles else ["Could not generate titles. Gemini response was empty or unparseable."]
|
| 78 |
+
except Exception as e:
|
| 79 |
+
return [f"Error generating titles: {str(e)}"]
|
| 80 |
|
| 81 |
# Main generation function
|
| 82 |
+
def generate_article(api_key_input, primary_keyword_text, seo_plan, pov, tone, length, emotion,
|
| 83 |
+
include_titles, include_readability, target_keywords_raw, links_to_include_raw, cta_text=""):
|
| 84 |
try:
|
| 85 |
+
api_key_to_use = os.getenv("GEMINI_API_KEY")
|
| 86 |
+
if api_key_input: # Prioritize textbox input if provided
|
| 87 |
+
api_key_to_use = api_key_input.strip()
|
| 88 |
+
|
| 89 |
+
if not api_key_to_use:
|
| 90 |
+
return "๐จ Error: API Key not provided. Please enter it in the field or set the GEMINI_API_KEY environment variable."
|
| 91 |
|
| 92 |
+
genai.configure(api_key=api_key_to_use)
|
| 93 |
+
|
| 94 |
+
if not primary_keyword_text:
|
| 95 |
+
return "๐จ Error: Primary Keyword is required."
|
| 96 |
+
if not seo_plan:
|
| 97 |
+
return "๐จ Error: SEO Plan / Article Outline is required."
|
| 98 |
+
|
| 99 |
+
# Process links (internal & external)
|
| 100 |
+
parsed_links_for_prompt = []
|
| 101 |
+
if links_to_include_raw:
|
| 102 |
+
for line in links_to_include_raw.strip().split('\n'):
|
| 103 |
+
line = line.strip()
|
| 104 |
+
if ':' in line:
|
| 105 |
+
anchor, url = line.split(':', 1)
|
| 106 |
+
anchor = anchor.strip()
|
| 107 |
+
url = url.strip()
|
| 108 |
+
if anchor and url: # Ensure both parts are non-empty
|
| 109 |
+
parsed_links_for_prompt.append(f"- Anchor Text: \"{anchor}\", URL: {url}")
|
| 110 |
+
links_instruction = "\n".join(parsed_links_for_prompt) if parsed_links_for_prompt else "No specific links provided; link naturally if opportunities arise to relevant internal or external resources."
|
| 111 |
+
|
| 112 |
+
# Process target keywords
|
| 113 |
+
target_keywords_list = [kw.strip() for kw in target_keywords_raw.split(',') if kw.strip()] if target_keywords_raw else []
|
| 114 |
+
target_keywords_instruction = f"Subtly integrate these target keywords where they fit naturally: {', '.join(target_keywords_list)}." if target_keywords_list else "No additional target keywords provided."
|
| 115 |
+
|
| 116 |
+
|
| 117 |
prompt = f"""
|
| 118 |
+
You are an expert human writer, skilled in creating engaging, SEO-friendly content that solves real problems for readers.
|
| 119 |
+
|
| 120 |
+
ARTICLE TASK:
|
| 121 |
+
Write a {length} article in {pov} POV with a {tone} tone. The central focus is the primary keyword: "{primary_keyword_text}".
|
| 122 |
+
|
| 123 |
+
CORE INSTRUCTIONS:
|
| 124 |
+
1. Headline (H1): Create an H1 title that is compelling, unique, and naturally incorporates the primary keyword: "{primary_keyword_text}". Do NOT just repeat the primary keyword as the H1.
|
| 125 |
+
2. Structure: Adhere strictly to the headings (H2s, H3s, etc., as indicated by ##, ###) provided in the 'SEO PLAN / OUTLINE' section below. If no H-tags are in the outline, structure it logically based on the content.
|
| 126 |
+
3. Content Style:
|
| 127 |
+
* Adopt a {emotion} emotional tone. For example, if "Confidence", use phrases like "You've got this!" or "Rest assured...".
|
| 128 |
+
* Write conversationally and engagingly. Use contractions (e.g., you're, it's, don't).
|
| 129 |
+
* Incorporate one brief, relatable anecdote relevant to the topic.
|
| 130 |
+
* Keep paragraphs short and highly readable (generally 1-3 sentences).
|
| 131 |
+
* Strictly AVOID AI clichรฉs, robotic phrasing, and overly formal language (e.g., "In conclusion", "Furthermore", "It is important to note", "The realm of", "The digital landscape"). Be original.
|
| 132 |
+
4. Keyword Integration:
|
| 133 |
+
* Primary Keyword: "{primary_keyword_text}". Ensure this is used naturally 2-4 times throughout the article, including the H1 (or a close variation), introduction, and conclusion where appropriate.
|
| 134 |
+
* Target/Semantic Keywords: {target_keywords_instruction}
|
| 135 |
+
5. Link Integration:
|
| 136 |
+
* The following links (which may be internal or external) should be embedded at natural and relevant points in the text. Use the exact anchor text and corresponding URL provided for each. Make the integration seamless.
|
| 137 |
+
Links to Incorporate:
|
| 138 |
+
{links_instruction}
|
| 139 |
+
6. FAQs: If appropriate for the topic (or if specified in the outline), include a Frequently Asked Questions (FAQ) section at the end with 2-4 relevant questions and clear, concise, conversational answers.
|
| 140 |
+
{f"7. Call to Action: Conclude the article with this specific call to action: \"{cta_text}\"" if cta_text else "7. Call to Action: Conclude with a natural and relevant call to action (e.g., inviting comments, suggesting a next step, or encouraging sharing)."}
|
| 141 |
+
|
| 142 |
+
SEO PLAN / OUTLINE:
|
| 143 |
+
---
|
| 144 |
+
{seo_plan}
|
| 145 |
+
---
|
| 146 |
+
|
| 147 |
+
Begin the article directly with the H1 heading.
|
| 148 |
"""
|
| 149 |
|
| 150 |
+
model = genai.GenerativeModel('gemini-1.5-pro-latest')
|
|
|
|
| 151 |
response = model.generate_content(prompt)
|
| 152 |
+
article_text = response.text
|
| 153 |
|
| 154 |
+
humanized_article = humanize_text(article_text)
|
|
|
|
| 155 |
|
| 156 |
+
final_output_parts = []
|
|
|
|
|
|
|
| 157 |
if include_titles:
|
| 158 |
+
titles = generate_title_variations(api_key_to_use, primary_keyword_text, tone, pov)
|
| 159 |
+
final_output_parts.append("โจ **Suggested Title Variations:**\n" + "\n".join([f"ย ย ย ย {i+1}. {t}" for i, t in enumerate(titles)]))
|
| 160 |
+
|
| 161 |
+
final_output_parts.append("โ๏ธ **Generated Article:**\n" + humanized_article)
|
| 162 |
+
|
| 163 |
+
if include_readability:
|
| 164 |
+
final_output_parts.append("๐ **Readability Analysis:**\n" + analyze_readability(humanized_article).replace("\n-", "\nย ย ย ย -")) # Indent list items
|
| 165 |
|
| 166 |
+
return "\n\n---\n\n".join(final_output_parts)
|
| 167 |
|
| 168 |
except Exception as e:
|
| 169 |
+
import traceback
|
| 170 |
+
print(f"Detailed Error: {traceback.format_exc()}") # Print full traceback to console for debugging
|
| 171 |
+
error_message = f"๐จ **Critical Error:** {str(e)}\n\n"
|
| 172 |
+
error_message += "This might be due to:\n"
|
| 173 |
+
error_message += "- Invalid or expired API Key.\n"
|
| 174 |
+
error_message += "- Issues with the Gemini API service.\n"
|
| 175 |
+
error_message += "- Malformed input in the SEO Plan or other fields.\n"
|
| 176 |
+
error_message += "- An unexpected problem during generation.\n\n"
|
| 177 |
+
error_message += "Please check your API key, ensure your inputs are correct, and try again. If the issue persists, review the console logs for more details."
|
| 178 |
+
return error_message
|
| 179 |
+
|
| 180 |
# Enhanced theme
|
| 181 |
custom_theme = gr.themes.Soft(
|
| 182 |
primary_hue="blue",
|
|
|
|
| 192 |
)
|
| 193 |
|
| 194 |
# Gradio Interface
|
| 195 |
+
with gr.Blocks(title="SEO Article Generator", theme=custom_theme, css=".gradio-container {max-width: 1000px !important; margin: auto;} footer {display: none !important}") as demo:
|
| 196 |
gr.Markdown("# SEO-to-Article Generator ๐")
|
| 197 |
+
gr.Markdown("Transform your SEO plan into humanized, optimized content. Provide your Gemini API key, primary keyword, outline, and linking strategy.")
|
| 198 |
|
| 199 |
with gr.Row():
|
| 200 |
with gr.Column(scale=1):
|
| 201 |
+
api_key_input_ui = gr.Textbox(
|
| 202 |
+
label="๐ Gemini API Key",
|
| 203 |
+
type="password",
|
| 204 |
+
placeholder="AIzaSy... (or leave blank if GEMINI_API_KEY env var is set)",
|
| 205 |
+
info="Your Google Gemini API Key. Can also be set as an environment variable."
|
| 206 |
+
)
|
| 207 |
|
| 208 |
+
primary_keyword_ui = gr.Textbox(
|
| 209 |
+
label="๐ฏ Primary Keyword (Required)",
|
| 210 |
+
placeholder="e.g., how to make kombucha",
|
| 211 |
+
info="The main keyword for the article."
|
| 212 |
)
|
| 213 |
|
| 214 |
+
target_keywords_ui = gr.Textbox( # RENAMED and PURPOSE CLARIFIED
|
| 215 |
+
label="โ Target/Semantic Keywords (Optional)",
|
| 216 |
+
placeholder="e.g., SCOBY, probiotics, fermentation, homebrew kombucha benefits",
|
| 217 |
+
lines=3,
|
| 218 |
+
info="Comma-separated list of additional keywords to include naturally."
|
| 219 |
+
)
|
| 220 |
+
|
| 221 |
+
links_to_include_ui = gr.Textbox( # RENAMED and PURPOSE CLARIFIED
|
| 222 |
+
label="๐ Internal & External Links to Include (Optional)",
|
| 223 |
+
placeholder="Benefit of Kombucha: https://example.com/kombucha-benefits\nAuthoritative Fermentation Guide: https://trusted-site.org/fermentation",
|
| 224 |
+
lines=4,
|
| 225 |
+
info="Format: Anchor Text: URL (one link per line). Both internal and external links."
|
| 226 |
)
|
| 227 |
|
| 228 |
+
pov_ui = gr.Dropdown(
|
| 229 |
+
label="๐ Point of View",
|
| 230 |
+
choices=["First Person (I/We)", "Second Person (You/Your)", "Third Person (He/She/It/They)"],
|
| 231 |
+
value="Second Person (You/Your)"
|
| 232 |
)
|
| 233 |
|
| 234 |
+
tone_ui = gr.Dropdown(
|
| 235 |
+
label="๐จ Article Tone",
|
| 236 |
+
choices=["Friendly Guide", "Professional Expert", "Step-by-Step Teacher", "Curious Explorer", "Inspirational Motivator", "Witty & Humorous"],
|
| 237 |
+
value="Friendly Guide"
|
| 238 |
)
|
| 239 |
|
| 240 |
+
length_ui = gr.Dropdown(
|
| 241 |
+
label="๐ Content Length",
|
| 242 |
+
choices=["Short (~400-600 words)", "Standard (~700-1000 words)", "Long (~1100-1500+ words)"],
|
| 243 |
+
value="Standard (~700-1000 words)"
|
| 244 |
)
|
| 245 |
|
| 246 |
+
emotion_ui = gr.Dropdown(
|
| 247 |
+
label="๐ง Desired Emotional Impact",
|
| 248 |
+
choices=["Trust", "Clarity", "Confidence", "Curiosity", "Excitement", "Empathy", "Reassurance"],
|
| 249 |
+
value="Confidence"
|
| 250 |
+
)
|
| 251 |
+
|
| 252 |
+
cta_text_ui = gr.Textbox(
|
| 253 |
+
label="๐ข Call to Action (Optional)",
|
| 254 |
+
placeholder="e.g., Share your kombucha brewing experiences in the comments below!",
|
| 255 |
+
info="A specific CTA for the end of the article. If blank, AI will generate one."
|
| 256 |
)
|
| 257 |
|
| 258 |
+
include_titles_ui = gr.Checkbox(label="Suggest 3 Title Variations", value=True)
|
| 259 |
+
include_readability_ui = gr.Checkbox(label="Show Readability Analysis", value=True)
|
| 260 |
|
| 261 |
with gr.Column(scale=2):
|
| 262 |
+
seo_plan_ui = gr.Textbox(
|
| 263 |
+
label="๐ SEO Plan / Article Outline (Required)",
|
| 264 |
+
placeholder=(
|
| 265 |
+
"Example:\n"
|
| 266 |
+
"## What is Kombucha?\n"
|
| 267 |
+
"### Brief History\n"
|
| 268 |
+
"### Why Drink It?\n\n"
|
| 269 |
+
"## Key Ingredients for Homemade Kombucha\n"
|
| 270 |
+
"### SCOBY Explained\n"
|
| 271 |
+
"### Best Tea for Kombucha\n\n"
|
| 272 |
+
"## Step-by-Step Kombucha Brewing Guide\n"
|
| 273 |
+
"### First Fermentation (F1)\n"
|
| 274 |
+
"### Second Fermentation (F2) for Flavor & Fizz\n\n"
|
| 275 |
+
"## Common Kombucha Problems & Solutions\n"
|
| 276 |
+
"### Preventing Mold\n\n"
|
| 277 |
+
"## FAQs\n"
|
| 278 |
+
" - How long does kombucha last?\n"
|
| 279 |
+
" - Can I use a different sugar?"
|
| 280 |
+
),
|
| 281 |
+
lines=20, # Increased lines
|
| 282 |
+
info="Paste your detailed article outline. Use ## for H2, ### for H3. Include specific questions for FAQs if desired."
|
| 283 |
)
|
| 284 |
|
| 285 |
+
submit_btn = gr.Button("โจ Generate Article", variant="primary", elem_id="generate_button") # Added elem_id for potential CSS
|
| 286 |
|
| 287 |
+
output_ui = gr.Markdown(label="๐ Generated Output", elem_id="output_markdown") # Changed to Markdown for better formatting of titles/readability
|
| 288 |
|
| 289 |
submit_btn.click(
|
| 290 |
fn=generate_article,
|
| 291 |
+
inputs=[
|
| 292 |
+
api_key_input_ui, primary_keyword_ui, seo_plan_ui, pov_ui, tone_ui,
|
| 293 |
+
length_ui, emotion_ui, include_titles_ui, include_readability_ui,
|
| 294 |
+
target_keywords_ui, links_to_include_ui, cta_text_ui
|
| 295 |
+
],
|
| 296 |
+
outputs=output_ui,
|
| 297 |
+
# show_progress="full" # Uncomment for Gradio's built-in progress indicator
|
| 298 |
)
|
| 299 |
+
gr.Markdown("--- \n *Powered by Google Gemini. Ensure your API key has the Generative Language API enabled.*")
|
| 300 |
+
|
| 301 |
|
| 302 |
# Launch app
|
| 303 |
if __name__ == "__main__":
|
| 304 |
+
demo.launch(share=False) # Set share=True to get a public link if needed (for temporary sharing)
|