Futuresony commited on
Commit
f69cb76
·
verified ·
1 Parent(s): e4de250

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +39 -77
app.py CHANGED
@@ -1,11 +1,26 @@
1
  import os
2
- from fastapi import FastAPI, Request, HTTPException, Form
3
  import uvicorn
4
  from gradio_client import Client
5
  from fastapi.responses import Response
6
  import json
7
- import re # Import re for potential future use (e.g., parsing messages)
8
- import asyncio # Import asyncio for async operations
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
9
 
10
  # Connect to your hosted Gradio Space (Futuresony/Mr.Events)
11
  # This client is used by BOTH the /chat and /webhook endpoints to interact with the core chatbot
@@ -15,13 +30,11 @@ try:
15
  except Exception as e:
16
  print(f"Error initializing Gradio Client for 'Futuresony/Mr.Events': {e}")
17
  print("Ensure the Space name is correct and it is accessible.")
18
- client = None # Set client to None if initialization fails
19
 
20
 
21
  # Get your secure API key for THIS FastAPI application and the hosted Space from environment
22
- # Assuming the same API key (APP_API_KEY) is used for both.
23
  VALID_API_KEY = os.getenv("APP_API_KEY")
24
- # Add a print statement to confirm if the API key is loaded
25
  print(f"APP_API_KEY loaded: {'Yes' if VALID_API_KEY else 'No'}")
26
  if not VALID_API_KEY:
27
  print("Warning: APP_API_KEY secret not set. API key validation and calls to hosted space may fail.")
@@ -34,102 +47,75 @@ app = FastAPI()
34
  async def chat(request: Request):
35
  """
36
  Handles chat requests via a JSON payload, validates API key,
37
- and calls the hosted Gradio chatbot with history.
38
  """
39
  print("\n--- Received POST request at /chat ---")
40
  data = await request.json()
41
 
42
  # API Key Check for THIS FastAPI application
43
- api_key = request.headers.get("X-API-Key") # Get API key from headers
44
  print(f"API Key from header: {api_key[:4]}...") if api_key else "No API Key in header"
45
  if not VALID_API_KEY or api_key != VALID_API_KEY:
46
  print("API Key validation failed.")
47
  raise HTTPException(status_code=403, detail="Invalid API Key")
48
  print("API Key validation successful.")
49
 
50
- # Get user message
51
  user_message = data.get("message")
52
  if not user_message:
53
  print("Error: 'message' is required in the request body.")
54
  raise HTTPException(status_code=400, detail="Message is required")
55
  print(f"User message: {user_message}")
56
 
57
-
58
- # Get chat history (assuming it's sent in the request body for stateless API)
59
- # The chat_history is expected to be a list of lists: [[user_msg, bot_msg], ...]
60
- # If not provided, initialize as empty list.
61
- chat_history = data.get("chat_history", [])
62
- # print(f"Received chat history: {chat_history}") # Be cautious logging history
63
-
64
-
65
- # --- Call the hosted Gradio chatbot ---
66
  if client is None:
67
  print("Error: Gradio Client not initialized. Cannot call chatbot.")
68
  raise HTTPException(status_code=500, detail="Chatbot service not available.")
69
 
70
  try:
71
- print(f"Calling hosted Gradio Space 'Futuresony/Mr.Events' /chat endpoint from /chat...")
72
- # Note: The Gradio ChatInterface API typically expects query (current message)
73
- # and chat_history (history *before* the current turn).
74
- # Use the same VALID_API_KEY for the hosted space call
75
- result = await client.predict( # Use await because client.predict can be async
76
  query=user_message,
77
- # chat_history=chat_history, # Remove chat_history parameter
78
- api_key=VALID_API_KEY, # Pass the APP_API_KEY to the hosted space
79
- api_name="/chat" # Ensure this matches the API endpoint exposed by the hosted Gradio app
80
  )
81
  print(f"Received raw result from hosted Space: {result}")
82
 
83
- # The result from client.predict on a ChatInterface is typically the assistant's response string
84
  assistant_response = result
85
  if not isinstance(assistant_response, str):
86
  print(f"Warning: Hosted Space returned unexpected result type: {type(assistant_response)}. Raw result: {result}")
87
- # Attempt to convert to string or handle appropriately
88
  assistant_response = str(assistant_response)
89
  print(f"Formatted assistant response: {assistant_response}")
90
 
91
-
92
  except Exception as e:
93
  print(f"Error calling hosted Gradio Space from /chat: {e}")
94
  raise HTTPException(status_code=500, detail=f"Error communicating with chatbot model: {e}")
95
 
96
-
97
  return {"response": assistant_response}
98
 
99
 
100
  # --- Twilio Webhook Endpoint ---
101
  # In-memory dictionary to store history per sender (NOT for production!)
102
- # Replace this with a persistent storage solution (database, file storage) for production.
103
  conversation_histories = {}
104
- # For production-level history management, you would initialize and interact with
105
- # a database or other persistent storage here.
106
 
107
  @app.post("/webhook")
108
  async def webhook(
109
- # Explicitly receive form data parameters expected from Twilio
110
- From: str = Form(...), # Sender's phone number
111
- Body: str = Form(...), # Message content
112
- # Twilio sends other parameters like MessageSid, To, AccountSid, etc.
113
- # You can receive them here if needed:
114
- # MessageSid: str = Form(None),
115
- # To: str = Form(None),
116
- request: Request = None # Keep request for raw access if needed
117
  ):
118
  """
119
- Handles incoming Twilio webhook requests for new messages,
120
  processes them with the chatbot, and returns TwiML.
121
  Note: This implementation uses in-memory history (NOT for production).
122
  """
123
- print("\n--- Received POST request at /webhook from Twilio ---")
124
 
125
- # Access the incoming message and sender number directly from Form parameters
126
- incoming_message = Body
127
- sender_number = From
128
 
129
  print(f"Parsed Incoming Message: '{incoming_message}' from {sender_number}")
130
 
131
  # --- Conversation History Management (In-Memory - NOT Persistent!) ---
132
- # In a real application, you would load/save history from a database/file.
133
  chat_history = conversation_histories.get(sender_number, [])
134
  print(f"Retrieved in-memory history for {sender_number}: {chat_history}")
135
 
@@ -140,18 +126,13 @@ async def webhook(
140
  bot_response = "Error: Chatbot service is not available."
141
  else:
142
  try:
143
- # Use the same VALID_API_KEY for the hosted space call from webhook
144
- print(f"Calling hosted Gradio Space 'Futuresony/Mr.Events' /chat endpoint from /webhook...")
145
  print(f" Query: {incoming_message}")
146
- # print(f" History: {chat_history}") # Be cautious logging history
147
 
148
- # Call the hosted chatbot with the retrieved history
149
- # Gradio client expects query (current message) and chat_history (history *before* current turn)
150
- result = await client.predict( # Use await
151
  query=incoming_message,
152
- # chat_history=chat_history, # Remove chat_history parameter
153
- api_key=VALID_API_KEY, # Pass the APP_API_KEY to the hosted space
154
- api_name="/chat" # Ensure this matches the API endpoint exposed by the hosted Gradio app
155
  )
156
  print(f"Received raw result from hosted Space for webhook: {result}")
157
 
@@ -163,41 +144,22 @@ async def webhook(
163
 
164
  except Exception as e:
165
  print(f"Error calling hosted Gradio Space from /webhook: {e}")
166
- bot_response = f"An error occurred while processing your request: {e}" # Provide a user-friendly error message
167
 
168
 
169
  # --- Update and Store History (In-Memory - NOT Persistent!) ---
170
- # Append the current turn (user message + bot response)
171
  chat_history.append([incoming_message, bot_response])
172
  conversation_histories[sender_number] = chat_history
173
  print(f"Updated in-memory history for {sender_number}: {conversation_histories[sender_number]}")
174
 
175
 
176
  # --- Generate TwiML Response ---
177
- # Twilio expects TwiML XML to know what to do with the message
178
- # Use f-string with triple single quotes for multi-line string to avoid conflicts with HTML-like tags
179
  twiml_response = f'''<Response><Message>{bot_response}</Message></Response>'''
180
  print(f"Generated TwiML response: {twiml_response}")
181
 
182
- # Return TwiML with the correct media type
183
  return Response(content=twiml_response, media_type="application/xml")
184
 
185
 
186
  if __name__ == "__main__":
187
- # When running this app.py directly (e.g., with `uvicorn app:app --reload`),
188
- # this block is executed. On Hugging Face Spaces, the environment typically
189
- # runs the FastAPI application directly without executing this block.
190
- # If you need specific initializations (like loading RAG data, initializing cache)
191
- # when running on Spaces via FastAPI directly, you might need to move them
192
- # outside this __main__ block or ensure they are called on app startup.
193
-
194
- # Example (commented out, adjust based on your needs):
195
- # from app_components import authenticate_google_sheets, load_business_info, initialize_cache
196
- # authenticate_google_sheets()
197
- # load_business_info()
198
- # initialize_cache()
199
- # cleanup_expired_cache_entries() # Optional
200
-
201
-
202
  print("Starting FastAPI application with Uvicorn...")
203
- uvicorn.run(app, host="0.0.0.0", port=7860) # HF default port
 
1
  import os
2
+ from fastapi import FastAPI, Request, HTTPException, Form # Keep Form for now, though not used in webhook
3
  import uvicorn
4
  from gradio_client import Client
5
  from fastapi.responses import Response
6
  import json
7
+ import re
8
+ import asyncio
9
+ from pydantic import BaseModel # Import BaseModel
10
+
11
+ # Define Pydantic model for incoming Twilio webhook data
12
+ # Twilio typically sends 'From', 'Body', 'MessageSid', etc. as form data,
13
+ # but the previous log showed application/json. We'll adapt to expect JSON for now.
14
+ # If Twilio is configured to send JSON, the keys should match exactly.
15
+ # Assuming the keys are 'From' and 'Body' based on the previous error.
16
+ class TwilioWebhookData(BaseModel):
17
+ From: str
18
+ Body: str
19
+ # Add other fields you expect in the JSON payload if needed, e.g.:
20
+ # MessageSid: str = None
21
+ # AccountSid: str = None
22
+ # To: str = None
23
+
24
 
25
  # Connect to your hosted Gradio Space (Futuresony/Mr.Events)
26
  # This client is used by BOTH the /chat and /webhook endpoints to interact with the core chatbot
 
30
  except Exception as e:
31
  print(f"Error initializing Gradio Client for 'Futuresony/Mr.Events': {e}")
32
  print("Ensure the Space name is correct and it is accessible.")
33
+ client = None
34
 
35
 
36
  # Get your secure API key for THIS FastAPI application and the hosted Space from environment
 
37
  VALID_API_KEY = os.getenv("APP_API_KEY")
 
38
  print(f"APP_API_KEY loaded: {'Yes' if VALID_API_KEY else 'No'}")
39
  if not VALID_API_KEY:
40
  print("Warning: APP_API_KEY secret not set. API key validation and calls to hosted space may fail.")
 
47
  async def chat(request: Request):
48
  """
49
  Handles chat requests via a JSON payload, validates API key,
50
+ and calls the hosted Gradio chatbot.
51
  """
52
  print("\n--- Received POST request at /chat ---")
53
  data = await request.json()
54
 
55
  # API Key Check for THIS FastAPI application
56
+ api_key = request.headers.get("X-API-Key")
57
  print(f"API Key from header: {api_key[:4]}...") if api_key else "No API Key in header"
58
  if not VALID_API_KEY or api_key != VALID_API_KEY:
59
  print("API Key validation failed.")
60
  raise HTTPException(status_code=403, detail="Invalid API Key")
61
  print("API Key validation successful.")
62
 
 
63
  user_message = data.get("message")
64
  if not user_message:
65
  print("Error: 'message' is required in the request body.")
66
  raise HTTPException(status_code=400, detail="Message is required")
67
  print(f"User message: {user_message}")
68
 
 
 
 
 
 
 
 
 
 
69
  if client is None:
70
  print("Error: Gradio Client not initialized. Cannot call chatbot.")
71
  raise HTTPException(status_code=500, detail="Chatbot service not available.")
72
 
73
  try:
74
+ print(f"Calling hosted Gradio Space 'Futuresony/ABSA_Test_Space' /chat endpoint from /chat...")
75
+ result = await client.predict(
 
 
 
76
  query=user_message,
77
+ api_key=VALID_API_KEY,
78
+ api_name="/chat"
 
79
  )
80
  print(f"Received raw result from hosted Space: {result}")
81
 
 
82
  assistant_response = result
83
  if not isinstance(assistant_response, str):
84
  print(f"Warning: Hosted Space returned unexpected result type: {type(assistant_response)}. Raw result: {result}")
 
85
  assistant_response = str(assistant_response)
86
  print(f"Formatted assistant response: {assistant_response}")
87
 
 
88
  except Exception as e:
89
  print(f"Error calling hosted Gradio Space from /chat: {e}")
90
  raise HTTPException(status_code=500, detail=f"Error communicating with chatbot model: {e}")
91
 
 
92
  return {"response": assistant_response}
93
 
94
 
95
  # --- Twilio Webhook Endpoint ---
96
  # In-memory dictionary to store history per sender (NOT for production!)
 
97
  conversation_histories = {}
 
 
98
 
99
  @app.post("/webhook")
100
  async def webhook(
101
+ # Receive JSON data using the Pydantic model
102
+ data: TwilioWebhookData,
103
+ request: Request = None
 
 
 
 
 
104
  ):
105
  """
106
+ Handles incoming Twilio webhook requests (expecting JSON),
107
  processes them with the chatbot, and returns TwiML.
108
  Note: This implementation uses in-memory history (NOT for production).
109
  """
110
+ print("\n--- Received POST request at /webhook from Twilio (expecting JSON) ---")
111
 
112
+ # Access incoming message and sender number from the Pydantic model instance
113
+ incoming_message = data.Body
114
+ sender_number = data.From
115
 
116
  print(f"Parsed Incoming Message: '{incoming_message}' from {sender_number}")
117
 
118
  # --- Conversation History Management (In-Memory - NOT Persistent!) ---
 
119
  chat_history = conversation_histories.get(sender_number, [])
120
  print(f"Retrieved in-memory history for {sender_number}: {chat_history}")
121
 
 
126
  bot_response = "Error: Chatbot service is not available."
127
  else:
128
  try:
129
+ print(f"Calling hosted Gradio Space 'Futuresony/ABSA_Test_Space' /chat endpoint from /webhook...")
 
130
  print(f" Query: {incoming_message}")
 
131
 
132
+ result = await client.predict(
 
 
133
  query=incoming_message,
134
+ api_key=VALID_API_KEY,
135
+ api_name="/chat"
 
136
  )
137
  print(f"Received raw result from hosted Space for webhook: {result}")
138
 
 
144
 
145
  except Exception as e:
146
  print(f"Error calling hosted Gradio Space from /webhook: {e}")
147
+ bot_response = f"An error occurred while processing your request: {e}"
148
 
149
 
150
  # --- Update and Store History (In-Memory - NOT Persistent!) ---
 
151
  chat_history.append([incoming_message, bot_response])
152
  conversation_histories[sender_number] = chat_history
153
  print(f"Updated in-memory history for {sender_number}: {conversation_histories[sender_number]}")
154
 
155
 
156
  # --- Generate TwiML Response ---
 
 
157
  twiml_response = f'''<Response><Message>{bot_response}</Message></Response>'''
158
  print(f"Generated TwiML response: {twiml_response}")
159
 
 
160
  return Response(content=twiml_response, media_type="application/xml")
161
 
162
 
163
  if __name__ == "__main__":
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
164
  print("Starting FastAPI application with Uvicorn...")
165
+ uvicorn.run(app, host="0.0.0.0", port=7860)