switch to huggingface API

#1
by not-lain - opened
Files changed (2) hide show
  1. README.md +4 -0
  2. app.py +128 -101
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
@@ -1,13 +1,13 @@
1
  # imports
2
  import gradio as gr
3
- from crewai import Agent, Task, Crew
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):
@@ -39,7 +40,10 @@ class SupportCrew:
39
  if not self.api_key:
40
  raise ValueError("OpenAI API key is required")
41
 
42
- os.environ["OPENAI_API_KEY"] = self.api_key
 
 
 
43
  self.scrape_tool = ScrapeWebsiteTool(website_url=website_url)
44
 
45
  self.support_agent = Agent(
@@ -50,8 +54,9 @@ class SupportCrew:
50
  "You need to make sure that you provide the best support! "
51
  "Make sure to provide full complete answers, and make no assumptions."
52
  ),
 
53
  allow_delegation=False,
54
- verbose=True
55
  )
56
 
57
  self.qa_agent = Agent(
@@ -63,7 +68,8 @@ class SupportCrew:
63
  "You need to make sure that the support representative is providing full "
64
  "complete answers, and make no assumptions."
65
  ),
66
- verbose=True
 
67
  )
68
 
69
  # task creation with description and expected output format and tools
@@ -82,7 +88,7 @@ class SupportCrew:
82
  "leaving no questions unanswered, and maintain a helpful and friendly tone throughout."
83
  ),
84
  tools=[self.scrape_tool],
85
- agent=self.support_agent
86
  )
87
 
88
  quality_assurance_review = Task(
@@ -101,25 +107,31 @@ class SupportCrew:
101
  "relevant feedback and improvements.\n"
102
  "Don't be too formal, maintain a professional and friendly tone throughout."
103
  ),
104
- agent=self.qa_agent
105
  )
106
 
107
  return [inquiry_resolution, quality_assurance_review]
108
 
109
  # main processing function
110
- async def process_support(self, inquiry: str, website_url: str) -> Generator[List[Dict], None, None]:
 
 
111
  def add_agent_messages(agent_name: str, tasks: str, emoji: str = "πŸ€–"):
112
- self.message_queue.add_message({
113
- "role": "assistant",
114
- "content": agent_name,
115
- "metadata": {"title": f"{emoji} {agent_name}"}
116
- })
117
-
118
- self.message_queue.add_message({
119
- "role": "assistant",
120
- "content": tasks,
121
- "metadata": {"title": f"πŸ“‹ Task for {agent_name}"}
122
- })
 
 
 
 
123
 
124
  # Manages transition between agents
125
  def setup_next_agent(current_agent: str) -> None:
@@ -127,64 +139,69 @@ class SupportCrew:
127
  self.current_agent = "Support Quality Assurance Specialist"
128
  add_agent_messages(
129
  "Support Quality Assurance Specialist",
130
- "Review and improve the support representative's response"
131
  )
132
 
133
  def task_callback(task_output) -> None:
134
  print(f"Task callback received: {task_output}")
135
-
136
  raw_output = task_output.raw
137
  if "## Final Answer:" in raw_output:
138
  content = raw_output.split("## Final Answer:")[1].strip()
139
  else:
140
  content = raw_output.strip()
141
-
142
  if self.current_agent == "Support Quality Assurance Specialist":
143
- self.message_queue.add_message({
144
- "role": "assistant",
145
- "content": "Final response is ready!",
146
- "metadata": {"title": "βœ… Final Response"}
147
- })
148
-
 
 
149
  formatted_content = content
150
  formatted_content = formatted_content.replace("\n#", "\n\n#")
151
  formatted_content = formatted_content.replace("\n-", "\n\n-")
152
  formatted_content = formatted_content.replace("\n*", "\n\n*")
153
  formatted_content = formatted_content.replace("\n1.", "\n\n1.")
154
  formatted_content = formatted_content.replace("\n\n\n", "\n\n")
155
-
156
- self.message_queue.add_message({
157
- "role": "assistant",
158
- "content": formatted_content
159
- })
160
  else:
161
- self.message_queue.add_message({
162
- "role": "assistant",
163
- "content": content,
164
- "metadata": {"title": f"✨ Output from {self.current_agent}"}
165
- })
 
 
166
  setup_next_agent(self.current_agent)
167
 
168
  try:
169
  self.initialize_agents(website_url)
170
  self.current_agent = "Senior Support Representative"
171
 
172
- yield [{
173
- "role": "assistant",
174
- "content": "Starting to process your inquiry...",
175
- "metadata": {"title": "πŸš€ Process Started"}
176
- }]
 
 
177
 
178
  add_agent_messages(
179
  "Senior Support Representative",
180
- "Analyze customer inquiry and provide comprehensive support"
181
  )
182
 
183
  crew = Crew(
184
  agents=[self.support_agent, self.qa_agent],
185
  tasks=self.create_tasks(inquiry),
186
  verbose=True,
187
- task_callback=task_callback
188
  )
189
 
190
  def run_crew():
@@ -192,11 +209,13 @@ class SupportCrew:
192
  crew.kickoff()
193
  except Exception as e:
194
  print(f"Error in crew execution: {str(e)}")
195
- self.message_queue.add_message({
196
- "role": "assistant",
197
- "content": f"An error occurred: {str(e)}",
198
- "metadata": {"title": "❌ Error"}
199
- })
 
 
200
 
201
  thread = threading.Thread(target=run_crew)
202
  thread.start()
@@ -210,23 +229,22 @@ class SupportCrew:
210
 
211
  except Exception as e:
212
  print(f"Error in process_support: {str(e)}")
213
- yield [{
214
- "role": "assistant",
215
- "content": f"An error occurred: {str(e)}",
216
- "metadata": {"title": "❌ Error"}
217
- }]
 
 
 
218
 
219
  def create_demo():
220
  support_crew = None
221
 
222
  with gr.Blocks(theme=gr.themes.Ocean()) as demo:
223
  gr.Markdown("# 🎯 AI Customer Support Crew")
224
- 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.")
225
- openai_api_key = gr.Textbox(
226
- label='OpenAI API Key',
227
- type='password',
228
- placeholder='Type your OpenAI API Key and press Enter to access the app...',
229
- interactive=True
230
  )
231
 
232
  chatbot = gr.Chatbot(
@@ -234,9 +252,11 @@ def create_demo():
234
  height=700,
235
  type="messages",
236
  show_label=True,
237
- visible=False,
238
- avatar_images=(None, "https://avatars.githubusercontent.com/u/170677839?v=4"),
239
- render_markdown=True
 
 
240
  )
241
 
242
  with gr.Row(equal_height=True):
@@ -244,66 +264,73 @@ def create_demo():
244
  label="Your Inquiry",
245
  placeholder="Enter your question...",
246
  scale=4,
247
- visible=False
248
  )
249
  website_url = gr.Textbox(
250
  label="Documentation URL",
251
  placeholder="Enter documentation URL to search...",
252
  scale=4,
253
- visible=False
254
  )
255
- btn = gr.Button("Get Support", variant="primary", scale=1, visible=False)
256
 
257
- async def process_input(inquiry_text, website_url_text, history, api_key):
 
 
258
  nonlocal support_crew
259
- if not api_key:
260
- history = history or []
261
- history.append({
262
- "role": "assistant",
263
- "content": "Please provide an OpenAI API key.",
264
- "metadata": {"title": "❌ Error"}
265
- })
266
- yield history
267
- return
268
 
269
  if support_crew is None:
270
  support_crew = SupportCrew(api_key=api_key)
271
 
272
  history = history or []
273
- history.append({
274
- "role": "user",
275
- "content": f"Question: {inquiry_text}\nDocumentation: {website_url_text}"
276
- })
 
 
277
  yield history
278
 
279
  try:
280
- async for messages in support_crew.process_support(inquiry_text, website_url_text):
 
 
281
  history.extend(messages)
282
  yield history
283
  except Exception as e:
284
- history.append({
285
- "role": "assistant",
286
- "content": f"An error occurred: {str(e)}",
287
- "metadata": {"title": "❌ Error"}
288
- })
 
 
289
  yield history
290
 
291
- def show_interface():
292
- return {
293
- openai_api_key: gr.Textbox(visible=False),
294
- chatbot: gr.Chatbot(visible=True),
295
- inquiry: gr.Textbox(visible=True),
296
- website_url: gr.Textbox(visible=True),
297
- btn: gr.Button(visible=True)
298
- }
299
-
300
- openai_api_key.submit(show_interface, None, [openai_api_key, chatbot, inquiry, website_url, btn])
301
- btn.click(process_input, [inquiry, website_url, chatbot, openai_api_key], [chatbot])
302
- inquiry.submit(process_input, [inquiry, website_url, chatbot, openai_api_key], [chatbot])
303
 
304
  return demo
305
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
306
  if __name__ == "__main__":
307
- demo = create_demo()
308
  demo.queue()
309
- demo.launch(debug=True)
 
1
  # imports
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):
 
40
  if not self.api_key:
41
  raise ValueError("OpenAI API key is required")
42
 
43
+ self.llm = LLM(
44
+ model="huggingface/meta-llama/Llama-3.3-70B-Instruct",
45
+ api_key=self.api_key,
46
+ )
47
  self.scrape_tool = ScrapeWebsiteTool(website_url=website_url)
48
 
49
  self.support_agent = Agent(
 
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
 
281
  if support_crew is None:
282
  support_crew = SupportCrew(api_key=api_key)
283
 
284
  history = history or []
285
+ history.append(
286
+ {
287
+ "role": "user",
288
+ "content": f"Question: {inquiry_text}\nDocumentation: {website_url_text}",
289
+ }
290
+ )
291
  yield history
292
 
293
  try:
294
+ async for messages in support_crew.process_support(
295
+ inquiry_text, website_url_text
296
+ ):
297
  history.extend(messages)
298
  yield history
299
  except Exception as e:
300
+ history.append(
301
+ {
302
+ "role": "assistant",
303
+ "content": f"An error occurred: {str(e)}",
304
+ "metadata": {"title": "❌ Error"},
305
+ }
306
+ )
307
  yield history
308
 
309
+ btn.click(process_input, [inquiry, website_url, chatbot], [chatbot])
310
+ inquiry.submit(process_input, [inquiry, website_url, chatbot], [chatbot])
 
 
 
 
 
 
 
 
 
 
311
 
312
  return demo
313
 
314
+
315
+ def swap_visibilty(profile: gr.OAuthProfile | None):
316
+ return (
317
+ gr.update(elem_classes=["main_ui_logged_in"])
318
+ if profile
319
+ else gr.update(elem_classes=["main_ui_logged_out"])
320
+ )
321
+
322
+
323
+ css = """
324
+ .main_ui_logged_out{opacity: 0.3; pointer-events: none}
325
+ """
326
+
327
+ interface = create_demo()
328
+ with gr.Blocks(css=css) as demo:
329
+ gr.LoginButton()
330
+ with gr.Column(elem_classes="main_ui_logged_out") as main_ui:
331
+ interface.render()
332
+ demo.load(fn=swap_visibilty, outputs=main_ui)
333
+
334
  if __name__ == "__main__":
 
335
  demo.queue()
336
+ demo.launch()