File size: 10,100 Bytes
6c153a2
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
e801b3f
6c153a2
e801b3f
6c153a2
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
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
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
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.")