decodingdatascience commited on
Commit
d480446
Β·
verified Β·
1 Parent(s): e3edf42

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +31 -38
app.py CHANGED
@@ -1,32 +1,23 @@
1
- # app.py β€” Hugging Face Spaces (Gradio) for DDS Strategy β†’ Social Generator
2
- # Two-column UI, DDS logo, reduced dropdown (LinkedIn / X / Article)
3
- # Secrets: set OPENAI_API_KEY in Spaces -> Settings -> Secrets
4
-
5
- import os
6
  import re
7
  from datetime import datetime
8
  from pathlib import Path
 
9
 
10
- import gradio as gr
11
- from crewai import Agent, Task, Crew
12
 
13
- # --- Optional: ensure key exists (CrewAI will pick it up) ---
14
- OPENAI_API_KEY = os.getenv("OPENAI_API_KEY", "")
15
 
16
- # --- DDS logo (raw GitHub URL conversion) ---
17
  def to_raw_github(url: str) -> str:
 
18
  return url.replace("https://github.com/", "https://raw.githubusercontent.com/").replace("/blob/", "/")
19
 
20
  LOGO_URL = to_raw_github("https://github.com/Decoding-Data-Science/airesidency/blob/main/dds_logo.jpg")
21
 
22
- # --- Helper: safe filename slug ---
23
- def slugify(s: str) -> str:
24
- # replace non-word chars with underscores, collapse repeats, strip edges
25
- slug = re.sub(r"\W+", "_", (s or "").lower())
26
- return slug.strip("_") or "post"
27
-
28
  # ----------------------------
29
- # Agents (same logic)
30
  # ----------------------------
31
  lead_market_analyst = Agent(
32
  role="Lead Market Analyst",
@@ -55,7 +46,7 @@ creative_content_creator = Agent(
55
  verbose=True,
56
  )
57
 
58
- # Optional: focused social copywriter (used only if toggled)
59
  social_copywriter = Agent(
60
  role="Social Copywriter",
61
  goal="Turn strategy into platform-appropriate copy with strong hooks and clear CTAs.",
@@ -65,7 +56,7 @@ social_copywriter = Agent(
65
  )
66
 
67
  # ----------------------------
68
- # Core crew (unchanged)
69
  # ----------------------------
70
  def run_marketing_crew(product_brand: str, target_audience: str, objective: str) -> str:
71
  topic = f"{product_brand} | Audience: {target_audience} | Objective: {objective}"
@@ -116,7 +107,7 @@ def run_marketing_crew(product_brand: str, target_audience: str, objective: str)
116
  tasks=[market_analysis_task, strategy_task, creative_task],
117
  verbose=True
118
  )
119
- return crew.kickoff() # full text output
120
 
121
  # ----------------------------
122
  # Helpers
@@ -142,7 +133,7 @@ def _truncate_chars(s: str, max_chars: int) -> str:
142
  return s if len(s) <= max_chars else s[:max_chars - 1] + "…"
143
 
144
  # ----------------------------
145
- # Templates (no extra LLM call)
146
  # ----------------------------
147
  def tpl_linkedin(strategy, brand, audience, objective, hashtags, max_words=180):
148
  hook = f"{brand}: a sharper path to {objective} for {audience}."
@@ -195,7 +186,7 @@ def tpl_article(strategy, brand, audience, objective, hashtags, max_words=800):
195
  return (" ".join(words[:max_words]) + "…") if len(words) > max_words else text
196
 
197
  # ----------------------------
198
- # Optional LLM copywriter (reuses Social Copywriter agent)
199
  # ----------------------------
200
  def llm_copywriter(strategy_text, brand, audience, objective, tone, platform,
201
  hashtags, li_words, tweet_chars, article_words):
@@ -241,42 +232,41 @@ def generate(product_brand, target_audience, objective,
241
  # 1) Run the main Crew once
242
  strategy = run_marketing_crew(product_brand, target_audience, objective)
243
 
244
- product_slug = slugify(product_brand)
245
-
246
- # 2) Copy creation
247
- if use_llm and OPENAI_API_KEY:
248
  social = llm_copywriter(strategy, product_brand, target_audience, objective,
249
  tone, platform, hashtags,
250
  li_max_words, tweet_max_chars, article_max_words)
251
  else:
252
  if platform == "LinkedIn":
253
  social = tpl_linkedin(strategy, product_brand, target_audience, objective, hashtags, li_max_words)
254
- fname = f"linkedin_{product_slug}.md"
255
  elif platform == "X (Twitter)":
256
  social = tpl_tweet(strategy, product_brand, target_audience, objective, hashtags, tweet_max_chars)
257
- fname = f"tweet_{product_slug}.txt"
258
  else: # Article
259
  social = tpl_article(strategy, product_brand, target_audience, objective, hashtags, article_max_words)
260
- fname = f"article_{product_slug}.md"
261
 
262
  out_path = Path(fname).resolve()
263
  out_path.write_text(social, encoding="utf-8")
264
  return strategy, social, str(out_path)
265
 
266
- # Save LLM result
267
  name_map = {"LinkedIn": "linkedin", "X (Twitter)": "tweet", "Article": "article"}
268
- fname = f"{name_map.get(platform,'post')}_{product_slug}.md"
269
  out_path = Path(fname).resolve()
270
  out_path.write_text(social, encoding="utf-8")
271
  return strategy, social, str(out_path)
272
 
273
  # ----------------------------
274
- # Theming & Layout (two columns + header)
275
  # ----------------------------
276
  theme = gr.themes.Soft(primary_hue="indigo", neutral_hue="slate")
 
277
  CUSTOM_CSS = """
278
  #header {display:flex; align-items:center; gap:16px; padding:10px 14px; border-radius:12px;
279
- background: linear-gradient(90deg, #eef2ff, #f8fafc); border:1px solid #e5e7eb; margin-bottom:8px;}
280
  #header img {width:44px; height:44px; object-fit:contain; border-radius:8px;}
281
  #header .title {font-weight:700; font-size:18px; color:#111827;}
282
  #header .subtitle {font-size:13px; color:#6b7280;}
@@ -290,8 +280,8 @@ with gr.Blocks(title="DDS Marketing Crew β†’ Social Content", theme=theme, css=C
290
  gr.Image(value=LOGO_URL, show_label=False, interactive=False, height=48, width=48)
291
  gr.HTML("""
292
  <div>
293
- <div class="title">Decoding Data Science β€” Strategy β†’ Social Generator</div>
294
- <div class="subtitle">Run Analyst β†’ Strategist β†’ Creator, then produce LinkedIn / X / Article.</div>
295
  </div>
296
  """)
297
 
@@ -308,7 +298,7 @@ with gr.Blocks(title="DDS Marketing Crew β†’ Social Content", theme=theme, css=C
308
  tone = gr.Dropdown(choices=["Professional", "Friendly", "Bold", "Educational", "Conversational"],
309
  value="Professional", label="Tone")
310
  hashtags = gr.Textbox(label="Hashtags (comma separated)", placeholder="ai, generativeai, datascience")
311
- use_llm = gr.Checkbox(value=False, label="Use LLM Copywriter (requires OPENAI_API_KEY)")
312
 
313
  with gr.Group(elem_classes="card"):
314
  li_max_words = gr.Slider(100, 350, value=180, step=10, label="LinkedIn max words")
@@ -335,6 +325,9 @@ with gr.Blocks(title="DDS Marketing Crew β†’ Social Content", theme=theme, css=C
335
  outputs=[strategy_md, social_tb, download_file]
336
  )
337
 
338
- # HF Spaces will start Gradio automatically; leaving default launch is fine.
339
  if __name__ == "__main__":
340
- demo.launch()
 
 
 
 
1
+ # app.py
2
+ from crewai import Agent, Task, Crew
3
+ import gradio as gr
 
 
4
  import re
5
  from datetime import datetime
6
  from pathlib import Path
7
+ import os
8
 
9
+ OPENAI_API_KEY = os.getenv("OPENAI_API_KEY")
 
10
 
 
 
11
 
12
+ # --- Logo: convert GitHub page URL to raw if needed ---
13
  def to_raw_github(url: str) -> str:
14
+ # Accepts both blob and raw URLs; converts blob β†’ raw
15
  return url.replace("https://github.com/", "https://raw.githubusercontent.com/").replace("/blob/", "/")
16
 
17
  LOGO_URL = to_raw_github("https://github.com/Decoding-Data-Science/airesidency/blob/main/dds_logo.jpg")
18
 
 
 
 
 
 
 
19
  # ----------------------------
20
+ # Agents
21
  # ----------------------------
22
  lead_market_analyst = Agent(
23
  role="Lead Market Analyst",
 
46
  verbose=True,
47
  )
48
 
49
+ # Optional: focused social copywriter (only if toggled)
50
  social_copywriter = Agent(
51
  role="Social Copywriter",
52
  goal="Turn strategy into platform-appropriate copy with strong hooks and clear CTAs.",
 
56
  )
57
 
58
  # ----------------------------
59
+ # Core crew
60
  # ----------------------------
61
  def run_marketing_crew(product_brand: str, target_audience: str, objective: str) -> str:
62
  topic = f"{product_brand} | Audience: {target_audience} | Objective: {objective}"
 
107
  tasks=[market_analysis_task, strategy_task, creative_task],
108
  verbose=True
109
  )
110
+ return crew.kickoff()
111
 
112
  # ----------------------------
113
  # Helpers
 
133
  return s if len(s) <= max_chars else s[:max_chars - 1] + "…"
134
 
135
  # ----------------------------
136
+ # Lightweight templates (no extra LLM call)
137
  # ----------------------------
138
  def tpl_linkedin(strategy, brand, audience, objective, hashtags, max_words=180):
139
  hook = f"{brand}: a sharper path to {objective} for {audience}."
 
186
  return (" ".join(words[:max_words]) + "…") if len(words) > max_words else text
187
 
188
  # ----------------------------
189
+ # Optional LLM copywriter
190
  # ----------------------------
191
  def llm_copywriter(strategy_text, brand, audience, objective, tone, platform,
192
  hashtags, li_words, tweet_chars, article_words):
 
232
  # 1) Run the main Crew once
233
  strategy = run_marketing_crew(product_brand, target_audience, objective)
234
 
235
+ # 2) Copy route
236
+ if use_llm:
 
 
237
  social = llm_copywriter(strategy, product_brand, target_audience, objective,
238
  tone, platform, hashtags,
239
  li_max_words, tweet_max_chars, article_max_words)
240
  else:
241
  if platform == "LinkedIn":
242
  social = tpl_linkedin(strategy, product_brand, target_audience, objective, hashtags, li_max_words)
243
+ fname = f"linkedin_{re.sub(r'\\W+','_',product_brand.lower())}.md"
244
  elif platform == "X (Twitter)":
245
  social = tpl_tweet(strategy, product_brand, target_audience, objective, hashtags, tweet_max_chars)
246
+ fname = f"tweet_{re.sub(r'\\W+','_',product_brand.lower())}.txt"
247
  else: # Article
248
  social = tpl_article(strategy, product_brand, target_audience, objective, hashtags, article_max_words)
249
+ fname = f"article_{re.sub(r'\\W+','_',product_brand.lower())}.md"
250
 
251
  out_path = Path(fname).resolve()
252
  out_path.write_text(social, encoding="utf-8")
253
  return strategy, social, str(out_path)
254
 
255
+ # Save LLM result with reasonable extension
256
  name_map = {"LinkedIn": "linkedin", "X (Twitter)": "tweet", "Article": "article"}
257
+ fname = f"{name_map.get(platform,'post')}_{re.sub(r'\\W+','_',product_brand.lower())}.md"
258
  out_path = Path(fname).resolve()
259
  out_path.write_text(social, encoding="utf-8")
260
  return strategy, social, str(out_path)
261
 
262
  # ----------------------------
263
+ # Theming & Layout (2 columns with header)
264
  # ----------------------------
265
  theme = gr.themes.Soft(primary_hue="indigo", neutral_hue="slate")
266
+
267
  CUSTOM_CSS = """
268
  #header {display:flex; align-items:center; gap:16px; padding:10px 14px; border-radius:12px;
269
+ background: linear-gradient(90deg, #eef2ff, #f8fafc); border:1px solid #e5e7eb;}
270
  #header img {width:44px; height:44px; object-fit:contain; border-radius:8px;}
271
  #header .title {font-weight:700; font-size:18px; color:#111827;}
272
  #header .subtitle {font-size:13px; color:#6b7280;}
 
280
  gr.Image(value=LOGO_URL, show_label=False, interactive=False, height=48, width=48)
281
  gr.HTML("""
282
  <div>
283
+ <div class="title">Decoding Data Science β€” Marketing Strategy β†’ Social Generator</div>
284
+ <div class="subtitle">Run the Analyst β†’ Strategist β†’ Creator pipeline, then produce a platform-ready post.</div>
285
  </div>
286
  """)
287
 
 
298
  tone = gr.Dropdown(choices=["Professional", "Friendly", "Bold", "Educational", "Conversational"],
299
  value="Professional", label="Tone")
300
  hashtags = gr.Textbox(label="Hashtags (comma separated)", placeholder="ai, generativeai, datascience")
301
+ use_llm = gr.Checkbox(value=False, label="Use LLM Copywriter (higher fidelity)")
302
 
303
  with gr.Group(elem_classes="card"):
304
  li_max_words = gr.Slider(100, 350, value=180, step=10, label="LinkedIn max words")
 
325
  outputs=[strategy_md, social_tb, download_file]
326
  )
327
 
328
+ # Space-friendly launch
329
  if __name__ == "__main__":
330
+ # Space runners set HOST/PORT; default to 0.0.0.0:7860 for local
331
+ host = os.getenv("HOST", "0.0.0.0")
332
+ port = int(os.getenv("PORT", "7860"))
333
+ demo.launch(server_name=host, server_port=port)