eaglelandsonce commited on
Commit
2f239c7
Β·
verified Β·
1 Parent(s): ee41f8e

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +328 -0
app.py ADDED
@@ -0,0 +1,328 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from crewai import Agent, Task, Crew
2
+ import gradio as gr
3
+ import asyncio
4
+ from typing import List, Dict, Any, Generator
5
+ from langchain_openai import ChatOpenAI
6
+ import queue
7
+ import threading
8
+ import os
9
+
10
+ class AgentMessageQueue:
11
+ def __init__(self):
12
+ self.message_queue = queue.Queue()
13
+ self.last_agent = None
14
+
15
+ def add_message(self, message: Dict):
16
+ print(f"Adding message to queue: {message}") # Debug print
17
+ self.message_queue.put(message)
18
+
19
+ def get_messages(self) -> List[Dict]:
20
+ messages = []
21
+ while not self.message_queue.empty():
22
+ messages.append(self.message_queue.get())
23
+ return messages
24
+
25
+ class ArticleCrew:
26
+ def __init__(self, api_key: str = None):
27
+ self.api_key = api_key
28
+ self.message_queue = AgentMessageQueue()
29
+ self.planner = None
30
+ self.writer = None
31
+ self.editor = None
32
+ self.current_agent = None
33
+ self.final_article = None
34
+
35
+ def initialize_agents(self, topic: str):
36
+ if not self.api_key:
37
+ raise ValueError("OpenAI API key is required")
38
+
39
+ os.environ["OPENAI_API_KEY"] = self.api_key
40
+ llm = ChatOpenAI(temperature=0.7, model="gpt-4")
41
+
42
+ self.planner = Agent(
43
+ role="Content Planner",
44
+ goal=f"Plan engaging and factually accurate content on {topic}",
45
+ backstory="Expert content planner with focus on creating engaging outlines",
46
+ allow_delegation=False,
47
+ verbose=True,
48
+ llm=llm
49
+ )
50
+
51
+ self.writer = Agent(
52
+ role="Content Writer",
53
+ goal=f"Write insightful and factually accurate piece about {topic}",
54
+ backstory="Expert content writer with focus on engaging articles",
55
+ allow_delegation=False,
56
+ verbose=True,
57
+ llm=llm
58
+ )
59
+
60
+ self.editor = Agent(
61
+ role="Editor",
62
+ goal="Polish and refine the article",
63
+ backstory="Expert editor with eye for detail and clarity",
64
+ allow_delegation=False,
65
+ verbose=True,
66
+ llm=llm
67
+ )
68
+
69
+ def create_tasks(self, topic: str) -> List[Task]:
70
+ planner_task = Task(
71
+ description=f"""Create a detailed content plan for an article about {topic} by:
72
+ 1. Prioritizing the latest trends, key players, and noteworthy news
73
+ 2. Identifying the target audience, considering their interests and pain points
74
+ 3. Developing a detailed content outline including introduction, key points, and call to action
75
+ 4. Including SEO keywords and relevant data or sources""",
76
+ expected_output="A comprehensive content plan with outline, keywords, and target audience analysis",
77
+ agent=self.planner
78
+ )
79
+
80
+ writer_task = Task(
81
+ description="""Based on the provided content plan:
82
+ 1. Use the content plan to craft a compelling blog post
83
+ 2. Incorporate SEO keywords naturally
84
+ 3. Ensure sections/subtitles are properly named in an engaging manner
85
+ 4. Create proper structure with introduction, body, and conclusion
86
+ 5. Proofread for grammatical errors""",
87
+ expected_output="A well-written article draft following the content plan",
88
+ agent=self.writer
89
+ )
90
+
91
+ editor_task = Task(
92
+ description="""Review the written article by:
93
+ 1. Checking for clarity and coherence
94
+ 2. Correcting any grammatical errors and typos
95
+ 3. Ensuring consistent tone and style
96
+ 4. Verifying proper formatting and structure""",
97
+ expected_output="A polished, final version of the article ready for publication",
98
+ agent=self.editor
99
+ )
100
+
101
+ return [planner_task, writer_task, editor_task]
102
+
103
+ async def process_article(self, topic: str) -> Generator[List[Dict], None, None]:
104
+ def add_agent_messages(agent_name: str, tasks: str, emoji: str = "πŸ€–"):
105
+ # Add agent header
106
+ self.message_queue.add_message({
107
+ "role": "assistant",
108
+ "content": agent_name,
109
+ "metadata": {"title": f"{emoji} {agent_name}"}
110
+ })
111
+
112
+ # Add task description
113
+ self.message_queue.add_message({
114
+ "role": "assistant",
115
+ "content": tasks,
116
+ "metadata": {"title": f"πŸ“‹ Task for {agent_name}"}
117
+ })
118
+
119
+ def setup_next_agent(current_agent: str) -> None:
120
+ agent_sequence = {
121
+ "Content Planner": ("Content Writer", """1. Use the content plan to craft a compelling blog post
122
+ 2. Incorporate SEO keywords naturally
123
+ 3. Ensure sections/subtitles are properly named in an engaging manner
124
+ 4. Create proper structure with introduction, body, and conclusion
125
+ 5. Proofread for grammatical errors"""),
126
+
127
+ "Content Writer": ("Editor", """1. Review the article for clarity and coherence
128
+ 2. Check for grammatical errors and typos
129
+ 3. Ensure consistent tone and style
130
+ 4. Verify proper formatting and structure""")
131
+ }
132
+
133
+ if current_agent in agent_sequence:
134
+ next_agent, tasks = agent_sequence[current_agent]
135
+ self.current_agent = next_agent
136
+ add_agent_messages(next_agent, tasks)
137
+
138
+
139
+ def task_callback(task_output) -> None:
140
+ print(f"Task callback received: {task_output}") # Debug print
141
+
142
+ # Extract content from raw output
143
+ raw_output = task_output.raw
144
+ if "## Final Answer:" in raw_output:
145
+ content = raw_output.split("## Final Answer:")[1].strip()
146
+ else:
147
+ content = raw_output.strip()
148
+
149
+ # Handle the output based on current agent
150
+ if self.current_agent == "Editor":
151
+ # Don't show editor's output with metadata
152
+ # Instead, show completion message and final article
153
+ self.message_queue.add_message({
154
+ "role": "assistant",
155
+ "content": "Final article is ready!",
156
+ "metadata": {"title": "πŸ“ Final Article"}
157
+ })
158
+
159
+ # Convert common markdown patterns to Gradio-compatible markdown
160
+ formatted_content = content
161
+ # Ensure proper spacing for headers
162
+ formatted_content = formatted_content.replace("\n#", "\n\n#")
163
+ # Ensure proper spacing for lists
164
+ formatted_content = formatted_content.replace("\n-", "\n\n-")
165
+ formatted_content = formatted_content.replace("\n*", "\n\n*")
166
+ formatted_content = formatted_content.replace("\n1.", "\n\n1.")
167
+ # Ensure proper spacing for paragraphs
168
+ formatted_content = formatted_content.replace("\n\n\n", "\n\n")
169
+
170
+ # Add the final article content without metadata
171
+ self.message_queue.add_message({
172
+ "role": "assistant",
173
+ "content": formatted_content
174
+ })
175
+ else:
176
+ # For other agents, show their output with metadata
177
+ self.message_queue.add_message({
178
+ "role": "assistant",
179
+ "content": content,
180
+ "metadata": {"title": f"✨ Output from {self.current_agent}"}
181
+ })
182
+ # Setup next agent
183
+ setup_next_agent(self.current_agent)
184
+
185
+ def step_callback(output: Any) -> None:
186
+ print(f"Step callback received: {output}") # Debug print
187
+ # We'll only use step_callback for logging purposes now
188
+ pass
189
+
190
+ try:
191
+ self.initialize_agents(topic)
192
+ self.current_agent = "Content Planner"
193
+
194
+ # Start process
195
+ yield [{
196
+ "role": "assistant",
197
+ "content": "Starting work on your article...",
198
+ "metadata": {"title": "πŸš€ Process Started"}
199
+ }]
200
+
201
+ # Initialize first agent
202
+ add_agent_messages("Content Planner",
203
+ """1. Prioritize the latest trends, key players, and noteworthy news
204
+ 2. Identify the target audience, considering their interests and pain points
205
+ 3. Develop a detailed content outline including introduction, key points, and call to action
206
+ 4. Include SEO keywords and relevant data or sources""")
207
+
208
+ crew = Crew(
209
+ agents=[self.planner, self.writer, self.editor],
210
+ tasks=self.create_tasks(topic),
211
+ verbose=True,
212
+ step_callback=step_callback,
213
+ task_callback=task_callback
214
+ )
215
+
216
+ def run_crew():
217
+ try:
218
+ crew.kickoff()
219
+ except Exception as e:
220
+ print(f"Error in crew execution: {str(e)}") # Debug print
221
+ self.message_queue.add_message({
222
+ "role": "assistant",
223
+ "content": f"An error occurred: {str(e)}",
224
+ "metadata": {"title": "❌ Error"}
225
+ })
226
+
227
+ thread = threading.Thread(target=run_crew)
228
+ thread.start()
229
+
230
+ while thread.is_alive() or not self.message_queue.message_queue.empty():
231
+ messages = self.message_queue.get_messages()
232
+ if messages:
233
+ print(f"Yielding messages: {messages}") # Debug print
234
+ yield messages
235
+ await asyncio.sleep(0.1)
236
+
237
+ except Exception as e:
238
+ print(f"Error in process_article: {str(e)}") # Debug print
239
+ yield [{
240
+ "role": "assistant",
241
+ "content": f"An error occurred: {str(e)}",
242
+ "metadata": {"title": "❌ Error"}
243
+ }]
244
+
245
+ # [Rest of the code remains the same]
246
+
247
+
248
+ def create_demo():
249
+ article_crew = None
250
+
251
+ with gr.Blocks(theme=gr.themes.Soft()) as demo:
252
+ gr.Markdown("# πŸ“ AI Article Writing Crew")
253
+
254
+ openai_api_key = gr.Textbox(
255
+ label='OpenAI API Key',
256
+ type='password',
257
+ placeholder='Enter your OpenAI API key...',
258
+ interactive=True
259
+ )
260
+
261
+ chatbot = gr.Chatbot(
262
+ label="Writing Process",
263
+ height=700,
264
+ type="messages",
265
+ show_label=True,
266
+ visible=False,
267
+ avatar_images=(None, "https://avatars.githubusercontent.com/u/170677839?v=4"),
268
+ render_markdown=True # Enable markdown rendering
269
+ )
270
+
271
+ with gr.Row(equal_height=True):
272
+ topic = gr.Textbox(
273
+ label="Article Topic",
274
+ placeholder="Enter topic...",
275
+ scale=4,
276
+ visible=False
277
+ )
278
+ btn = gr.Button("Write Article", variant="primary", scale=1, visible=False)
279
+
280
+ async def process_input(topic, history, api_key):
281
+ nonlocal article_crew
282
+ if not api_key:
283
+ history = history or []
284
+ history.append({
285
+ "role": "assistant",
286
+ "content": "Please provide an OpenAI API key.",
287
+ "metadata": {"title": "❌ Error"}
288
+ })
289
+ yield history
290
+ return
291
+
292
+ if article_crew is None:
293
+ article_crew = ArticleCrew(api_key=api_key)
294
+
295
+ history = history or []
296
+ history.append({"role": "user", "content": f"Write an article about: {topic}"})
297
+ yield history
298
+
299
+ try:
300
+ async for messages in article_crew.process_article(topic):
301
+ history.extend(messages)
302
+ yield history
303
+ except Exception as e:
304
+ history.append({
305
+ "role": "assistant",
306
+ "content": f"An error occurred: {str(e)}",
307
+ "metadata": {"title": "❌ Error"}
308
+ })
309
+ yield history
310
+
311
+ def show_interface():
312
+ return {
313
+ openai_api_key: gr.Textbox(visible=False),
314
+ chatbot: gr.Chatbot(visible=True),
315
+ topic: gr.Textbox(visible=True),
316
+ btn: gr.Button(visible=True)
317
+ }
318
+
319
+ openai_api_key.submit(show_interface, None, [openai_api_key, chatbot, topic, btn])
320
+ btn.click(process_input, [topic, chatbot, openai_api_key], [chatbot])
321
+ topic.submit(process_input, [topic, chatbot, openai_api_key], [chatbot])
322
+
323
+ return demo
324
+
325
+ if __name__ == "__main__":
326
+ demo = create_demo()
327
+ demo.queue()
328
+ demo.launch(debug=True)