update
Browse files
app.py
CHANGED
|
@@ -41,7 +41,7 @@ except Exception as e:
|
|
| 41 |
@tool
|
| 42 |
def generate_quote_tool(niche: str, style: str) -> str:
|
| 43 |
"""
|
| 44 |
-
Generate a unique inspirational quote using the HybridQuoteGenerator.
|
| 45 |
|
| 46 |
Args:
|
| 47 |
niche: The category of the quote (e.g. Motivation, Fitness, Mindfulness).
|
|
@@ -81,7 +81,7 @@ def search_pexels_video_tool(style: str, niche: str) -> dict:
|
|
| 81 |
|
| 82 |
Args:
|
| 83 |
style: Visual style (e.g. Cinematic, Nature, Urban, Minimal, Abstract).
|
| 84 |
-
niche: Content niche (e.g. Motivation,
|
| 85 |
|
| 86 |
Returns:
|
| 87 |
A dictionary with:
|
|
@@ -91,7 +91,8 @@ def search_pexels_video_tool(style: str, niche: str) -> dict:
|
|
| 91 |
- pexels_url: The Pexels page URL (or None).
|
| 92 |
- error: Optional error message on failure.
|
| 93 |
"""
|
| 94 |
-
|
|
|
|
| 95 |
"Motivation": {
|
| 96 |
"Cinematic": ["person climbing mountain", "running sunrise", "achievement success"],
|
| 97 |
"Nature": ["sunrise mountain peak", "ocean waves powerful", "forest light"],
|
|
@@ -143,7 +144,29 @@ def search_pexels_video_tool(style: str, niche: str) -> dict:
|
|
| 143 |
},
|
| 144 |
}
|
| 145 |
|
| 146 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 147 |
|
| 148 |
try:
|
| 149 |
if not PEXELS_API_KEY:
|
|
@@ -156,8 +179,8 @@ def search_pexels_video_tool(style: str, niche: str) -> dict:
|
|
| 156 |
}
|
| 157 |
|
| 158 |
headers = {"Authorization": PEXELS_API_KEY}
|
| 159 |
-
query = random.choice(queries)
|
| 160 |
|
|
|
|
| 161 |
url = (
|
| 162 |
f"https://api.pexels.com/videos/search"
|
| 163 |
f"?query={query}&per_page=15&orientation=portrait"
|
|
@@ -245,6 +268,7 @@ def create_quote_video_tool(
|
|
| 245 |
|
| 246 |
try:
|
| 247 |
print(f"π Processing on Modal (fast!) with text_style={text_style}...")
|
|
|
|
| 248 |
response = requests.post(
|
| 249 |
modal_endpoint,
|
| 250 |
json={
|
|
@@ -298,7 +322,8 @@ def create_quote_video_tool(
|
|
| 298 |
@tool
|
| 299 |
def get_trending_topics_tool(platform: str, niche: str, max_topics: int = 6) -> dict:
|
| 300 |
"""
|
| 301 |
-
Suggest trending content angles and hooks for short-form quote videos
|
|
|
|
| 302 |
|
| 303 |
Args:
|
| 304 |
platform: Main platform (e.g. 'TikTok', 'Instagram Reels', 'YouTube Shorts', or 'General').
|
|
@@ -320,10 +345,20 @@ def get_trending_topics_tool(platform: str, niche: str, max_topics: int = 6) ->
|
|
| 320 |
You are helping a creator make short quote videos.
|
| 321 |
|
| 322 |
Platform: {platform}
|
| 323 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 324 |
|
| 325 |
Task:
|
| 326 |
-
- Propose {max_topics} SPECIFIC trending-style topics/angles that would perform well on {platform}.
|
| 327 |
- Think in terms of what actually shows up on TikTok/Reels/Shorts: concrete micro-themes and patterns, not vague ideas.
|
| 328 |
|
| 329 |
For each topic, include:
|
|
@@ -332,7 +367,7 @@ For each topic, include:
|
|
| 332 |
|
| 333 |
Constraints:
|
| 334 |
- No generic motivational fluff like "follow your dreams" or "believe in yourself".
|
| 335 |
-
- Make it feel current: you can use language like "soft life", "discipline era", "glow up", "reset routine"
|
| 336 |
- Keep everything under 20 words per topic name and per hook.
|
| 337 |
|
| 338 |
Return a JSON object with EXACTLY this structure:
|
|
@@ -446,10 +481,10 @@ def mcp_agent_pipeline(
|
|
| 446 |
MAIN PIPELINE: uses smolagents CodeAgent.run to plan & call tools.
|
| 447 |
|
| 448 |
The agent:
|
| 449 |
-
- calls
|
| 450 |
-
- calls
|
|
|
|
| 451 |
- calls create_quote_video_tool for each variation
|
| 452 |
-
- returns JSON with status_log + video_paths
|
| 453 |
"""
|
| 454 |
base_log = ["π€ **MCP AGENT RUN**"]
|
| 455 |
|
|
@@ -469,66 +504,100 @@ def mcp_agent_pipeline(
|
|
| 469 |
base_prefix = os.path.join(output_dir, f"agent_{timestamp}_v")
|
| 470 |
|
| 471 |
user_task = f"""
|
| 472 |
-
You are an autonomous Python agent helping
|
|
|
|
|
|
|
| 473 |
|
| 474 |
-
|
| 475 |
-
Visual Style: {style}
|
| 476 |
-
Text style for quotes: {text_style}
|
| 477 |
-
Number of variations: {num_variations}
|
| 478 |
|
| 479 |
-
|
|
|
|
|
|
|
|
|
|
| 480 |
|
| 481 |
-
|
| 482 |
-
- Returns a single
|
| 483 |
|
| 484 |
-
|
| 485 |
- Returns a dict with:
|
| 486 |
- "success": bool
|
| 487 |
- "video_url": str or None
|
| 488 |
|
| 489 |
-
|
| 490 |
video_url: str,
|
| 491 |
quote_text: str,
|
| 492 |
output_path: str,
|
| 493 |
text_style: str = "classic_center"
|
| 494 |
) -> dict
|
| 495 |
-
- Writes a video file to output_path and returns
|
| 496 |
- "success": bool
|
| 497 |
- "output_path": str or None
|
| 498 |
|
| 499 |
-
|
| 500 |
-
|
| 501 |
-
|
| 502 |
-
|
| 503 |
-
|
| 504 |
-
|
| 505 |
-
|
| 506 |
-
|
| 507 |
-
|
| 508 |
-
|
| 509 |
-
|
| 510 |
-
-
|
| 511 |
-
-
|
| 512 |
-
|
| 513 |
-
|
| 514 |
-
|
| 515 |
-
|
| 516 |
-
|
| 517 |
-
|
| 518 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 519 |
|
| 520 |
{{
|
| 521 |
-
"status_log": "multi-line human readable
|
| 522 |
-
"video_paths": [
|
| 523 |
-
"{base_prefix}1.mp4",
|
| 524 |
-
"... only paths that actually succeeded ..."
|
| 525 |
-
]
|
| 526 |
}}
|
| 527 |
|
| 528 |
CRITICAL:
|
| 529 |
-
- Do NOT
|
| 530 |
-
- Do NOT
|
| 531 |
-
- Do NOT print anything
|
| 532 |
"""
|
| 533 |
|
| 534 |
agent_result = agent.run(user_task)
|
|
@@ -670,17 +739,17 @@ with gr.Blocks(
|
|
| 670 |
|
| 671 |
**Key Features:**
|
| 672 |
- π Short, non-repeating Gemini quotes (per niche history)
|
|
|
|
| 673 |
- π€ smolagents CodeAgent for tool planning
|
| 674 |
- π Optional MCP client integration
|
| 675 |
- π₯ Modal for fast video rendering
|
| 676 |
- π
°οΈ Text style controls (font & placement)
|
| 677 |
-
- π₯ Trending topics / angle helper
|
| 678 |
"""
|
| 679 |
)
|
| 680 |
|
| 681 |
with gr.Accordion("πΈ Example Gallery - Recent Videos", open=True):
|
| 682 |
gr.Markdown(
|
| 683 |
-
"See what
|
| 684 |
)
|
| 685 |
|
| 686 |
with gr.Row():
|
|
@@ -828,11 +897,11 @@ with gr.Blocks(
|
|
| 828 |
---
|
| 829 |
### β¨ Features
|
| 830 |
- π Gemini-powered, short non-repeating quotes (per niche)
|
|
|
|
| 831 |
- π¨ Multiple aesthetic video & text layouts
|
| 832 |
- β‘ Modal-accelerated rendering
|
| 833 |
- π€ smolagents CodeAgent for autonomous tool-calling
|
| 834 |
- π Optional MCP integration via MCPClient
|
| 835 |
-
- π₯ Trend helper to shape topics & hooks
|
| 836 |
|
| 837 |
### π Hackathon: MCP 1st Birthday
|
| 838 |
**Track:** Track 2 - MCP in Action
|
|
|
|
| 41 |
@tool
|
| 42 |
def generate_quote_tool(niche: str, style: str) -> str:
|
| 43 |
"""
|
| 44 |
+
Generate a unique, short inspirational quote using the HybridQuoteGenerator.
|
| 45 |
|
| 46 |
Args:
|
| 47 |
niche: The category of the quote (e.g. Motivation, Fitness, Mindfulness).
|
|
|
|
| 81 |
|
| 82 |
Args:
|
| 83 |
style: Visual style (e.g. Cinematic, Nature, Urban, Minimal, Abstract).
|
| 84 |
+
niche: Content niche or free-text theme (e.g. Motivation, Soft Life Mornings).
|
| 85 |
|
| 86 |
Returns:
|
| 87 |
A dictionary with:
|
|
|
|
| 91 |
- pexels_url: The Pexels page URL (or None).
|
| 92 |
- error: Optional error message on failure.
|
| 93 |
"""
|
| 94 |
+
# ---- Canonical mapping (your original curated niches) ----
|
| 95 |
+
canonical_strategies = {
|
| 96 |
"Motivation": {
|
| 97 |
"Cinematic": ["person climbing mountain", "running sunrise", "achievement success"],
|
| 98 |
"Nature": ["sunrise mountain peak", "ocean waves powerful", "forest light"],
|
|
|
|
| 144 |
},
|
| 145 |
}
|
| 146 |
|
| 147 |
+
# Normalize style so we don't crash on unexpected input
|
| 148 |
+
normalized_style = style or "Cinematic"
|
| 149 |
+
normalized_style = normalized_style.strip().title()
|
| 150 |
+
if normalized_style not in ["Cinematic", "Nature", "Urban", "Minimal", "Abstract"]:
|
| 151 |
+
normalized_style = "Cinematic"
|
| 152 |
+
|
| 153 |
+
# Decide queries:
|
| 154 |
+
# 1) If niche is one of the canonical categories -> use curated mapping
|
| 155 |
+
# 2) Otherwise, treat niche as free-text theme and build dynamic queries
|
| 156 |
+
if niche in canonical_strategies:
|
| 157 |
+
queries = canonical_strategies[niche].get(normalized_style, ["aesthetic nature"])
|
| 158 |
+
else:
|
| 159 |
+
base = (niche or "").lower().strip()
|
| 160 |
+
style_word = normalized_style.lower()
|
| 161 |
+
if not base:
|
| 162 |
+
base = "aesthetic vertical"
|
| 163 |
+
|
| 164 |
+
queries = [
|
| 165 |
+
f"{base} {style_word} aesthetic vertical video",
|
| 166 |
+
f"{base} b-roll {style_word}",
|
| 167 |
+
f"{base} lifestyle aesthetic",
|
| 168 |
+
f"{base} cinematic b-roll",
|
| 169 |
+
]
|
| 170 |
|
| 171 |
try:
|
| 172 |
if not PEXELS_API_KEY:
|
|
|
|
| 179 |
}
|
| 180 |
|
| 181 |
headers = {"Authorization": PEXELS_API_KEY}
|
|
|
|
| 182 |
|
| 183 |
+
query = random.choice(queries)
|
| 184 |
url = (
|
| 185 |
f"https://api.pexels.com/videos/search"
|
| 186 |
f"?query={query}&per_page=15&orientation=portrait"
|
|
|
|
| 268 |
|
| 269 |
try:
|
| 270 |
print(f"π Processing on Modal (fast!) with text_style={text_style}...")
|
| 271 |
+
# Note: modal_video_processing.py currently ignores text_style; it's safe to send it
|
| 272 |
response = requests.post(
|
| 273 |
modal_endpoint,
|
| 274 |
json={
|
|
|
|
| 322 |
@tool
|
| 323 |
def get_trending_topics_tool(platform: str, niche: str, max_topics: int = 6) -> dict:
|
| 324 |
"""
|
| 325 |
+
Suggest trending content angles and hooks for short-form quote videos,
|
| 326 |
+
tightly aligned with the given niche.
|
| 327 |
|
| 328 |
Args:
|
| 329 |
platform: Main platform (e.g. 'TikTok', 'Instagram Reels', 'YouTube Shorts', or 'General').
|
|
|
|
| 345 |
You are helping a creator make short quote videos.
|
| 346 |
|
| 347 |
Platform: {platform}
|
| 348 |
+
Creator niche: {niche}
|
| 349 |
+
|
| 350 |
+
All suggested topics MUST be tightly aligned with this niche.
|
| 351 |
+
|
| 352 |
+
Examples of alignment:
|
| 353 |
+
- If niche = Mindfulness β topics about calm lifestyle, awareness, mental clarity, soft-life energy, nervous system regulation.
|
| 354 |
+
- If niche = Motivation β topics about discipline era, glow up routines, self-improvement, consistency, habit stacking.
|
| 355 |
+
- If niche = Love & Relationships β topics about healing, attachment, secure love, boundaries, heartbreak recovery.
|
| 356 |
+
- If niche = Stoicism β topics about emotional resilience, fate, control vs. acceptance, inner strength.
|
| 357 |
+
- If niche = Leadership β topics about leading teams, decision-making, emotional intelligence at work.
|
| 358 |
+
- If niche = Business/Entrepreneurship β topics about founders, failure, shipping, building in public, risk.
|
| 359 |
|
| 360 |
Task:
|
| 361 |
+
- Propose {max_topics} SPECIFIC trending-style topics/angles that would perform well on {platform} for THIS niche.
|
| 362 |
- Think in terms of what actually shows up on TikTok/Reels/Shorts: concrete micro-themes and patterns, not vague ideas.
|
| 363 |
|
| 364 |
For each topic, include:
|
|
|
|
| 367 |
|
| 368 |
Constraints:
|
| 369 |
- No generic motivational fluff like "follow your dreams" or "believe in yourself".
|
| 370 |
+
- Make it feel current: you can use language like "soft life", "discipline era", "glow up", "reset routine", etc., only if it fits the niche.
|
| 371 |
- Keep everything under 20 words per topic name and per hook.
|
| 372 |
|
| 373 |
Return a JSON object with EXACTLY this structure:
|
|
|
|
| 481 |
MAIN PIPELINE: uses smolagents CodeAgent.run to plan & call tools.
|
| 482 |
|
| 483 |
The agent:
|
| 484 |
+
- calls get_trending_topics_tool to pick a trend
|
| 485 |
+
- calls generate_quote_tool with style influenced by that trend
|
| 486 |
+
- calls search_pexels_video_tool using the TRENDING TOPIC as niche (free-text support)
|
| 487 |
- calls create_quote_video_tool for each variation
|
|
|
|
| 488 |
"""
|
| 489 |
base_log = ["π€ **MCP AGENT RUN**"]
|
| 490 |
|
|
|
|
| 504 |
base_prefix = os.path.join(output_dir, f"agent_{timestamp}_v")
|
| 505 |
|
| 506 |
user_task = f"""
|
| 507 |
+
You are an autonomous Python agent helping a creator generate short vertical quote videos that follow TikTok-style trends.
|
| 508 |
+
|
| 509 |
+
You MUST follow these steps exactly, using the provided TOOLS:
|
| 510 |
|
| 511 |
+
AVAILABLE TOOLS (with signatures):
|
|
|
|
|
|
|
|
|
|
| 512 |
|
| 513 |
+
1. get_trending_topics_tool(platform: str, niche: str, max_topics: int) -> dict
|
| 514 |
+
- Returns a dict with:
|
| 515 |
+
- "topics": list of {{"topic": str, "hook": str}}
|
| 516 |
+
- "notes": str
|
| 517 |
|
| 518 |
+
2. generate_quote_tool(niche: str, style: str) -> str
|
| 519 |
+
- Returns a single short quote as plain text.
|
| 520 |
|
| 521 |
+
3. search_pexels_video_tool(style: str, niche: str) -> dict
|
| 522 |
- Returns a dict with:
|
| 523 |
- "success": bool
|
| 524 |
- "video_url": str or None
|
| 525 |
|
| 526 |
+
4. create_quote_video_tool(
|
| 527 |
video_url: str,
|
| 528 |
quote_text: str,
|
| 529 |
output_path: str,
|
| 530 |
text_style: str = "classic_center"
|
| 531 |
) -> dict
|
| 532 |
+
- Writes a video file to output_path and returns:
|
| 533 |
- "success": bool
|
| 534 |
- "output_path": str or None
|
| 535 |
|
| 536 |
+
RUNTIME PARAMETERS:
|
| 537 |
+
|
| 538 |
+
- base_niche = "{niche}"
|
| 539 |
+
- base_style = "{style}"
|
| 540 |
+
- text_style = "{text_style}"
|
| 541 |
+
- num_variations = {num_variations}
|
| 542 |
+
- base_output_prefix = "{base_prefix}"
|
| 543 |
+
|
| 544 |
+
STEP-BY-STEP PLAN (YOU MUST FOLLOW):
|
| 545 |
+
|
| 546 |
+
1. Call get_trending_topics_tool with:
|
| 547 |
+
- platform = "TikTok"
|
| 548 |
+
- niche = base_niche
|
| 549 |
+
- max_topics = 5
|
| 550 |
+
|
| 551 |
+
From the returned dict:
|
| 552 |
+
- If "topics" is non-empty:
|
| 553 |
+
trending_topic = topics[0]["topic"]
|
| 554 |
+
trending_hook = topics[0]["hook"]
|
| 555 |
+
Otherwise:
|
| 556 |
+
trending_topic = base_niche
|
| 557 |
+
trending_hook = ""
|
| 558 |
+
|
| 559 |
+
2. Call generate_quote_tool with:
|
| 560 |
+
- niche = base_niche
|
| 561 |
+
- style = base_style + " β trend: " + trending_topic
|
| 562 |
+
|
| 563 |
+
Store the result as:
|
| 564 |
+
- quote_text
|
| 565 |
+
|
| 566 |
+
3. For i from 1 to num_variations (inclusive):
|
| 567 |
+
a) Call search_pexels_video_tool with:
|
| 568 |
+
style = base_style
|
| 569 |
+
niche = trending_topic
|
| 570 |
+
Let this result be search_result.
|
| 571 |
+
b) If search_result["success"] is True AND search_result["video_url"] is a non-empty string:
|
| 572 |
+
- Set:
|
| 573 |
+
output_path = base_output_prefix + str(i) + ".mp4"
|
| 574 |
+
- Call create_quote_video_tool with:
|
| 575 |
+
video_url = search_result["video_url"]
|
| 576 |
+
quote_text = quote_text
|
| 577 |
+
output_path = output_path
|
| 578 |
+
text_style = text_style
|
| 579 |
+
Let this be video_result.
|
| 580 |
+
- If video_result["success"] is True AND video_result["output_path"] is a non-empty string:
|
| 581 |
+
append video_result["output_path"] to a Python list called final_video_paths.
|
| 582 |
+
Otherwise, skip this variation.
|
| 583 |
+
|
| 584 |
+
4. Build a multi-line, human-readable string called status_log summarizing:
|
| 585 |
+
- base_niche, base_style, text_style
|
| 586 |
+
- The chosen trending_topic and trending_hook (or that you fell back to base_niche)
|
| 587 |
+
- The final quote_text
|
| 588 |
+
- How many variations succeeded vs how many were attempted
|
| 589 |
+
|
| 590 |
+
5. Return ONLY the following JSON object as your final answer (no markdown, no backticks):
|
| 591 |
|
| 592 |
{{
|
| 593 |
+
"status_log": "your multi-line human readable log here",
|
| 594 |
+
"video_paths": ["... list of successful output_path strings from final_video_paths ..."]
|
|
|
|
|
|
|
|
|
|
| 595 |
}}
|
| 596 |
|
| 597 |
CRITICAL:
|
| 598 |
+
- Do NOT include any other keys.
|
| 599 |
+
- Do NOT return Python objects; everything must be JSON-serializable.
|
| 600 |
+
- Do NOT print anything else beyond that single JSON object.
|
| 601 |
"""
|
| 602 |
|
| 603 |
agent_result = agent.run(user_task)
|
|
|
|
| 739 |
|
| 740 |
**Key Features:**
|
| 741 |
- π Short, non-repeating Gemini quotes (per niche history)
|
| 742 |
+
- π₯ Trending topics actually drive the quote + visuals
|
| 743 |
- π€ smolagents CodeAgent for tool planning
|
| 744 |
- π Optional MCP client integration
|
| 745 |
- π₯ Modal for fast video rendering
|
| 746 |
- π
°οΈ Text style controls (font & placement)
|
|
|
|
| 747 |
"""
|
| 748 |
)
|
| 749 |
|
| 750 |
with gr.Accordion("πΈ Example Gallery - Recent Videos", open=True):
|
| 751 |
gr.Markdown(
|
| 752 |
+
"See what has been generated. Auto-updates after each run."
|
| 753 |
)
|
| 754 |
|
| 755 |
with gr.Row():
|
|
|
|
| 897 |
---
|
| 898 |
### β¨ Features
|
| 899 |
- π Gemini-powered, short non-repeating quotes (per niche)
|
| 900 |
+
- π₯ Trend-aware: topics influence quotes + videos
|
| 901 |
- π¨ Multiple aesthetic video & text layouts
|
| 902 |
- β‘ Modal-accelerated rendering
|
| 903 |
- π€ smolagents CodeAgent for autonomous tool-calling
|
| 904 |
- π Optional MCP integration via MCPClient
|
|
|
|
| 905 |
|
| 906 |
### π Hackathon: MCP 1st Birthday
|
| 907 |
**Track:** Track 2 - MCP in Action
|