File size: 6,644 Bytes
141953b
f69cb76
141953b
 
 
 
f69cb76
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
141953b
 
 
 
e4de250
 
141953b
 
 
f69cb76
141953b
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
f69cb76
141953b
 
 
 
 
f69cb76
141953b
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
f69cb76
 
141953b
f69cb76
 
141953b
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
f69cb76
 
 
141953b
 
f69cb76
141953b
 
 
f69cb76
141953b
f69cb76
 
 
141953b
 
 
 
 
 
 
 
 
 
 
 
 
 
f69cb76
141953b
 
f69cb76
141953b
f69cb76
 
141953b
 
 
 
 
 
 
 
 
 
 
f69cb76
141953b
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
f69cb76
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
import os
from fastapi import FastAPI, Request, HTTPException, Form # Keep Form for now, though not used in webhook
import uvicorn
from gradio_client import Client
from fastapi.responses import Response
import json
import re
import asyncio
from pydantic import BaseModel # Import BaseModel

# Define Pydantic model for incoming Twilio webhook data
# Twilio typically sends 'From', 'Body', 'MessageSid', etc. as form data,
# but the previous log showed application/json. We'll adapt to expect JSON for now.
# If Twilio is configured to send JSON, the keys should match exactly.
# Assuming the keys are 'From' and 'Body' based on the previous error.
class TwilioWebhookData(BaseModel):
    From: str
    Body: str
    # Add other fields you expect in the JSON payload if needed, e.g.:
    # MessageSid: str = None
    # AccountSid: str = None
    # To: str = None


# Connect to your hosted Gradio Space (Futuresony/Mr.Events)
# This client is used by BOTH the /chat and /webhook endpoints to interact with the core chatbot
try:
    client = Client("Futuresony/ABSA_Test_Space")
    print("Gradio Client for 'Futuresony/ABSA_Test_Space' initialized.")
except Exception as e:
    print(f"Error initializing Gradio Client for 'Futuresony/Mr.Events': {e}")
    print("Ensure the Space name is correct and it is accessible.")
    client = None


# Get your secure API key for THIS FastAPI application and the hosted Space from environment
VALID_API_KEY = os.getenv("APP_API_KEY")
print(f"APP_API_KEY loaded: {'Yes' if VALID_API_KEY else 'No'}")
if not VALID_API_KEY:
    print("Warning: APP_API_KEY secret not set. API key validation and calls to hosted space may fail.")


app = FastAPI()

# --- Chat Endpoint (Existing Functionality) ---
@app.post("/chat")
async def chat(request: Request):
    """
    Handles chat requests via a JSON payload, validates API key,
    and calls the hosted Gradio chatbot.
    """
    print("\n--- Received POST request at /chat ---")
    data = await request.json()

    # API Key Check for THIS FastAPI application
    api_key = request.headers.get("X-API-Key")
    print(f"API Key from header: {api_key[:4]}...") if api_key else "No API Key in header"
    if not VALID_API_KEY or api_key != VALID_API_KEY:
        print("API Key validation failed.")
        raise HTTPException(status_code=403, detail="Invalid API Key")
    print("API Key validation successful.")

    user_message = data.get("message")
    if not user_message:
        print("Error: 'message' is required in the request body.")
        raise HTTPException(status_code=400, detail="Message is required")
    print(f"User message: {user_message}")

    if client is None:
        print("Error: Gradio Client not initialized. Cannot call chatbot.")
        raise HTTPException(status_code=500, detail="Chatbot service not available.")

    try:
        print(f"Calling hosted Gradio Space 'Futuresony/ABSA_Test_Space' /chat endpoint from /chat...")
        result = await client.predict(
            query=user_message,
            api_key=VALID_API_KEY,
            api_name="/chat"
        )
        print(f"Received raw result from hosted Space: {result}")

        assistant_response = result
        if not isinstance(assistant_response, str):
            print(f"Warning: Hosted Space returned unexpected result type: {type(assistant_response)}. Raw result: {result}")
            assistant_response = str(assistant_response)
        print(f"Formatted assistant response: {assistant_response}")

    except Exception as e:
        print(f"Error calling hosted Gradio Space from /chat: {e}")
        raise HTTPException(status_code=500, detail=f"Error communicating with chatbot model: {e}")

    return {"response": assistant_response}


# --- Twilio Webhook Endpoint ---
# In-memory dictionary to store history per sender (NOT for production!)
conversation_histories = {}

@app.post("/webhook")
async def webhook(
    # Receive JSON data using the Pydantic model
    data: TwilioWebhookData,
    request: Request = None
):
    """
    Handles incoming Twilio webhook requests (expecting JSON),
    processes them with the chatbot, and returns TwiML.
    Note: This implementation uses in-memory history (NOT for production).
    """
    print("\n--- Received POST request at /webhook from Twilio (expecting JSON) ---")

    # Access incoming message and sender number from the Pydantic model instance
    incoming_message = data.Body
    sender_number = data.From

    print(f"Parsed Incoming Message: '{incoming_message}' from {sender_number}")

    # --- Conversation History Management (In-Memory - NOT Persistent!) ---
    chat_history = conversation_histories.get(sender_number, [])
    print(f"Retrieved in-memory history for {sender_number}: {chat_history}")


    # --- Call Chatbot Logic ---
    if client is None:
        print("Error: Gradio Client not initialized. Cannot call chatbot from webhook.")
        bot_response = "Error: Chatbot service is not available."
    else:
        try:
             print(f"Calling hosted Gradio Space 'Futuresony/ABSA_Test_Space' /chat endpoint from /webhook...")
             print(f"  Query: {incoming_message}")

             result = await client.predict(
                query=incoming_message,
                api_key=VALID_API_KEY,
                api_name="/chat"
             )
             print(f"Received raw result from hosted Space for webhook: {result}")

             bot_response = result
             if not isinstance(bot_response, str):
                 print(f"Warning: Hosted Space returned unexpected result type for webhook: {type(bot_response)}. Raw result: {result}")
                 bot_response = str(bot_response)
             print(f"Formatted chatbot response for webhook: {bot_response}")

        except Exception as e:
             print(f"Error calling hosted Gradio Space from /webhook: {e}")
             bot_response = f"An error occurred while processing your request: {e}"


    # --- Update and Store History (In-Memory - NOT Persistent!) ---
    chat_history.append([incoming_message, bot_response])
    conversation_histories[sender_number] = chat_history
    print(f"Updated in-memory history for {sender_number}: {conversation_histories[sender_number]}")


    # --- Generate TwiML Response ---
    twiml_response = f'''<Response><Message>{bot_response}</Message></Response>'''
    print(f"Generated TwiML response: {twiml_response}")

    return Response(content=twiml_response, media_type="application/xml")


if __name__ == "__main__":
    print("Starting FastAPI application with Uvicorn...")
    uvicorn.run(app, host="0.0.0.0", port=7860)