gajjukhan commited on
Commit
7a4e132
·
verified ·
1 Parent(s): cc98b5f

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +51 -261
app.py CHANGED
@@ -9,10 +9,7 @@ from crewai_tools import SerperDevTool, ScrapeWebsiteTool
9
  import gradio as gr
10
 
11
  # =============================================================================
12
- # API KEYS — Read from Hugging Face Space Secrets
13
- # Go to: Space → Settings → Variables and secrets → New secret
14
- # Secret name : OPENAI_API_KEY → your OpenAI key
15
- # Secret name : SERPER_API_KEY → your Serper key (serper.dev)
16
  # =============================================================================
17
  OPENAI_API_KEY = os.environ.get("OPENAI_API_KEY", "")
18
  SERPER_API_KEY = os.environ.get("SERPER_API_KEY", "")
@@ -29,7 +26,6 @@ if not SERPER_API_KEY:
29
  "Add it in Space → Settings → Variables and secrets."
30
  )
31
 
32
- # CrewAI and LangChain pick these up automatically from os.environ
33
  os.environ["OPENAI_API_KEY"] = OPENAI_API_KEY
34
  os.environ["SERPER_API_KEY"] = SERPER_API_KEY
35
 
@@ -45,13 +41,13 @@ llm = LLM(
45
  # =============================================================================
46
  # TOOLS
47
  # =============================================================================
48
- search_tool = SerperDevTool() # Finds URLs + short snippets via Serper
49
- scrape_tool = ScrapeWebsiteTool() # Reads full page content from those URLs
50
 
51
  # =============================================================================
52
  # MEMORY
53
  # =============================================================================
54
- MEMORY_DIR = "/tmp/crewai_memory" # /tmp is always writable on HF Spaces
55
  os.makedirs(MEMORY_DIR, exist_ok=True)
56
 
57
  EMBEDDER_CONFIG = {
@@ -68,17 +64,10 @@ EMBEDDER_CONFIG = {
68
  researcher = Agent(
69
  role="Senior Research Analyst",
70
  goal=(
71
- "Find the most accurate, up-to-date, and relevant information on the given topic. "
72
- "Always prioritize credible sources, cite key statistics, and highlight emerging trends."
73
  ),
74
- backstory=(
75
- "You are a veteran research analyst with 15 years of experience across technology, "
76
- "business, and science journalism. You have a reputation for digging deep into topics, "
77
- "cross-referencing multiple sources, and presenting findings in a clear, structured format. "
78
- "You never make assumptions — everything you report is evidence-backed. "
79
- "You organize research into clean bullet points with source context."
80
- ),
81
- tools=[search_tool, scrape_tool], # Serper finds URLs, ScrapeWebsite reads full content
82
  llm=llm,
83
  verbose=True,
84
  allow_delegation=False,
@@ -87,17 +76,8 @@ researcher = Agent(
87
 
88
  writer = Agent(
89
  role="Professional Content Writer",
90
- goal=(
91
- "Transform the research summary into a compelling, well-structured, and audience-appropriate article. "
92
- "Ensure the article flows naturally, uses clear headings, and maintains the requested tone throughout."
93
- ),
94
- backstory=(
95
- "You are an award-winning content writer with expertise in long-form journalism, technical writing, "
96
- "and digital publishing. You have written for Forbes, TechCrunch, and Harvard Business Review. "
97
- "You have a talent for making complex topics accessible without dumbing them down. "
98
- "You always start with a strong hook, use subheadings to guide the reader, "
99
- "and end with a memorable conclusion that reinforces the key message."
100
- ),
101
  llm=llm,
102
  verbose=True,
103
  allow_delegation=False,
@@ -106,17 +86,8 @@ writer = Agent(
106
 
107
  editor = Agent(
108
  role="Senior Editor",
109
- goal=(
110
- "Review and refine the article to ensure it is grammatically flawless, tonally consistent, "
111
- "logically structured, and ready for publication. Return only the final polished article."
112
- ),
113
- backstory=(
114
- "You are a meticulous senior editor with 20 years of experience at top-tier publications. "
115
- "You have an unmatched eye for inconsistency, passive voice overuse, redundant phrasing, "
116
- "and weak transitions. You tighten prose without stripping its personality. "
117
- "You ensure every paragraph earns its place and every sentence is purposeful. "
118
- "You never add commentary — you deliver only the clean, final article."
119
- ),
120
  llm=llm,
121
  verbose=True,
122
  allow_delegation=False,
@@ -126,84 +97,23 @@ editor = Agent(
126
  # =============================================================================
127
  # TASK BUILDER
128
  # =============================================================================
129
- def build_tasks(topic: str, audience: str, tone: str, length: str) -> list:
130
  research_task = Task(
131
- description=textwrap.dedent(f"""
132
- Conduct thorough research on the following topic:
133
- TOPIC: "{topic}"
134
-
135
- Your research should be targeted for this audience: {audience}
136
- Required depth: {length}
137
-
138
- Your deliverables:
139
- 1. A 2-sentence topic overview
140
- 2. 5-8 key facts or statistics with source context
141
- 3. 3-5 recent developments or trends (2023-2025)
142
- 4. 2-3 expert opinions or notable perspectives
143
- 5. A list of 3 key takeaways
144
-
145
- Format everything in clearly labelled bullet points.
146
- """),
147
- expected_output=(
148
- "A comprehensive, well-structured research summary in bullet points "
149
- "covering overview, key facts, recent developments, expert opinions, and takeaways."
150
- ),
151
  agent=researcher,
152
  )
153
 
154
  writing_task = Task(
155
- description=textwrap.dedent(f"""
156
- Using the research summary provided, write a complete article on:
157
- TOPIC: "{topic}"
158
-
159
- Specifications:
160
- - Target Audience: {audience}
161
- - Tone: {tone}
162
- - Length: {length}
163
-
164
- Structure:
165
- 1. Headline - compelling and specific
166
- 2. Introduction - hook + why this topic matters now
167
- 3. Section 1 with subheading - background / context
168
- 4. Section 2 with subheading - key developments or insights
169
- 5. Section 3 with subheading - implications or practical applications
170
- 6. Conclusion - summary + forward-looking statement
171
-
172
- Rules:
173
- - Do NOT use generic filler phrases like "In conclusion" or "It is worth noting"
174
- - Use active voice whenever possible
175
- - Every paragraph should add new value - no repetition
176
- """),
177
- expected_output=(
178
- "A complete, structured article with headline, introduction, three body sections "
179
- "with subheadings, and a conclusion - written in the requested tone for the specified audience."
180
- ),
181
  agent=writer,
182
  context=[research_task],
183
  )
184
 
185
  editing_task = Task(
186
- description=textwrap.dedent(f"""
187
- You are the final gatekeeper before publication.
188
- Review and edit the article for the following:
189
-
190
- Checklist:
191
- - Grammar and spelling are flawless
192
- - Tone is consistent and matches: {tone}
193
- - Audience appropriateness: {audience}
194
- - No redundant sentences or paragraphs
195
- - Transitions between sections are smooth
196
- - Headline is punchy and specific
197
- - Introduction hooks the reader immediately
198
- - Conclusion ends with impact
199
-
200
- IMPORTANT: Return ONLY the final polished article text.
201
- Do NOT include your editorial notes, checklist, or any commentary.
202
- """),
203
- expected_output=(
204
- "The final, publication-ready article - clean, polished, and complete. "
205
- "No editor notes or commentary included."
206
- ),
207
  agent=editor,
208
  context=[writing_task],
209
  )
@@ -213,20 +123,9 @@ def build_tasks(topic: str, audience: str, tone: str, length: str) -> list:
213
  # =============================================================================
214
  # CREW RUNNER
215
  # =============================================================================
216
- def run_crew(topic: str, audience: str, tone: str, length: str) -> str:
217
- if not topic or not topic.strip():
218
- return "Please enter a topic before generating."
219
-
220
- if len(topic.strip()) < 5:
221
- return "Topic is too short. Please be more descriptive (e.g. 'AI in healthcare 2025')."
222
-
223
- print(f"\n{'='*60}")
224
- print(f"Starting CrewAI Pipeline")
225
- print(f" Topic : {topic}")
226
- print(f" Audience : {audience}")
227
- print(f" Tone : {tone}")
228
- print(f" Length : {length}")
229
- print(f"{'='*60}\n")
230
 
231
  try:
232
  tasks = build_tasks(topic, audience, tone, length)
@@ -238,184 +137,75 @@ def run_crew(topic: str, audience: str, tone: str, length: str) -> str:
238
  verbose=True,
239
  memory=True,
240
  embedder=EMBEDDER_CONFIG,
241
- memory_config={
242
- "long_term": {
243
- "storage_path": MEMORY_DIR
244
- }
245
- },
246
  )
247
 
248
- print("\nAgent 1 (Researcher) starting...\n")
249
  result = crew.kickoff()
250
-
251
- print(f"\n{'='*60}")
252
- print("CrewAI pipeline complete!")
253
- print(f"{'='*60}\n")
254
-
255
  return str(result)
256
 
257
  except Exception as e:
258
- error_msg = f"An error occurred during pipeline execution:\n{str(e)}"
259
- print(error_msg)
260
- return error_msg
261
 
262
  # =============================================================================
263
- # GRADIO UI
264
  # =============================================================================
265
- with gr.Blocks(
266
- theme=gr.themes.Soft(primary_hue="blue", secondary_hue="slate"),
267
- title="Multi-Agent Article Generator",
268
- css="""
269
- .title-text { text-align: center; }
270
- .agent-badge { font-size: 0.85rem; color: #666; }
271
- footer { display: none !important; }
272
- """
273
- ) as demo:
274
 
275
- gr.Markdown(
276
- """
277
- <div class="title-text">
278
- <h1>Multi-Agent Article Generator</h1>
279
- <p style="color:#555;">Powered by <strong>CrewAI</strong> + <strong>GPT-4o</strong> + <strong>SerperDev</strong> + <strong>ScrapeWebsite</strong></p>
280
- </div>
281
-
282
- ---
283
-
284
- ### How it works
285
- `Researcher` searches the web + scrapes full pages for deep research →
286
- `Writer` drafts a structured article →
287
- `Editor` polishes and finalizes for publication
288
-
289
- > Generation takes **45-120 seconds**. Agent logs appear in the Space logs panel.
290
- """
291
- )
292
 
293
- with gr.Row(equal_height=False):
294
 
295
- with gr.Column(scale=1, min_width=280):
296
- gr.Markdown("### Settings")
297
-
298
- topic_input = gr.Textbox(
299
- label="Topic *",
300
- placeholder="e.g. The impact of AI agents on software development in 2025",
301
- lines=3,
302
- info="Be specific - better topic = better article."
303
- )
304
 
305
  audience_input = gr.Dropdown(
306
- label="Target Audience",
307
- choices=[
308
- "General Public",
309
- "Business Professionals",
310
- "Students & Learners",
311
- "Researchers & Academics",
312
- "Tech Enthusiasts"
313
- ],
314
  value="General Public",
315
- info="Shapes vocabulary and depth of explanation."
316
  )
317
 
318
  tone_input = gr.Dropdown(
319
- label="Writing Tone",
320
- choices=[
321
- "Informative",
322
- "Persuasive",
323
- "Conversational",
324
- "Academic",
325
- "Inspirational"
326
- ],
327
  value="Informative",
328
- info="Controls the style and voice of the article."
329
  )
330
 
331
  length_input = gr.Radio(
332
- label="Article Length",
333
- choices=[
334
- "Short (300-500 words)",
335
- "Medium (600-900 words)",
336
- "Long (1000-1400 words)"
337
- ],
338
- value="Medium (600-900 words)",
339
- )
340
-
341
- gr.Markdown("---")
342
-
343
- submit_btn = gr.Button(
344
- "Generate Article",
345
- variant="primary",
346
- size="lg"
347
- )
348
-
349
- clear_btn = gr.Button(
350
- "Clear",
351
- variant="secondary",
352
- size="sm"
353
- )
354
-
355
- gr.Markdown(
356
- """
357
- <div class="agent-badge">
358
- <b>Agents:</b><br>
359
- Researcher - web search + scrape full pages<br>
360
- Writer - draft structured article<br>
361
- Editor - polish + finalize
362
- </div>
363
- """
364
  )
365
 
366
- with gr.Column(scale=2):
367
- gr.Markdown("### Generated Article")
368
 
 
369
  output_box = gr.Textbox(
370
- label="",
371
- lines=35,
372
- placeholder=(
373
- "Your article will appear here once generation is complete.\n\n"
374
- "This takes 45-120 seconds.\n"
375
- "Watch the Space logs for live agent output."
376
- ),
377
- show_copy_button=True,
378
  interactive=False,
379
  )
380
 
381
- gr.Markdown("### Example Topics")
382
- gr.Examples(
383
- examples=[
384
- ["The future of AI agents in enterprise software", "Business Professionals", "Informative", "Medium (600-900 words)"],
385
- ["How large language models are changing education", "Students & Learners", "Conversational", "Short (300-500 words)"],
386
- ["Quantum computing: where are we in 2025?", "Tech Enthusiasts", "Informative", "Long (1000-1400 words)"],
387
- ["The ethics of autonomous AI decision-making", "Researchers & Academics", "Academic", "Long (1000-1400 words)"],
388
- ["Why every startup needs an AI strategy in 2025", "Business Professionals", "Persuasive", "Medium (600-900 words)"],
389
- ],
390
- inputs=[topic_input, audience_input, tone_input, length_input],
391
- label="Click any example to auto-fill the inputs"
392
- )
393
-
394
- gr.Markdown(
395
- """
396
- ---
397
- <div style="text-align:center; color:#888; font-size:0.85rem;">
398
- Built with <strong>CrewAI</strong> · <strong>LangChain</strong> · <strong>GPT-4o</strong> · <strong>Gradio</strong> · <strong>ScrapeWebsiteTool</strong>
399
- </div>
400
- """
401
- )
402
-
403
  submit_btn.click(
404
  fn=run_crew,
405
  inputs=[topic_input, audience_input, tone_input, length_input],
406
  outputs=output_box,
407
- api_name="generate",
408
  )
409
 
410
  clear_btn.click(
411
  fn=lambda: ("", ""),
412
  inputs=[],
413
- outputs=[topic_input, output_box]
414
  )
415
 
416
  # =============================================================================
417
- # LAUNCH
418
- # server_name="0.0.0.0" is required for Hugging Face Spaces
419
- # server_port=7860 is the default port HF Spaces expects
420
  # =============================================================================
421
- demo.launch(server_name="0.0.0.0", server_port=7860)
 
 
 
 
 
 
 
 
9
  import gradio as gr
10
 
11
  # =============================================================================
12
+ # API KEYS
 
 
 
13
  # =============================================================================
14
  OPENAI_API_KEY = os.environ.get("OPENAI_API_KEY", "")
15
  SERPER_API_KEY = os.environ.get("SERPER_API_KEY", "")
 
26
  "Add it in Space → Settings → Variables and secrets."
27
  )
28
 
 
29
  os.environ["OPENAI_API_KEY"] = OPENAI_API_KEY
30
  os.environ["SERPER_API_KEY"] = SERPER_API_KEY
31
 
 
41
  # =============================================================================
42
  # TOOLS
43
  # =============================================================================
44
+ search_tool = SerperDevTool()
45
+ scrape_tool = ScrapeWebsiteTool()
46
 
47
  # =============================================================================
48
  # MEMORY
49
  # =============================================================================
50
+ MEMORY_DIR = "/tmp/crewai_memory"
51
  os.makedirs(MEMORY_DIR, exist_ok=True)
52
 
53
  EMBEDDER_CONFIG = {
 
64
  researcher = Agent(
65
  role="Senior Research Analyst",
66
  goal=(
67
+ "Find the most accurate, up-to-date, and relevant information on the given topic."
 
68
  ),
69
+ backstory="Veteran research analyst with deep investigation skills.",
70
+ tools=[search_tool, scrape_tool],
 
 
 
 
 
 
71
  llm=llm,
72
  verbose=True,
73
  allow_delegation=False,
 
76
 
77
  writer = Agent(
78
  role="Professional Content Writer",
79
+ goal="Write a compelling structured article from research.",
80
+ backstory="Expert in long-form and technical writing.",
 
 
 
 
 
 
 
 
 
81
  llm=llm,
82
  verbose=True,
83
  allow_delegation=False,
 
86
 
87
  editor = Agent(
88
  role="Senior Editor",
89
+ goal="Polish article to publication quality.",
90
+ backstory="Meticulous editor ensuring clarity and consistency.",
 
 
 
 
 
 
 
 
 
91
  llm=llm,
92
  verbose=True,
93
  allow_delegation=False,
 
97
  # =============================================================================
98
  # TASK BUILDER
99
  # =============================================================================
100
+ def build_tasks(topic: str, audience: str, tone: str, length: str):
101
  research_task = Task(
102
+ description=f"Research topic: {topic}",
103
+ expected_output="Structured research summary",
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
104
  agent=researcher,
105
  )
106
 
107
  writing_task = Task(
108
+ description=f"Write article on: {topic}",
109
+ expected_output="Full article",
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
110
  agent=writer,
111
  context=[research_task],
112
  )
113
 
114
  editing_task = Task(
115
+ description="Edit article to final form",
116
+ expected_output="Final polished article",
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
117
  agent=editor,
118
  context=[writing_task],
119
  )
 
123
  # =============================================================================
124
  # CREW RUNNER
125
  # =============================================================================
126
+ def run_crew(topic: str, audience: str, tone: str, length: str):
127
+ if not topic.strip():
128
+ return "Please enter a topic."
 
 
 
 
 
 
 
 
 
 
 
129
 
130
  try:
131
  tasks = build_tasks(topic, audience, tone, length)
 
137
  verbose=True,
138
  memory=True,
139
  embedder=EMBEDDER_CONFIG,
140
+ memory_config={"long_term": {"storage_path": MEMORY_DIR}},
 
 
 
 
141
  )
142
 
 
143
  result = crew.kickoff()
 
 
 
 
 
144
  return str(result)
145
 
146
  except Exception as e:
147
+ return f"Error: {str(e)}"
 
 
148
 
149
  # =============================================================================
150
+ # UI
151
  # =============================================================================
152
+ with gr.Blocks() as demo:
 
 
 
 
 
 
 
 
153
 
154
+ gr.Markdown("# Multi-Agent Article Generator")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
155
 
156
+ with gr.Row():
157
 
158
+ with gr.Column():
159
+ topic_input = gr.Textbox(label="Topic", lines=3)
 
 
 
 
 
 
 
160
 
161
  audience_input = gr.Dropdown(
162
+ ["General Public", "Business", "Students"],
 
 
 
 
 
 
 
163
  value="General Public",
164
+ label="Audience",
165
  )
166
 
167
  tone_input = gr.Dropdown(
168
+ ["Informative", "Conversational", "Academic"],
 
 
 
 
 
 
 
169
  value="Informative",
170
+ label="Tone",
171
  )
172
 
173
  length_input = gr.Radio(
174
+ ["Short", "Medium", "Long"],
175
+ value="Medium",
176
+ label="Length",
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
177
  )
178
 
179
+ submit_btn = gr.Button("Generate")
180
+ clear_btn = gr.Button("Clear")
181
 
182
+ with gr.Column():
183
  output_box = gr.Textbox(
184
+ label="Output",
185
+ lines=30,
 
 
 
 
 
 
186
  interactive=False,
187
  )
188
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
189
  submit_btn.click(
190
  fn=run_crew,
191
  inputs=[topic_input, audience_input, tone_input, length_input],
192
  outputs=output_box,
 
193
  )
194
 
195
  clear_btn.click(
196
  fn=lambda: ("", ""),
197
  inputs=[],
198
+ outputs=[topic_input, output_box],
199
  )
200
 
201
  # =============================================================================
202
+ # LAUNCH (UPDATED FOR GRADIO 6)
 
 
203
  # =============================================================================
204
+ demo.launch(
205
+ server_name="0.0.0.0",
206
+ server_port=7860,
207
+ theme=gr.themes.Soft(primary_hue="blue", secondary_hue="slate"),
208
+ css="""
209
+ footer { display: none !important; }
210
+ """,
211
+ )