decodingdatascience commited on
Commit
b170190
Β·
verified Β·
1 Parent(s): 250c084

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +40 -37
app.py CHANGED
@@ -5,20 +5,28 @@ 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",
24
  goal="Deliver sharp, data-driven market insights for the brand/product.",
@@ -46,7 +54,7 @@ creative_content_creator = Agent(
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.",
@@ -55,9 +63,7 @@ social_copywriter = Agent(
55
  verbose=False,
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}"
63
 
@@ -109,9 +115,7 @@ def run_marketing_crew(product_brand: str, target_audience: str, objective: str)
109
  )
110
  return crew.kickoff()
111
 
112
- # ----------------------------
113
- # Helpers
114
- # ----------------------------
115
  def _first_n_points(text: str, n: int = 5):
116
  lines = [l.strip() for l in text.splitlines() if l.strip()]
117
  bullets = []
@@ -132,9 +136,11 @@ def _hashtags(csv_tags: str) -> str:
132
  def _truncate_chars(s: str, max_chars: int) -> str:
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}."
140
  pts = "\n".join([f"- {p}" for p in _first_n_points(strategy, 5)])
@@ -185,9 +191,7 @@ def tpl_article(strategy, brand, audience, objective, hashtags, max_words=800):
185
  words = text.split()
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):
193
  limits = {
@@ -217,9 +221,7 @@ Requirements:
217
  crew = Crew(agents=[social_copywriter], tasks=[task], verbose=False)
218
  return crew.kickoff()
219
 
220
- # ----------------------------
221
- # Generation wrapper
222
- # ----------------------------
223
  PLATFORMS = ["LinkedIn", "X (Twitter)", "Article"] # Reduced for better UX
224
 
225
  def generate(product_brand, target_audience, objective,
@@ -237,31 +239,31 @@ def generate(product_brand, target_audience, objective,
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 = """
@@ -295,8 +297,10 @@ with gr.Blocks(title="DDS Marketing Crew β†’ Social Content", theme=theme, css=C
295
 
296
  with gr.Group(elem_classes="card"):
297
  platform = gr.Dropdown(choices=PLATFORMS, value="LinkedIn", label="Platform")
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
 
@@ -325,9 +329,8 @@ with gr.Blocks(title="DDS Marketing Crew β†’ Social Content", theme=theme, css=C
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)
 
5
  from datetime import datetime
6
  from pathlib import Path
7
  import os
8
+ from openai import OpenAI # make sure `openai` is in requirements.txt
9
 
10
+ # ---- OpenAI setup (explicit) -------------------------------------------------
11
  OPENAI_API_KEY = os.getenv("OPENAI_API_KEY")
12
+ if not OPENAI_API_KEY:
13
+ raise RuntimeError(
14
+ "OPENAI_API_KEY is not set. In your Hugging Face Space, go to "
15
+ "Settings β†’ Secrets and add OPENAI_API_KEY."
16
+ )
17
 
18
+ # Optional: allow swapping models via secret/env
19
+ OPENAI_MODEL = os.getenv("OPENAI_MODEL", "gpt-4.1-mini")
20
+ openai_client = OpenAI(api_key=OPENAI_API_KEY) # CrewAI also reads env automatically
21
 
22
+ # ---- Logo helper --------------------------------------------------------------
23
  def to_raw_github(url: str) -> str:
24
  # Accepts both blob and raw URLs; converts blob β†’ raw
25
  return url.replace("https://github.com/", "https://raw.githubusercontent.com/").replace("/blob/", "/")
26
 
27
  LOGO_URL = to_raw_github("https://github.com/Decoding-Data-Science/airesidency/blob/main/dds_logo.jpg")
28
 
29
+ # ---- Agents ------------------------------------------------------------------
 
 
30
  lead_market_analyst = Agent(
31
  role="Lead Market Analyst",
32
  goal="Deliver sharp, data-driven market insights for the brand/product.",
 
54
  verbose=True,
55
  )
56
 
57
+ # Optional social copywriter (only used if toggled)
58
  social_copywriter = Agent(
59
  role="Social Copywriter",
60
  goal="Turn strategy into platform-appropriate copy with strong hooks and clear CTAs.",
 
63
  verbose=False,
64
  )
65
 
66
+ # ---- Tasks & Crew -------------------------------------------------------------
 
 
67
  def run_marketing_crew(product_brand: str, target_audience: str, objective: str) -> str:
68
  topic = f"{product_brand} | Audience: {target_audience} | Objective: {objective}"
69
 
 
115
  )
116
  return crew.kickoff()
117
 
118
+ # ---- Helpers -----------------------------------------------------------------
 
 
119
  def _first_n_points(text: str, n: int = 5):
120
  lines = [l.strip() for l in text.splitlines() if l.strip()]
121
  bullets = []
 
136
  def _truncate_chars(s: str, max_chars: int) -> str:
137
  return s if len(s) <= max_chars else s[:max_chars - 1] + "…"
138
 
139
+ def _slugify(name: str) -> str:
140
+ # Replace non-word chars with underscores and trim
141
+ return re.sub(r"\W+", "_", (name or "").lower()).strip("_")
142
+
143
+ # ---- Lightweight templates (no extra LLM call) --------------------------------
144
  def tpl_linkedin(strategy, brand, audience, objective, hashtags, max_words=180):
145
  hook = f"{brand}: a sharper path to {objective} for {audience}."
146
  pts = "\n".join([f"- {p}" for p in _first_n_points(strategy, 5)])
 
191
  words = text.split()
192
  return (" ".join(words[:max_words]) + "…") if len(words) > max_words else text
193
 
194
+ # ---- Optional LLM copywriter (uses the Social Copywriter agent) ---------------
 
 
195
  def llm_copywriter(strategy_text, brand, audience, objective, tone, platform,
196
  hashtags, li_words, tweet_chars, article_words):
197
  limits = {
 
221
  crew = Crew(agents=[social_copywriter], tasks=[task], verbose=False)
222
  return crew.kickoff()
223
 
224
+ # ---- Generation wrapper -------------------------------------------------------
 
 
225
  PLATFORMS = ["LinkedIn", "X (Twitter)", "Article"] # Reduced for better UX
226
 
227
  def generate(product_brand, target_audience, objective,
 
239
  social = llm_copywriter(strategy, product_brand, target_audience, objective,
240
  tone, platform, hashtags,
241
  li_max_words, tweet_max_chars, article_max_words)
242
+
243
+ name_map = {"LinkedIn": "linkedin", "X (Twitter)": "tweet", "Article": "article"}
244
+ safe = _slugify(product_brand)
245
+ fname = f"{name_map.get(platform, 'post')}_{safe}.md"
246
+ out_path = Path(fname).resolve()
247
+ out_path.write_text(social, encoding="utf-8")
248
+ return strategy, social, str(out_path)
249
+
250
  else:
251
+ safe = _slugify(product_brand)
252
  if platform == "LinkedIn":
253
  social = tpl_linkedin(strategy, product_brand, target_audience, objective, hashtags, li_max_words)
254
+ fname = f"linkedin_{safe}.md"
255
  elif platform == "X (Twitter)":
256
  social = tpl_tweet(strategy, product_brand, target_audience, objective, hashtags, tweet_max_chars)
257
+ fname = f"tweet_{safe}.txt"
258
  else: # Article
259
  social = tpl_article(strategy, product_brand, target_audience, objective, hashtags, article_max_words)
260
+ fname = f"article_{safe}.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
+ # ---- Theming & Layout (2 columns with header) --------------------------------
 
 
 
 
 
 
 
 
 
267
  theme = gr.themes.Soft(primary_hue="indigo", neutral_hue="slate")
268
 
269
  CUSTOM_CSS = """
 
297
 
298
  with gr.Group(elem_classes="card"):
299
  platform = gr.Dropdown(choices=PLATFORMS, value="LinkedIn", label="Platform")
300
+ tone = gr.Dropdown(
301
+ choices=["Professional", "Friendly", "Bold", "Educational", "Conversational"],
302
+ value="Professional", label="Tone"
303
+ )
304
  hashtags = gr.Textbox(label="Hashtags (comma separated)", placeholder="ai, generativeai, datascience")
305
  use_llm = gr.Checkbox(value=False, label="Use LLM Copywriter (higher fidelity)")
306
 
 
329
  outputs=[strategy_md, social_tb, download_file]
330
  )
331
 
332
+ # ---- Space-friendly launch ----------------------------------------------------
333
  if __name__ == "__main__":
 
334
  host = os.getenv("HOST", "0.0.0.0")
335
  port = int(os.getenv("PORT", "7860"))
336
  demo.launch(server_name=host, server_port=port)