not-lain commited on
Commit
63e2602
β€’
1 Parent(s): 56b63d6

format with ruff and switch to using gradio login button

Browse files
Files changed (2) hide show
  1. README.md +4 -0
  2. app.py +130 -97
README.md CHANGED
@@ -9,6 +9,10 @@ app_file: app.py
9
  pinned: false
10
  license: mit
11
  short_description: 'Build support agent with CrewAI multi-agents and Gradio '
 
 
 
 
12
  ---
13
 
14
  Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
9
  pinned: false
10
  license: mit
11
  short_description: 'Build support agent with CrewAI multi-agents and Gradio '
12
+ hf_oauth: true
13
+ hf_oauth_scopes:
14
+ - read-repos
15
+ ---
16
  ---
17
 
18
  Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
app.py CHANGED
@@ -2,12 +2,12 @@
2
  import gradio as gr
3
  from crewai import Agent, Task, Crew, LLM
4
  from crewai_tools import ScrapeWebsiteTool
5
- import os
6
  import queue
7
  import threading
8
  import asyncio
9
  from typing import List, Dict, Generator
10
 
 
11
  # Message Queue System to manage flow of message
12
  class SupportMessageQueue:
13
  def __init__(self):
@@ -24,6 +24,7 @@ class SupportMessageQueue:
24
  messages.append(self.message_queue.get())
25
  return messages
26
 
 
27
  # main class
28
  class SupportCrew:
29
  def __init__(self, api_key: str = None):
@@ -53,9 +54,9 @@ class SupportCrew:
53
  "You need to make sure that you provide the best support! "
54
  "Make sure to provide full complete answers, and make no assumptions."
55
  ),
56
- llm = self.llm,
57
  allow_delegation=False,
58
- verbose=True
59
  )
60
 
61
  self.qa_agent = Agent(
@@ -67,8 +68,8 @@ class SupportCrew:
67
  "You need to make sure that the support representative is providing full "
68
  "complete answers, and make no assumptions."
69
  ),
70
- llm = self.llm,
71
- verbose=True
72
  )
73
 
74
  # task creation with description and expected output format and tools
@@ -87,7 +88,7 @@ class SupportCrew:
87
  "leaving no questions unanswered, and maintain a helpful and friendly tone throughout."
88
  ),
89
  tools=[self.scrape_tool],
90
- agent=self.support_agent
91
  )
92
 
93
  quality_assurance_review = Task(
@@ -106,25 +107,31 @@ class SupportCrew:
106
  "relevant feedback and improvements.\n"
107
  "Don't be too formal, maintain a professional and friendly tone throughout."
108
  ),
109
- agent=self.qa_agent
110
  )
111
 
112
  return [inquiry_resolution, quality_assurance_review]
113
 
114
  # main processing function
115
- async def process_support(self, inquiry: str, website_url: str) -> Generator[List[Dict], None, None]:
 
 
116
  def add_agent_messages(agent_name: str, tasks: str, emoji: str = "πŸ€–"):
117
- self.message_queue.add_message({
118
- "role": "assistant",
119
- "content": agent_name,
120
- "metadata": {"title": f"{emoji} {agent_name}"}
121
- })
122
-
123
- self.message_queue.add_message({
124
- "role": "assistant",
125
- "content": tasks,
126
- "metadata": {"title": f"πŸ“‹ Task for {agent_name}"}
127
- })
 
 
 
 
128
 
129
  # Manages transition between agents
130
  def setup_next_agent(current_agent: str) -> None:
@@ -132,64 +139,69 @@ class SupportCrew:
132
  self.current_agent = "Support Quality Assurance Specialist"
133
  add_agent_messages(
134
  "Support Quality Assurance Specialist",
135
- "Review and improve the support representative's response"
136
  )
137
 
138
  def task_callback(task_output) -> None:
139
  print(f"Task callback received: {task_output}")
140
-
141
  raw_output = task_output.raw
142
  if "## Final Answer:" in raw_output:
143
  content = raw_output.split("## Final Answer:")[1].strip()
144
  else:
145
  content = raw_output.strip()
146
-
147
  if self.current_agent == "Support Quality Assurance Specialist":
148
- self.message_queue.add_message({
149
- "role": "assistant",
150
- "content": "Final response is ready!",
151
- "metadata": {"title": "βœ… Final Response"}
152
- })
153
-
 
 
154
  formatted_content = content
155
  formatted_content = formatted_content.replace("\n#", "\n\n#")
156
  formatted_content = formatted_content.replace("\n-", "\n\n-")
157
  formatted_content = formatted_content.replace("\n*", "\n\n*")
158
  formatted_content = formatted_content.replace("\n1.", "\n\n1.")
159
  formatted_content = formatted_content.replace("\n\n\n", "\n\n")
160
-
161
- self.message_queue.add_message({
162
- "role": "assistant",
163
- "content": formatted_content
164
- })
165
  else:
166
- self.message_queue.add_message({
167
- "role": "assistant",
168
- "content": content,
169
- "metadata": {"title": f"✨ Output from {self.current_agent}"}
170
- })
 
 
171
  setup_next_agent(self.current_agent)
172
 
173
  try:
174
  self.initialize_agents(website_url)
175
  self.current_agent = "Senior Support Representative"
176
 
177
- yield [{
178
- "role": "assistant",
179
- "content": "Starting to process your inquiry...",
180
- "metadata": {"title": "πŸš€ Process Started"}
181
- }]
 
 
182
 
183
  add_agent_messages(
184
  "Senior Support Representative",
185
- "Analyze customer inquiry and provide comprehensive support"
186
  )
187
 
188
  crew = Crew(
189
  agents=[self.support_agent, self.qa_agent],
190
  tasks=self.create_tasks(inquiry),
191
  verbose=True,
192
- task_callback=task_callback
193
  )
194
 
195
  def run_crew():
@@ -197,11 +209,13 @@ class SupportCrew:
197
  crew.kickoff()
198
  except Exception as e:
199
  print(f"Error in crew execution: {str(e)}")
200
- self.message_queue.add_message({
201
- "role": "assistant",
202
- "content": f"An error occurred: {str(e)}",
203
- "metadata": {"title": "❌ Error"}
204
- })
 
 
205
 
206
  thread = threading.Thread(target=run_crew)
207
  thread.start()
@@ -215,23 +229,22 @@ class SupportCrew:
215
 
216
  except Exception as e:
217
  print(f"Error in process_support: {str(e)}")
218
- yield [{
219
- "role": "assistant",
220
- "content": f"An error occurred: {str(e)}",
221
- "metadata": {"title": "❌ Error"}
222
- }]
 
 
 
223
 
224
  def create_demo():
225
  support_crew = None
226
 
227
  with gr.Blocks(theme=gr.themes.Ocean()) as demo:
228
  gr.Markdown("# 🎯 AI Customer Support Crew")
229
- gr.Markdown("This is a friendly, high-performing multi-agent application built with Gradio and CrewAI. Enter a webpage URL and your questions from that webpage.")
230
- HF_api_key = gr.Textbox(
231
- label='HuggingFace API Key',
232
- type='password',
233
- placeholder='Type your HuggingFace API Key and press Enter to access the app...',
234
- interactive=True
235
  )
236
 
237
  chatbot = gr.Chatbot(
@@ -239,9 +252,11 @@ def create_demo():
239
  height=700,
240
  type="messages",
241
  show_label=True,
242
- visible=False,
243
- avatar_images=(None, "https://avatars.githubusercontent.com/u/170677839?v=4"),
244
- render_markdown=True
 
 
245
  )
246
 
247
  with gr.Row(equal_height=True):
@@ -249,25 +264,28 @@ def create_demo():
249
  label="Your Inquiry",
250
  placeholder="Enter your question...",
251
  scale=4,
252
- visible=False
253
  )
254
  website_url = gr.Textbox(
255
  label="Documentation URL",
256
  placeholder="Enter documentation URL to search...",
257
  scale=4,
258
- visible=False
259
  )
260
- btn = gr.Button("Get Support", variant="primary", scale=1, visible=False)
261
 
262
- async def process_input(inquiry_text, website_url_text, history, api_key):
 
 
263
  nonlocal support_crew
 
264
  if not api_key:
265
  history = history or []
266
- history.append({
267
- "role": "assistant",
268
- "content": "Please provide an OpenAI API key.",
269
- "metadata": {"title": "❌ Error"}
270
- })
 
 
271
  yield history
272
  return
273
 
@@ -275,40 +293,55 @@ def create_demo():
275
  support_crew = SupportCrew(api_key=api_key)
276
 
277
  history = history or []
278
- history.append({
279
- "role": "user",
280
- "content": f"Question: {inquiry_text}\nDocumentation: {website_url_text}"
281
- })
 
 
282
  yield history
283
 
284
  try:
285
- async for messages in support_crew.process_support(inquiry_text, website_url_text):
 
 
286
  history.extend(messages)
287
  yield history
288
  except Exception as e:
289
- history.append({
290
- "role": "assistant",
291
- "content": f"An error occurred: {str(e)}",
292
- "metadata": {"title": "❌ Error"}
293
- })
 
 
294
  yield history
295
 
296
- def show_interface():
297
- return {
298
- HF_api_key: gr.Textbox(visible=False),
299
- chatbot: gr.Chatbot(visible=True),
300
- inquiry: gr.Textbox(visible=True),
301
- website_url: gr.Textbox(visible=True),
302
- btn: gr.Button(visible=True)
303
- }
304
-
305
- HF_api_key.submit(show_interface, None, [HF_api_key, chatbot, inquiry, website_url, btn])
306
- btn.click(process_input, [inquiry, website_url, chatbot, HF_api_key], [chatbot])
307
- inquiry.submit(process_input, [inquiry, website_url, chatbot, HF_api_key], [chatbot])
308
 
309
  return demo
310
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
311
  if __name__ == "__main__":
312
- demo = create_demo()
313
  demo.queue()
314
- demo.launch(debug=True)
 
2
  import gradio as gr
3
  from crewai import Agent, Task, Crew, LLM
4
  from crewai_tools import ScrapeWebsiteTool
 
5
  import queue
6
  import threading
7
  import asyncio
8
  from typing import List, Dict, Generator
9
 
10
+
11
  # Message Queue System to manage flow of message
12
  class SupportMessageQueue:
13
  def __init__(self):
 
24
  messages.append(self.message_queue.get())
25
  return messages
26
 
27
+
28
  # main class
29
  class SupportCrew:
30
  def __init__(self, api_key: str = None):
 
54
  "You need to make sure that you provide the best support! "
55
  "Make sure to provide full complete answers, and make no assumptions."
56
  ),
57
+ llm=self.llm,
58
  allow_delegation=False,
59
+ verbose=True,
60
  )
61
 
62
  self.qa_agent = Agent(
 
68
  "You need to make sure that the support representative is providing full "
69
  "complete answers, and make no assumptions."
70
  ),
71
+ llm=self.llm,
72
+ verbose=True,
73
  )
74
 
75
  # task creation with description and expected output format and tools
 
88
  "leaving no questions unanswered, and maintain a helpful and friendly tone throughout."
89
  ),
90
  tools=[self.scrape_tool],
91
+ agent=self.support_agent,
92
  )
93
 
94
  quality_assurance_review = Task(
 
107
  "relevant feedback and improvements.\n"
108
  "Don't be too formal, maintain a professional and friendly tone throughout."
109
  ),
110
+ agent=self.qa_agent,
111
  )
112
 
113
  return [inquiry_resolution, quality_assurance_review]
114
 
115
  # main processing function
116
+ async def process_support(
117
+ self, inquiry: str, website_url: str
118
+ ) -> Generator[List[Dict], None, None]:
119
  def add_agent_messages(agent_name: str, tasks: str, emoji: str = "πŸ€–"):
120
+ self.message_queue.add_message(
121
+ {
122
+ "role": "assistant",
123
+ "content": agent_name,
124
+ "metadata": {"title": f"{emoji} {agent_name}"},
125
+ }
126
+ )
127
+
128
+ self.message_queue.add_message(
129
+ {
130
+ "role": "assistant",
131
+ "content": tasks,
132
+ "metadata": {"title": f"πŸ“‹ Task for {agent_name}"},
133
+ }
134
+ )
135
 
136
  # Manages transition between agents
137
  def setup_next_agent(current_agent: str) -> None:
 
139
  self.current_agent = "Support Quality Assurance Specialist"
140
  add_agent_messages(
141
  "Support Quality Assurance Specialist",
142
+ "Review and improve the support representative's response",
143
  )
144
 
145
  def task_callback(task_output) -> None:
146
  print(f"Task callback received: {task_output}")
147
+
148
  raw_output = task_output.raw
149
  if "## Final Answer:" in raw_output:
150
  content = raw_output.split("## Final Answer:")[1].strip()
151
  else:
152
  content = raw_output.strip()
153
+
154
  if self.current_agent == "Support Quality Assurance Specialist":
155
+ self.message_queue.add_message(
156
+ {
157
+ "role": "assistant",
158
+ "content": "Final response is ready!",
159
+ "metadata": {"title": "βœ… Final Response"},
160
+ }
161
+ )
162
+
163
  formatted_content = content
164
  formatted_content = formatted_content.replace("\n#", "\n\n#")
165
  formatted_content = formatted_content.replace("\n-", "\n\n-")
166
  formatted_content = formatted_content.replace("\n*", "\n\n*")
167
  formatted_content = formatted_content.replace("\n1.", "\n\n1.")
168
  formatted_content = formatted_content.replace("\n\n\n", "\n\n")
169
+
170
+ self.message_queue.add_message(
171
+ {"role": "assistant", "content": formatted_content}
172
+ )
 
173
  else:
174
+ self.message_queue.add_message(
175
+ {
176
+ "role": "assistant",
177
+ "content": content,
178
+ "metadata": {"title": f"✨ Output from {self.current_agent}"},
179
+ }
180
+ )
181
  setup_next_agent(self.current_agent)
182
 
183
  try:
184
  self.initialize_agents(website_url)
185
  self.current_agent = "Senior Support Representative"
186
 
187
+ yield [
188
+ {
189
+ "role": "assistant",
190
+ "content": "Starting to process your inquiry...",
191
+ "metadata": {"title": "πŸš€ Process Started"},
192
+ }
193
+ ]
194
 
195
  add_agent_messages(
196
  "Senior Support Representative",
197
+ "Analyze customer inquiry and provide comprehensive support",
198
  )
199
 
200
  crew = Crew(
201
  agents=[self.support_agent, self.qa_agent],
202
  tasks=self.create_tasks(inquiry),
203
  verbose=True,
204
+ task_callback=task_callback,
205
  )
206
 
207
  def run_crew():
 
209
  crew.kickoff()
210
  except Exception as e:
211
  print(f"Error in crew execution: {str(e)}")
212
+ self.message_queue.add_message(
213
+ {
214
+ "role": "assistant",
215
+ "content": f"An error occurred: {str(e)}",
216
+ "metadata": {"title": "❌ Error"},
217
+ }
218
+ )
219
 
220
  thread = threading.Thread(target=run_crew)
221
  thread.start()
 
229
 
230
  except Exception as e:
231
  print(f"Error in process_support: {str(e)}")
232
+ yield [
233
+ {
234
+ "role": "assistant",
235
+ "content": f"An error occurred: {str(e)}",
236
+ "metadata": {"title": "❌ Error"},
237
+ }
238
+ ]
239
+
240
 
241
  def create_demo():
242
  support_crew = None
243
 
244
  with gr.Blocks(theme=gr.themes.Ocean()) as demo:
245
  gr.Markdown("# 🎯 AI Customer Support Crew")
246
+ gr.Markdown(
247
+ "This is a friendly, high-performing multi-agent application built with Gradio and CrewAI. Enter a webpage URL and your questions from that webpage."
 
 
 
 
248
  )
249
 
250
  chatbot = gr.Chatbot(
 
252
  height=700,
253
  type="messages",
254
  show_label=True,
255
+ avatar_images=(
256
+ None,
257
+ "https://avatars.githubusercontent.com/u/170677839?v=4",
258
+ ),
259
+ render_markdown=True,
260
  )
261
 
262
  with gr.Row(equal_height=True):
 
264
  label="Your Inquiry",
265
  placeholder="Enter your question...",
266
  scale=4,
 
267
  )
268
  website_url = gr.Textbox(
269
  label="Documentation URL",
270
  placeholder="Enter documentation URL to search...",
271
  scale=4,
 
272
  )
273
+ btn = gr.Button("Get Support", variant="primary", scale=1)
274
 
275
+ async def process_input(
276
+ inquiry_text, website_url_text, history, oauth_token: gr.OAuthToken | None
277
+ ):
278
  nonlocal support_crew
279
+ api_key = oauth_token.token
280
  if not api_key:
281
  history = history or []
282
+ history.append(
283
+ {
284
+ "role": "assistant",
285
+ "content": "Please provide huggingface key.",
286
+ "metadata": {"title": "❌ Error"},
287
+ }
288
+ )
289
  yield history
290
  return
291
 
 
293
  support_crew = SupportCrew(api_key=api_key)
294
 
295
  history = history or []
296
+ history.append(
297
+ {
298
+ "role": "user",
299
+ "content": f"Question: {inquiry_text}\nDocumentation: {website_url_text}",
300
+ }
301
+ )
302
  yield history
303
 
304
  try:
305
+ async for messages in support_crew.process_support(
306
+ inquiry_text, website_url_text
307
+ ):
308
  history.extend(messages)
309
  yield history
310
  except Exception as e:
311
+ history.append(
312
+ {
313
+ "role": "assistant",
314
+ "content": f"An error occurred: {str(e)}",
315
+ "metadata": {"title": "❌ Error"},
316
+ }
317
+ )
318
  yield history
319
 
320
+ btn.click(process_input, [inquiry, website_url, chatbot], [chatbot])
321
+ inquiry.submit(process_input, [inquiry, website_url, chatbot], [chatbot])
 
 
 
 
 
 
 
 
 
 
322
 
323
  return demo
324
 
325
+
326
+ def swap_visibilty(profile: gr.OAuthProfile | None):
327
+ return (
328
+ gr.update(elem_classes=["main_ui_logged_in"])
329
+ if profile
330
+ else gr.update(elem_classes=["main_ui_logged_out"])
331
+ )
332
+
333
+
334
+ css = """
335
+ .main_ui_logged_out{opacity: 0.3; pointer-events: none}
336
+ """
337
+
338
+ interface = create_demo()
339
+ with gr.Blocks(css=css) as demo:
340
+ gr.LoginButton()
341
+ with gr.Column(elem_classes="main_ui_logged_out") as main_ui:
342
+ interface.render()
343
+ demo.load(fn=swap_visibilty, outputs=main_ui)
344
+
345
  if __name__ == "__main__":
 
346
  demo.queue()
347
+ demo.launch()