donb-hf commited on
Commit
f4f5e89
β€’
1 Parent(s): 3775a55

push to hf

Browse files
Files changed (3) hide show
  1. .gitignore +2 -0
  2. .python-version +1 -0
  3. app.py +287 -23
.gitignore ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ .venv/
2
+ .env
.python-version ADDED
@@ -0,0 +1 @@
 
 
1
+ 3.10
app.py CHANGED
@@ -1,25 +1,289 @@
1
- # app.py
2
-
3
  import gradio as gr
4
- import subprocess
5
-
6
- def chat_with_support(input_text):
7
- process = subprocess.Popen(
8
- ["python3", "function_orchestrator.py"],
9
- stdin=subprocess.PIPE,
10
- stdout=subprocess.PIPE,
11
- stderr=subprocess.PIPE,
12
- text=True
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
13
  )
14
- stdout, stderr = process.communicate(input=input_text)
15
- return stdout
16
-
17
- iface = gr.Interface(
18
- fn=chat_with_support,
19
- inputs="text",
20
- outputs="text",
21
- title="TechNova Support Chat",
22
- description="Chat with TechNova support bot to manage your orders and account."
23
- )
24
-
25
- iface.launch()
 
 
 
1
  import gradio as gr
2
+ import anthropic
3
+ import json
4
+ import requests
5
+ import warnings
6
+ import logging
7
+ import os
8
+ import pandas as pd
9
+ from dotenv import load_dotenv
10
+
11
+ # Load environment variables
12
+ load_dotenv()
13
+
14
+ # Configure logging
15
+ logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
16
+ logger = logging.getLogger(__name__)
17
+
18
+ # Initialize Anthropoc client with API key
19
+ client = anthropic.Client(api_key=os.getenv('ANTHROPIC_API_KEY'))
20
+ MODEL_NAME = "claude-3-5-sonnet-20240620"
21
+
22
+ # Define the base URL for the FastAPI service
23
+ BASE_URL = "https://dwb2023-blackbird-svc.hf.space"
24
+
25
+ # Define tools
26
+ tools = [
27
+ {
28
+ "name": "get_user",
29
+ "description": "Looks up a user by email, phone, or username.",
30
+ "input_schema": {
31
+ "type": "object",
32
+ "properties": {
33
+ "key": {
34
+ "type": "string",
35
+ "enum": ["email", "phone", "username"],
36
+ "description": "The attribute to search for a user by (email, phone, or username)."
37
+ },
38
+ "value": {
39
+ "type": "string",
40
+ "description": "The value to match for the specified attribute."
41
+ }
42
+ },
43
+ "required": ["key", "value"]
44
+ }
45
+ },
46
+ {
47
+ "name": "get_order_by_id",
48
+ "description": "Retrieves the details of a specific order based on the order ID.",
49
+ "input_schema": {
50
+ "type": "object",
51
+ "properties": {
52
+ "order_id": {
53
+ "type": "string",
54
+ "description": "The unique identifier for the order."
55
+ }
56
+ },
57
+ "required": ["order_id"]
58
+ }
59
+ },
60
+ {
61
+ "name": "get_customer_orders",
62
+ "description": "Retrieves the list of orders belonging to a user based on a user's customer id.",
63
+ "input_schema": {
64
+ "type": "object",
65
+ "properties": {
66
+ "customer_id": {
67
+ "type": "string",
68
+ "description": "The customer_id belonging to the user"
69
+ }
70
+ },
71
+ "required": ["customer_id"]
72
+ }
73
+ },
74
+ {
75
+ "name": "cancel_order",
76
+ "description": "Cancels an order based on a provided order_id. Only orders that are 'processing' can be cancelled.",
77
+ "input_schema": {
78
+ "type": "object",
79
+ "properties": {
80
+ "order_id": {
81
+ "type": "string",
82
+ "description": "The order_id pertaining to a particular order"
83
+ }
84
+ },
85
+ "required": ["order_id"]
86
+ }
87
+ },
88
+ {
89
+ "name": "update_user_contact",
90
+ "description": "Updates a user's email and/or phone number.",
91
+ "input_schema": {
92
+ "type": "object",
93
+ "properties": {
94
+ "user_id": {
95
+ "type": "string",
96
+ "description": "The ID of the user"
97
+ },
98
+ "email": {
99
+ "type": "string",
100
+ "description": "The new email address of the user"
101
+ },
102
+ "phone": {
103
+ "type": "string",
104
+ "description": "The new phone number of the user"
105
+ }
106
+ },
107
+ "required": ["user_id"]
108
+ }
109
+ },
110
+ {
111
+ "name": "get_user_info",
112
+ "description": "Retrieves a user's information along with their order history based on email, phone, or username.",
113
+ "input_schema": {
114
+ "type": "object",
115
+ "properties": {
116
+ "key": {
117
+ "type": "string",
118
+ "enum": ["email", "phone", "username"],
119
+ "description": "The attribute to search for a user by (email, phone, or username)."
120
+ },
121
+ "value": {
122
+ "type": "string",
123
+ "description": "The value to match for the specified attribute."
124
+ }
125
+ },
126
+ "required": ["key", "value"]
127
+ }
128
+ }
129
+ ]
130
+
131
+ # Suppress the InsecureRequestWarning
132
+ warnings.filterwarnings("ignore", category=requests.urllib3.exceptions.InsecureRequestWarning)
133
+
134
+ def process_tool_call(tool_name, tool_input):
135
+ tool_endpoints = {
136
+ "get_user": "get_user",
137
+ "get_order_by_id": "get_order_by_id",
138
+ "get_customer_orders": "get_customer_orders",
139
+ "cancel_order": "cancel_order",
140
+ "update_user_contact": "update_user",
141
+ "get_user_info": "get_user_info"
142
+ }
143
+
144
+ if tool_name in tool_endpoints:
145
+ response = requests.post(f"{BASE_URL}/{tool_endpoints[tool_name]}", json=tool_input, verify=False)
146
+ else:
147
+ logger.error(f"Invalid tool name: {tool_name}")
148
+ return {"error": "Invalid tool name"}
149
+
150
+ if response.status_code == 200:
151
+ return response.json()
152
+ else:
153
+ logger.error(f"Tool call failed: {response.text}")
154
+ return {"error": response.text}
155
+
156
+ system_prompt = """
157
+ You are a customer support chat bot for an online retailer called BlackBird.
158
+ Your job is to help users look up their account, orders, and cancel orders.
159
+ Be helpful and brief in your responses.
160
+ You have access to a set of tools, but only use them when needed.
161
+ If you do not have enough information to use a tool correctly, ask a user follow up questions to get the required inputs.
162
+ Do not call any of the tools unless you have the required data from a user.
163
+
164
+ In each conversational turn, you will begin by thinking about your response.
165
+ Once you're done, you will write a user-facing response.
166
+ """
167
+
168
+ def simple_chat(user_message, history):
169
+ # Reconstruct the message history
170
+ messages = []
171
+ for i, (user_msg, assistant_msg) in enumerate(history):
172
+ messages.append({"role": "user", "content": user_msg})
173
+ messages.append({"role": "assistant", "content": assistant_msg})
174
+ messages.append({"role": "user", "content": user_message})
175
+
176
+ full_response = ""
177
+ MAX_ITERATIONS = 5
178
+ iteration_count = 0
179
+
180
+ while iteration_count < MAX_ITERATIONS:
181
+ try:
182
+ logger.info(f"Sending messages to API: {json.dumps(messages, indent=2)}")
183
+ response = client.messages.create(
184
+ model=MODEL_NAME,
185
+ system=system_prompt,
186
+ max_tokens=4096,
187
+ tools=tools,
188
+ messages=messages,
189
+ )
190
+
191
+ assistant_message = response.content[0].text if isinstance(response.content, list) else response.content
192
+
193
+ if response.stop_reason == "tool_use":
194
+ tool_use = response.content[-1]
195
+ tool_name = tool_use.name
196
+ tool_input = tool_use.input
197
+
198
+ tool_result = process_tool_call(tool_name, tool_input)
199
+
200
+ # Add assistant message indicating tool use
201
+ messages.append({"role": "assistant", "content": assistant_message})
202
+
203
+ # Add user message with tool result to maintain role alternation
204
+ messages.append({
205
+ "role": "user",
206
+ "content": json.dumps({
207
+ "type": "tool_result",
208
+ "tool_use_id": tool_use.id,
209
+ "content": tool_result,
210
+ })
211
+ })
212
+
213
+ full_response += f"\nUsing tool: {tool_name}\n"
214
+ iteration_count += 1
215
+ continue
216
+ else:
217
+ # Add the assistant's reply to the full response
218
+ full_response += assistant_message
219
+ messages.append({"role": "assistant", "content": assistant_message})
220
+ break
221
+
222
+ except anthropic.BadRequestError as e:
223
+ logger.error(f"BadRequestError: {str(e)}")
224
+ full_response = f"Error: {str(e)}"
225
+ break
226
+ except Exception as e:
227
+ logger.error(f"Unexpected error: {str(e)}")
228
+ full_response = f"An unexpected error occurred: {str(e)}"
229
+ break
230
+
231
+ logger.info(f"Final messages: {json.dumps(messages, indent=2)}")
232
+
233
+ if iteration_count == MAX_ITERATIONS:
234
+ logger.warning("Maximum iterations reached in simple_chat")
235
+
236
+ history.append((user_message, full_response))
237
+ return history, "", messages # Return messages as well
238
+
239
+ def messages_to_dataframe(messages):
240
+ data = []
241
+ for msg in messages:
242
+ row = {
243
+ 'role': msg['role'],
244
+ 'content': msg['content'] if isinstance(msg['content'], str) else json.dumps(msg['content']),
245
+ 'tool_use': None,
246
+ 'tool_result': None
247
+ }
248
+ if msg['role'] == 'assistant' and isinstance(msg['content'], list):
249
+ for item in msg['content']:
250
+ if isinstance(item, dict) and 'type' in item:
251
+ if item['type'] == 'tool_use':
252
+ row['tool_use'] = json.dumps(item)
253
+ elif item['type'] == 'tool_result':
254
+ row['tool_result'] = json.dumps(item)
255
+ data.append(row)
256
+ return pd.DataFrame(data)
257
+
258
+ def submit_message(message, history):
259
+ history, _, messages = simple_chat(message, history)
260
+ df = messages_to_dataframe(messages)
261
+ print(df) # For console output
262
+ return history, "", df
263
+
264
+ with gr.Blocks() as demo:
265
+ gr.Markdown("# BlackBird Customer Support Chat")
266
+ chatbot = gr.Chatbot()
267
+ msg = gr.Textbox(label="Your message")
268
+ clear = gr.Button("Clear")
269
+ df_output = gr.Dataframe(label="Conversation Analysis")
270
+
271
+ submit_event = msg.submit(submit_message, [msg, chatbot], [chatbot, msg, df_output]).then(
272
+ lambda: "", None, msg
273
+ )
274
+
275
+ example_inputs = [
276
+ "What's the status of my orders? My Customer id is 2837622",
277
+ "Can you confirm my customer info and order status? My email is [email protected]",
278
+ "I'd like to cancel an order",
279
+ "Can you update my email address to [email protected]?",
280
+ ]
281
+
282
+ examples = gr.Examples(
283
+ examples=example_inputs,
284
+ inputs=msg
285
  )
286
+
287
+ clear.click(lambda: None, None, chatbot, queue=False)
288
+
289
+ demo.launch()