ysharma HF staff commited on
Commit
26c0317
β€’
1 Parent(s): 4c845a1

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +302 -0
app.py ADDED
@@ -0,0 +1,302 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+ from crewai import Agent, Task, Crew
3
+ from crewai_tools import ScrapeWebsiteTool
4
+ import os
5
+ import queue
6
+ import threading
7
+ import asyncio
8
+ from typing import List, Dict, Generator
9
+
10
+ class SupportMessageQueue:
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}")
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 SupportCrew:
26
+ def __init__(self, api_key: str = None):
27
+ self.api_key = api_key
28
+ self.message_queue = SupportMessageQueue()
29
+ self.support_agent = None
30
+ self.qa_agent = None
31
+ self.current_agent = None
32
+ self.scrape_tool = None
33
+
34
+ def initialize_agents(self, website_url: str):
35
+ if not self.api_key:
36
+ raise ValueError("OpenAI API key is required")
37
+
38
+ os.environ["OPENAI_API_KEY"] = self.api_key
39
+ self.scrape_tool = ScrapeWebsiteTool(website_url=website_url)
40
+
41
+ self.support_agent = Agent(
42
+ role="Senior Support Representative",
43
+ goal="Be the most friendly and helpful support representative in your team",
44
+ backstory=(
45
+ "You work at crewAI and are now working on providing support to customers. "
46
+ "You need to make sure that you provide the best support! "
47
+ "Make sure to provide full complete answers, and make no assumptions."
48
+ ),
49
+ allow_delegation=False,
50
+ verbose=True
51
+ )
52
+
53
+ self.qa_agent = Agent(
54
+ role="Support Quality Assurance Specialist",
55
+ goal="Get recognition for providing the best support quality assurance in your team",
56
+ backstory=(
57
+ "You work at crewAI and are now working with your team on customer requests "
58
+ "ensuring that the support representative is providing the best support possible. "
59
+ "You need to make sure that the support representative is providing full "
60
+ "complete answers, and make no assumptions."
61
+ ),
62
+ verbose=True
63
+ )
64
+
65
+ def create_tasks(self, inquiry: str) -> List[Task]:
66
+ inquiry_resolution = Task(
67
+ description=(
68
+ f"A customer just reached out with a super important ask:\n{inquiry}\n\n"
69
+ "Make sure to use everything you know to provide the best support possible. "
70
+ "You must strive to provide a complete and accurate response to the customer's inquiry."
71
+ ),
72
+ expected_output=(
73
+ "A detailed, informative response to the customer's inquiry that addresses "
74
+ "all aspects of their question.\n"
75
+ "The response should include references to everything you used to find the answer, "
76
+ "including external data or solutions. Ensure the answer is complete, "
77
+ "leaving no questions unanswered, and maintain a helpful and friendly tone throughout."
78
+ ),
79
+ tools=[self.scrape_tool],
80
+ agent=self.support_agent
81
+ )
82
+
83
+ quality_assurance_review = Task(
84
+ description=(
85
+ "Review the response drafted by the Senior Support Representative for the customer's inquiry. "
86
+ "Ensure that the answer is comprehensive, accurate, and adheres to the "
87
+ "high-quality standards expected for customer support.\n"
88
+ "Verify that all parts of the customer's inquiry have been addressed "
89
+ "thoroughly, with a helpful and friendly tone.\n"
90
+ "Check for references and sources used to find the information, "
91
+ "ensuring the response is well-supported and leaves no questions unanswered."
92
+ ),
93
+ expected_output=(
94
+ "A final, detailed, and informative response ready to be sent to the customer.\n"
95
+ "This response should fully address the customer's inquiry, incorporating all "
96
+ "relevant feedback and improvements.\n"
97
+ "Don't be too formal, maintain a professional and friendly tone throughout."
98
+ ),
99
+ agent=self.qa_agent
100
+ )
101
+
102
+ return [inquiry_resolution, quality_assurance_review]
103
+
104
+ async def process_support(self, inquiry: str, website_url: str) -> Generator[List[Dict], None, None]:
105
+ def add_agent_messages(agent_name: str, tasks: str, emoji: str = "πŸ€–"):
106
+ self.message_queue.add_message({
107
+ "role": "assistant",
108
+ "content": agent_name,
109
+ "metadata": {"title": f"{emoji} {agent_name}"}
110
+ })
111
+
112
+ self.message_queue.add_message({
113
+ "role": "assistant",
114
+ "content": tasks,
115
+ "metadata": {"title": f"πŸ“‹ Task for {agent_name}"}
116
+ })
117
+
118
+ def setup_next_agent(current_agent: str) -> None:
119
+ if current_agent == "Senior Support Representative":
120
+ self.current_agent = "Support Quality Assurance Specialist"
121
+ add_agent_messages(
122
+ "Support Quality Assurance Specialist",
123
+ "Review and improve the support representative's response"
124
+ )
125
+
126
+ def task_callback(task_output) -> None:
127
+ print(f"Task callback received: {task_output}")
128
+
129
+ raw_output = task_output.raw
130
+ if "## Final Answer:" in raw_output:
131
+ content = raw_output.split("## Final Answer:")[1].strip()
132
+ else:
133
+ content = raw_output.strip()
134
+
135
+ if self.current_agent == "Support Quality Assurance Specialist":
136
+ self.message_queue.add_message({
137
+ "role": "assistant",
138
+ "content": "Final response is ready!",
139
+ "metadata": {"title": "βœ… Final Response"}
140
+ })
141
+
142
+ formatted_content = content
143
+ formatted_content = formatted_content.replace("\n#", "\n\n#")
144
+ formatted_content = formatted_content.replace("\n-", "\n\n-")
145
+ formatted_content = formatted_content.replace("\n*", "\n\n*")
146
+ formatted_content = formatted_content.replace("\n1.", "\n\n1.")
147
+ formatted_content = formatted_content.replace("\n\n\n", "\n\n")
148
+
149
+ self.message_queue.add_message({
150
+ "role": "assistant",
151
+ "content": formatted_content
152
+ })
153
+ else:
154
+ self.message_queue.add_message({
155
+ "role": "assistant",
156
+ "content": content,
157
+ "metadata": {"title": f"✨ Output from {self.current_agent}"}
158
+ })
159
+ setup_next_agent(self.current_agent)
160
+
161
+ try:
162
+ self.initialize_agents(website_url)
163
+ self.current_agent = "Senior Support Representative"
164
+
165
+ yield [{
166
+ "role": "assistant",
167
+ "content": "Starting to process your inquiry...",
168
+ "metadata": {"title": "πŸš€ Process Started"}
169
+ }]
170
+
171
+ add_agent_messages(
172
+ "Senior Support Representative",
173
+ "Analyze customer inquiry and provide comprehensive support"
174
+ )
175
+
176
+ crew = Crew(
177
+ agents=[self.support_agent, self.qa_agent],
178
+ tasks=self.create_tasks(inquiry),
179
+ verbose=True,
180
+ task_callback=task_callback
181
+ )
182
+
183
+ def run_crew():
184
+ try:
185
+ crew.kickoff()
186
+ except Exception as e:
187
+ print(f"Error in crew execution: {str(e)}")
188
+ self.message_queue.add_message({
189
+ "role": "assistant",
190
+ "content": f"An error occurred: {str(e)}",
191
+ "metadata": {"title": "❌ Error"}
192
+ })
193
+
194
+ thread = threading.Thread(target=run_crew)
195
+ thread.start()
196
+
197
+ while thread.is_alive() or not self.message_queue.message_queue.empty():
198
+ messages = self.message_queue.get_messages()
199
+ if messages:
200
+ print(f"Yielding messages: {messages}")
201
+ yield messages
202
+ await asyncio.sleep(0.1)
203
+
204
+ except Exception as e:
205
+ print(f"Error in process_support: {str(e)}")
206
+ yield [{
207
+ "role": "assistant",
208
+ "content": f"An error occurred: {str(e)}",
209
+ "metadata": {"title": "❌ Error"}
210
+ }]
211
+
212
+ def create_demo():
213
+ support_crew = None
214
+
215
+ with gr.Blocks(theme=gr.themes.Soft()) as demo:
216
+ gr.Markdown("# 🎯 AI Customer Support Crew")
217
+
218
+ openai_api_key = gr.Textbox(
219
+ label='OpenAI API Key',
220
+ type='password',
221
+ placeholder='Enter your OpenAI API key...',
222
+ interactive=True
223
+ )
224
+
225
+ chatbot = gr.Chatbot(
226
+ label="Support Process",
227
+ height=700,
228
+ type="messages",
229
+ show_label=True,
230
+ visible=False,
231
+ avatar_images=(None, "https://avatars.githubusercontent.com/u/170677839?v=4"),
232
+ render_markdown=True
233
+ )
234
+
235
+ with gr.Row(equal_height=True):
236
+ inquiry = gr.Textbox(
237
+ label="Your Inquiry",
238
+ placeholder="Enter your question...",
239
+ scale=4,
240
+ visible=False
241
+ )
242
+ website_url = gr.Textbox(
243
+ label="Documentation URL",
244
+ placeholder="Enter documentation URL to search...",
245
+ scale=4,
246
+ visible=False
247
+ )
248
+ btn = gr.Button("Get Support", variant="primary", scale=1, visible=False)
249
+
250
+ async def process_input(inquiry_text, website_url_text, history, api_key):
251
+ nonlocal support_crew
252
+ if not api_key:
253
+ history = history or []
254
+ history.append({
255
+ "role": "assistant",
256
+ "content": "Please provide an OpenAI API key.",
257
+ "metadata": {"title": "❌ Error"}
258
+ })
259
+ yield history
260
+ return
261
+
262
+ if support_crew is None:
263
+ support_crew = SupportCrew(api_key=api_key)
264
+
265
+ history = history or []
266
+ history.append({
267
+ "role": "user",
268
+ "content": f"Question: {inquiry_text}\nDocumentation: {website_url_text}"
269
+ })
270
+ yield history
271
+
272
+ try:
273
+ async for messages in support_crew.process_support(inquiry_text, website_url_text):
274
+ history.extend(messages)
275
+ yield history
276
+ except Exception as e:
277
+ history.append({
278
+ "role": "assistant",
279
+ "content": f"An error occurred: {str(e)}",
280
+ "metadata": {"title": "❌ Error"}
281
+ })
282
+ yield history
283
+
284
+ def show_interface():
285
+ return {
286
+ openai_api_key: gr.Textbox(visible=False),
287
+ chatbot: gr.Chatbot(visible=True),
288
+ inquiry: gr.Textbox(visible=True),
289
+ website_url: gr.Textbox(visible=True),
290
+ btn: gr.Button(visible=True)
291
+ }
292
+
293
+ openai_api_key.submit(show_interface, None, [openai_api_key, chatbot, inquiry, website_url, btn])
294
+ btn.click(process_input, [inquiry, website_url, chatbot, openai_api_key], [chatbot])
295
+ inquiry.submit(process_input, [inquiry, website_url, chatbot, openai_api_key], [chatbot])
296
+
297
+ return demo
298
+
299
+ if __name__ == "__main__":
300
+ demo = create_demo()
301
+ demo.queue()
302
+ demo.launch(debug=True)