Create app.py
Browse files
app.py
ADDED
@@ -0,0 +1,214 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import os
|
2 |
+
import time
|
3 |
+
|
4 |
+
import streamlit as st
|
5 |
+
from google import genai
|
6 |
+
from google.genai.types import Tool, GenerateContentConfig, GoogleSearch
|
7 |
+
import google.api_core
|
8 |
+
|
9 |
+
# --- Configuration ---
|
10 |
+
st.set_page_config(
|
11 |
+
page_title="AI Niche Content Idea Generator",
|
12 |
+
page_icon="π‘",
|
13 |
+
layout="centered",
|
14 |
+
)
|
15 |
+
|
16 |
+
# --- Patreon Link ---
|
17 |
+
PATREON_URL = "https://www.patreon.com/cineAI"
|
18 |
+
|
19 |
+
# --- Model List ---
|
20 |
+
FALLBACK_MODELS = [
|
21 |
+
"gemini-2.0-flash-thinking-exp-01-21",
|
22 |
+
"gemini-2.5-flash-preview-04-17",
|
23 |
+
"gemini-1.5-flash",
|
24 |
+
"gemini-1.5-pro",
|
25 |
+
"gemini-2.0-flash",
|
26 |
+
"gemini-2.0-flash-lite",
|
27 |
+
"gemini-2.5-pro-preview-03-25",
|
28 |
+
]
|
29 |
+
|
30 |
+
# --- Authentication ---
|
31 |
+
api_key = ""
|
32 |
+
try:
|
33 |
+
api_key = st.secrets["GOOGLE_API_KEY"]
|
34 |
+
api_key_configured = True
|
35 |
+
except (KeyError, FileNotFoundError):
|
36 |
+
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.")
|
37 |
+
api_key_configured = False
|
38 |
+
st.stop()
|
39 |
+
|
40 |
+
# --- Helper Function for API Call with Fallback & Advanced Mode ---
|
41 |
+
def generate_content_ideas(niche, audience, content_format, num_ideas=4, advanced_mode=False):
|
42 |
+
"""Generates content ideas using Google Generative AI, with fallback and optional search."""
|
43 |
+
if not api_key_configured:
|
44 |
+
return "API Key not configured. Cannot proceed."
|
45 |
+
|
46 |
+
# --- Base Prompt ---
|
47 |
+
base_prompt = f"""
|
48 |
+
Act as an expert content strategist specializing in the niche: '{niche}'.
|
49 |
+
Your task is to generate {num_ideas} fresh, specific, and engaging content ideas tailored for the following target audience: '{audience}'.
|
50 |
+
The desired content format is: '{content_format}'.
|
51 |
+
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.
|
52 |
+
"""
|
53 |
+
|
54 |
+
# --- Enhance Prompt for Advanced Mode ---
|
55 |
+
if advanced_mode:
|
56 |
+
prompt = base_prompt + f"""
|
57 |
+
**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.
|
58 |
+
"""
|
59 |
+
else:
|
60 |
+
prompt = base_prompt + """
|
61 |
+
Generate the ideas based on your general knowledge of the niche and audience.
|
62 |
+
"""
|
63 |
+
|
64 |
+
# --- Add Formatting Instruction ---
|
65 |
+
prompt += """
|
66 |
+
Please format the output as a numbered list. Each idea should be concise but descriptive.
|
67 |
+
Example for niche 'Sustainable Gardening', audience 'Urban Beginners', format 'Blog Post Titles':
|
68 |
+
1. 5 Easy-to-Grow Vegetables for Your Tiny Balcony Garden
|
69 |
+
2. Composting in Apartments: A No-Smell Guide for City Dwellers
|
70 |
+
3. Choosing the Right Recycled Pots for Your Indoor Plants
|
71 |
+
4. How to Attract Pollinators to Your Urban Oasis
|
72 |
+
5. Watering Wisely: Saving Water in Your Container Garden
|
73 |
+
Now, generate the ideas for the requested niche, audience, and format:
|
74 |
+
"""
|
75 |
+
|
76 |
+
last_error = None
|
77 |
+
last_error_message = ""
|
78 |
+
|
79 |
+
# --- Tool Configuration ---
|
80 |
+
if advanced_mode:
|
81 |
+
google_search_tool = Tool(google_search=GoogleSearch())
|
82 |
+
config = GenerateContentConfig(
|
83 |
+
tools=[google_search_tool],
|
84 |
+
response_modalities=['TEXT'],
|
85 |
+
# system_instruction=system_instruction_google_search,
|
86 |
+
max_output_tokens=4100,
|
87 |
+
)
|
88 |
+
else:
|
89 |
+
config = None
|
90 |
+
|
91 |
+
tool_status_msg = " (Advanced Mode with Google Search π)" if advanced_mode else ""
|
92 |
+
|
93 |
+
for model_name in FALLBACK_MODELS:
|
94 |
+
st.info(f"π§ Attempting generation with model: `{model_name}`{tool_status_msg}...")
|
95 |
+
try:
|
96 |
+
model = genai.Client(api_key=api_key)
|
97 |
+
response = model.models.generate_content(
|
98 |
+
model=model_name,
|
99 |
+
contents=prompt,
|
100 |
+
config=config,
|
101 |
+
)
|
102 |
+
# --- Check for successful response (handle potential blocks) ---
|
103 |
+
if hasattr(response, 'text') and response.text:
|
104 |
+
st.success(f"β
Successfully generated ideas using `{model_name}`!")
|
105 |
+
return response.text
|
106 |
+
elif hasattr(response, 'prompt_feedback') and response.prompt_feedback.block_reason:
|
107 |
+
error_message = f"β οΈ Generation blocked by `{model_name}`. Reason: {response.prompt_feedback.block_reason.name}. Trying next model..."
|
108 |
+
st.warning(error_message)
|
109 |
+
last_error = BlockingIOError(f"{model_name}: Blocked - {response.prompt_feedback.block_reason.name}")
|
110 |
+
last_error_message = error_message
|
111 |
+
time.sleep(0.5)
|
112 |
+
continue
|
113 |
+
else:
|
114 |
+
error_message = f"β οΈ Generation failed with `{model_name}` (empty or invalid response structure). Trying next model..."
|
115 |
+
st.warning(error_message)
|
116 |
+
last_error = ValueError(f"{model_name}: Empty/Invalid response")
|
117 |
+
last_error_message = error_message
|
118 |
+
time.sleep(1)
|
119 |
+
continue
|
120 |
+
|
121 |
+
# --- Catch Specific API Errors ---
|
122 |
+
except google.api_core.exceptions.ResourceExhausted as e:
|
123 |
+
error_message = f"β³ Model `{model_name}` likely rate limited: {e}. Trying next model..."
|
124 |
+
st.warning(error_message)
|
125 |
+
last_error = e
|
126 |
+
last_error_message = error_message
|
127 |
+
time.sleep(3)
|
128 |
+
continue
|
129 |
+
except google.api_core.exceptions.ServiceUnavailable as e:
|
130 |
+
error_message = f"βοΈ Model `{model_name}` unavailable: {e}. Trying next model..."
|
131 |
+
st.warning(error_message)
|
132 |
+
last_error = e
|
133 |
+
last_error_message = error_message
|
134 |
+
time.sleep(3)
|
135 |
+
continue
|
136 |
+
except (google.api_core.exceptions.InvalidArgument, ValueError) as e:
|
137 |
+
err_str = str(e).lower()
|
138 |
+
if "tool" in err_str or "function" in err_str or "retriever" in err_str:
|
139 |
+
error_message = f"π§ Model `{model_name}` might not support the requested tool or configuration: {e}. Trying next model..."
|
140 |
+
st.warning(error_message)
|
141 |
+
else:
|
142 |
+
error_message = f"π« Invalid argument for `{model_name}`: {e}. Trying next model..."
|
143 |
+
st.warning(error_message)
|
144 |
+
last_error = e
|
145 |
+
last_error_message = error_message
|
146 |
+
continue
|
147 |
+
except google.api_core.exceptions.GoogleAPIError as e:
|
148 |
+
error_message = f"π« API Error with `{model_name}`: {e}. Trying next model..."
|
149 |
+
st.warning(error_message)
|
150 |
+
last_error = e
|
151 |
+
last_error_message = error_message
|
152 |
+
continue
|
153 |
+
# --- Catch General Errors ---
|
154 |
+
except Exception as e:
|
155 |
+
error_message = f"β Unexpected error with `{model_name}`: {type(e).__name__} - {e}. Trying next model..."
|
156 |
+
st.warning(error_message)
|
157 |
+
last_error = e
|
158 |
+
last_error_message = error_message
|
159 |
+
continue
|
160 |
+
|
161 |
+
st.error("π₯ All attempts failed.")
|
162 |
+
final_error_message = "β οΈ Failed to generate ideas after trying all configured models."
|
163 |
+
if last_error:
|
164 |
+
final_error_message += f"\nLast known issue: {last_error_message}"
|
165 |
+
|
166 |
+
return final_error_message
|
167 |
+
|
168 |
+
# --- Streamlit App Interface ---
|
169 |
+
st.sidebar.title("Support This Tool")
|
170 |
+
st.sidebar.markdown(f"""
|
171 |
+
If you find this AI Content Idea Generator useful, please consider supporting its ongoing development and hosting costs.
|
172 |
+
[**π Support on Patreon**]({PATREON_URL})
|
173 |
+
Your support helps keep this tool running and allows for future improvements!
|
174 |
+
""")
|
175 |
+
|
176 |
+
st.title("π AI Niche Content Idea Generator")
|
177 |
+
st.markdown("Define your niche, audience, and format. Use **Advanced Mode** for potentially more current ideas!")
|
178 |
+
st.divider()
|
179 |
+
|
180 |
+
# --- User Inputs ---
|
181 |
+
with st.form("content_idea_form"):
|
182 |
+
st.subheader("Tell us about your content needs:")
|
183 |
+
niche = st.text_input("π― Enter your Niche:", placeholder="e.g., Sustainable Home Decor, Retro PC Gaming")
|
184 |
+
audience = st.text_area("π₯ Describe your Target Audience:", placeholder="e.g., Eco-conscious millennials, 90s Gamers", height=100)
|
185 |
+
content_format_options = ["Blog Post Titles", "Social Media Captions", "YouTube Video Ideas", "Podcast Topics", "Newsletter Subjects", "TikTok Video Concepts", "LinkedIn Article Headlines"]
|
186 |
+
content_format = st.selectbox("βοΈ Select Desired Content Format:", options=content_format_options)
|
187 |
+
num_ideas_slider = st.slider("π’ Number of ideas:", min_value=3, max_value=15, value=4)
|
188 |
+
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.")
|
189 |
+
submitted = st.form_submit_button("β¨ Generate Ideas!")
|
190 |
+
|
191 |
+
# --- Output ---
|
192 |
+
if submitted:
|
193 |
+
if not niche or not audience or not content_format:
|
194 |
+
st.warning("β οΈ Please fill in all the fields.")
|
195 |
+
elif api_key_configured:
|
196 |
+
mode_msg = " (Advanced Mode)" if advanced_mode_toggle else ""
|
197 |
+
with st.spinner(f"π§ Thinking up brilliant ideas{mode_msg}..."):
|
198 |
+
generated_ideas = generate_content_ideas(
|
199 |
+
niche, audience, content_format, num_ideas_slider, advanced_mode=advanced_mode_toggle
|
200 |
+
)
|
201 |
+
|
202 |
+
st.divider()
|
203 |
+
st.subheader("π‘ Here are your content ideas:")
|
204 |
+
if generated_ideas and not generated_ideas.startswith(("β οΈ", "π₯")):
|
205 |
+
st.markdown(generated_ideas)
|
206 |
+
st.success("Ideas generated successfully!")
|
207 |
+
st.markdown("---")
|
208 |
+
st.markdown(f"**Hope these ideas help!** If this tool saved you time or sparked creativity, consider [supporting the creator on Patreon]({PATREON_URL}).")
|
209 |
+
elif generated_ideas:
|
210 |
+
st.error(generated_ideas)
|
211 |
+
else:
|
212 |
+
st.error("An unexpected issue occurred. No ideas were generated or returned.")
|
213 |
+
else:
|
214 |
+
st.error("API Key is not configured.")
|