Bulb / app.py
CineAI's picture
Update app.py
e801b3f verified
import os
import time
import streamlit as st
from google import genai
from google.genai.types import Tool, GenerateContentConfig, GoogleSearch
import google.api_core
# --- Configuration ---
st.set_page_config(
page_title="AI Niche Content Idea Generator",
page_icon="πŸ’‘",
layout="centered",
)
# --- Patreon Link ---
PATREON_URL = "https://www.patreon.com/cineAI"
# --- Model List ---
FALLBACK_MODELS = [
"gemini-2.0-flash-thinking-exp-01-21",
"gemini-2.5-flash-preview-04-17",
"gemini-1.5-flash",
"gemini-1.5-pro",
"gemini-2.0-flash",
"gemini-2.0-flash-lite",
"gemini-2.5-pro-preview-03-25",
]
# --- Authentication ---
api_key = ""
try:
api_key = st.secrets["GOOGLE_API_KEY"]
api_key_configured = True
except (KeyError, FileNotFoundError):
st.error("🚨 Google API Key not found! Please go to the settings, then find the `Variables and secrets` item and click `New secret`, insert your Google API key.")
api_key_configured = False
st.stop()
# --- Helper Function for API Call with Fallback & Advanced Mode ---
def generate_content_ideas(niche, audience, content_format, num_ideas=4, advanced_mode=False):
"""Generates content ideas using Google Generative AI, with fallback and optional search."""
if not api_key_configured:
return "API Key not configured. Cannot proceed."
# --- Base Prompt ---
base_prompt = f"""
Act as an expert content strategist specializing in the niche: '{niche}'.
Your task is to generate {num_ideas} fresh, specific, and engaging content ideas tailored for the following target audience: '{audience}'.
The desired content format is: '{content_format}'.
Focus on providing unique angles, addressing potential pain points or interests of the audience, and suggesting topics that are relevant within the niche. Avoid generic ideas.
"""
# --- Enhance Prompt for Advanced Mode ---
if advanced_mode:
prompt = base_prompt + f"""
**IMPORTANT: Leverage current information from the web using the available search tool** to identify *trending topics*, *recent developments*, or *very specific audience questions* within the '{niche}' niche related to the '{audience}' audience. Ground your ideas in potentially searchable, up-to-date information.
"""
else:
prompt = base_prompt + """
Generate the ideas based on your general knowledge of the niche and audience.
"""
# --- Add Formatting Instruction ---
prompt += """
Please format the output as a numbered list. Each idea should be concise but descriptive.
Example for niche 'Sustainable Gardening', audience 'Urban Beginners', format 'Blog Post Titles':
1. 5 Easy-to-Grow Vegetables for Your Tiny Balcony Garden
2. Composting in Apartments: A No-Smell Guide for City Dwellers
3. Choosing the Right Recycled Pots for Your Indoor Plants
4. How to Attract Pollinators to Your Urban Oasis
5. Watering Wisely: Saving Water in Your Container Garden
Now, generate the ideas for the requested niche, audience, and format:
"""
last_error = None
last_error_message = ""
# --- Tool Configuration ---
if advanced_mode:
google_search_tool = Tool(google_search=GoogleSearch())
config = GenerateContentConfig(
tools=[google_search_tool],
response_modalities=['TEXT'],
# system_instruction=system_instruction_google_search,
max_output_tokens=4100,
)
else:
config = None
tool_status_msg = " (Advanced Mode with Google Search πŸ”)" if advanced_mode else ""
for model_name in FALLBACK_MODELS:
st.info(f"🧠 Attempting generation with model: `{model_name}`{tool_status_msg}...")
try:
model = genai.Client(api_key=api_key)
response = model.models.generate_content(
model=model_name,
contents=prompt,
config=config,
)
# --- Check for successful response (handle potential blocks) ---
if hasattr(response, 'text') and response.text:
st.success(f"βœ… Successfully generated ideas using `{model_name}`!")
return response.text
elif hasattr(response, 'prompt_feedback') and response.prompt_feedback.block_reason:
error_message = f"⚠️ Generation blocked by `{model_name}`. Reason: {response.prompt_feedback.block_reason.name}. Trying next model..."
st.warning(error_message)
last_error = BlockingIOError(f"{model_name}: Blocked - {response.prompt_feedback.block_reason.name}")
last_error_message = error_message
time.sleep(0.5)
continue
else:
error_message = f"⚠️ Generation failed with `{model_name}` (empty or invalid response structure). Trying next model..."
st.warning(error_message)
last_error = ValueError(f"{model_name}: Empty/Invalid response")
last_error_message = error_message
time.sleep(1)
continue
# --- Catch Specific API Errors ---
except google.api_core.exceptions.ResourceExhausted as e:
error_message = f"⏳ Model `{model_name}` likely rate limited: {e}. Trying next model..."
st.warning(error_message)
last_error = e
last_error_message = error_message
time.sleep(3)
continue
except google.api_core.exceptions.ServiceUnavailable as e:
error_message = f"☁️ Model `{model_name}` unavailable: {e}. Trying next model..."
st.warning(error_message)
last_error = e
last_error_message = error_message
time.sleep(3)
continue
except (google.api_core.exceptions.InvalidArgument, ValueError) as e:
err_str = str(e).lower()
if "tool" in err_str or "function" in err_str or "retriever" in err_str:
error_message = f"πŸ”§ Model `{model_name}` might not support the requested tool or configuration: {e}. Trying next model..."
st.warning(error_message)
else:
error_message = f"🚫 Invalid argument for `{model_name}`: {e}. Trying next model..."
st.warning(error_message)
last_error = e
last_error_message = error_message
continue
except google.api_core.exceptions.GoogleAPIError as e:
error_message = f"🚫 API Error with `{model_name}`: {e}. Trying next model..."
st.warning(error_message)
last_error = e
last_error_message = error_message
continue
# --- Catch General Errors ---
except Exception as e:
error_message = f"❌ Unexpected error with `{model_name}`: {type(e).__name__} - {e}. Trying next model..."
st.warning(error_message)
last_error = e
last_error_message = error_message
continue
st.error("πŸ’₯ All attempts failed.")
final_error_message = "⚠️ Failed to generate ideas after trying all configured models."
if last_error:
final_error_message += f"\nLast known issue: {last_error_message}"
return final_error_message
# --- Streamlit App Interface ---
st.sidebar.title("Support This Tool")
st.sidebar.markdown(f"""
If you find this AI Content Idea Generator useful, please consider supporting its ongoing development and hosting costs.
[**πŸ’– Support on Patreon**]({PATREON_URL})
Your support helps keep this tool running and allows for future improvements!
""")
st.title("πŸš€ AI Niche Content Idea Generator")
st.markdown("Define your niche, audience, and format. Use **Advanced Mode** for potentially more current ideas!")
st.divider()
# --- User Inputs ---
with st.form("content_idea_form"):
st.subheader("Tell us about your content needs:")
niche = st.text_input("🎯 Enter your Niche:", placeholder="e.g., Sustainable Home Decor, Retro PC Gaming")
audience = st.text_area("πŸ‘₯ Describe your Target Audience:", placeholder="e.g., Eco-conscious millennials, 90s Gamers", height=100)
content_format_options = ["Blog Post Titles", "Social Media Captions", "YouTube Video Ideas", "Podcast Topics", "Newsletter Subjects", "TikTok Video Concepts", "LinkedIn Article Headlines"]
content_format = st.selectbox("✍️ Select Desired Content Format:", options=content_format_options)
num_ideas_slider = st.slider("πŸ”’ Number of ideas:", min_value=3, max_value=15, value=4)
advanced_mode_toggle = st.toggle("πŸ” Advanced Mode (Use Google Search)", value=False, help="May use Google Search for more current ideas. Works best with Gemini models.")
submitted = st.form_submit_button("✨ Generate Ideas!")
# --- Output ---
if submitted:
if not niche or not audience or not content_format:
st.warning("⚠️ Please fill in all the fields.")
elif api_key_configured:
mode_msg = " (Advanced Mode)" if advanced_mode_toggle else ""
with st.spinner(f"🧠 Thinking up brilliant ideas{mode_msg}..."):
generated_ideas = generate_content_ideas(
niche, audience, content_format, num_ideas_slider, advanced_mode=advanced_mode_toggle
)
st.divider()
st.subheader("πŸ’‘ Here are your content ideas:")
if generated_ideas and not generated_ideas.startswith(("⚠️", "πŸ’₯")):
st.markdown(generated_ideas)
st.success("Ideas generated successfully!")
st.markdown("---")
st.markdown(f"**Hope these ideas help!** If this tool saved you time or sparked creativity, consider [supporting the creator on Patreon]({PATREON_URL}).")
elif generated_ideas:
st.error(generated_ideas)
else:
st.error("An unexpected issue occurred. No ideas were generated or returned.")
else:
st.error("API Key is not configured.")