gajjukhan commited on
Commit
cc98b5f
·
verified ·
1 Parent(s): 42c03b6

Upload app.py

Browse files
Files changed (1) hide show
  1. app.py +421 -0
app.py ADDED
@@ -0,0 +1,421 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import textwrap
3
+
4
+ # ─── CrewAI core ─────────────────────────────────────────────────────────────
5
+ from crewai import Agent, Task, Crew, Process, LLM
6
+ from crewai_tools import SerperDevTool, ScrapeWebsiteTool
7
+
8
+ # ─── UI ──────────────────────────────────────────────────────────────────────
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", "")
19
+
20
+ if not OPENAI_API_KEY:
21
+ raise EnvironmentError(
22
+ "❌ OPENAI_API_KEY not found. "
23
+ "Add it in Space → Settings → Variables and secrets."
24
+ )
25
+
26
+ if not SERPER_API_KEY:
27
+ raise EnvironmentError(
28
+ "❌ SERPER_API_KEY not found. "
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
+
36
+ # =============================================================================
37
+ # LLM
38
+ # =============================================================================
39
+ llm = LLM(
40
+ model="gpt-4o-mini",
41
+ temperature=0.4,
42
+ max_tokens=2000,
43
+ )
44
+
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 = {
58
+ "provider": "openai",
59
+ "config": {
60
+ "model": "text-embedding-3-small",
61
+ "api_key": OPENAI_API_KEY,
62
+ },
63
+ }
64
+
65
+ # =============================================================================
66
+ # AGENTS
67
+ # =============================================================================
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,
85
+ max_iter=5,
86
+ )
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,
104
+ max_iter=4,
105
+ )
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,
123
+ max_iter=3,
124
+ )
125
+
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
+ )
210
+
211
+ return [research_task, writing_task, editing_task]
212
+
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)
233
+
234
+ crew = Crew(
235
+ agents=[researcher, writer, editor],
236
+ tasks=tasks,
237
+ process=Process.sequential,
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)