Futuresony commited on
Commit
ab390c2
·
verified ·
1 Parent(s): 528eda9

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +90 -81
app.py CHANGED
@@ -1,98 +1,107 @@
1
  import os
2
- import time
3
- import logging
4
- from fastapi import FastAPI, Request, HTTPException, Depends
5
- from fastapi.responses import JSONResponse
6
- from fastapi.concurrency import run_in_threadpool
7
- from gradio_client import Client
8
  import uvicorn
9
- from typing import Dict
10
-
11
- # ---------------- CONFIG ----------------
12
- VALID_API_KEY = os.getenv("APP_API_KEY") # Your FastAPI security key
13
- SPACE_API_KEY = os.getenv("SPACE_API_KEY") # Hugging Face Space key
14
- SPACE_NAME = os.getenv("SPACE_NAME", "Futuresony/Mr.Events") # default space
15
-
16
- if not VALID_API_KEY or not SPACE_API_KEY:
17
- raise RuntimeError("❌ APP_API_KEY and SPACE_API_KEY must be set as environment variables.")
18
 
19
- # ---------------- LOGGER ----------------
20
- logging.basicConfig(
21
- level=logging.INFO,
22
- format="%(asctime)s [%(levelname)s] %(message)s",
23
- )
24
- logger = logging.getLogger("prod-api")
25
 
26
- # ---------------- CLIENT ----------------
27
- client = Client(SPACE_NAME)
 
 
 
 
 
 
 
 
28
 
29
- # ---------------- FASTAPI APP ----------------
30
  app = FastAPI(
31
- title="Production Chat API",
32
- description="Secure API that connects to Hugging Face Space",
33
- version="1.0.0"
34
  )
35
 
36
- # ---------------- RATE LIMITER ----------------
37
- RATE_LIMIT = 5 # max requests
38
- WINDOW = 60 # per 60 seconds
39
- client_requests: Dict[str, list] = {} # tracks requests per IP
40
-
41
- def rate_limiter(request: Request):
42
- client_ip = request.client.host
43
- now = time.time()
44
-
45
- if client_ip not in client_requests:
46
- client_requests[client_ip] = []
47
-
48
- # remove old requests outside time window
49
- client_requests[client_ip] = [
50
- t for t in client_requests[client_ip] if now - t < WINDOW
51
- ]
52
-
53
- if len(client_requests[client_ip]) >= RATE_LIMIT:
54
- logger.warning(f"Rate limit exceeded for {client_ip}")
55
- raise HTTPException(status_code=429, detail="Too many requests. Try again later.")
56
-
57
- client_requests[client_ip].append(now)
58
-
59
- # ---------------- ROUTES ----------------
60
- @app.post("/chat")
61
- async def chat(request: Request, _: None = Depends(rate_limiter)):
62
- data = await request.json()
63
-
64
- # API key check
65
- api_key = request.headers.get("X-API-Key") or data.get("api_key")
66
- if api_key != VALID_API_KEY:
67
- logger.warning("Unauthorized access attempt.")
68
- raise HTTPException(status_code=403, detail="Invalid API Key")
69
-
70
- user_message = data.get("message")
71
- if not user_message:
72
- raise HTTPException(status_code=400, detail="Message is required")
73
-
74
  try:
75
- logger.info(f"Processing request from {request.client.host}: {user_message}")
76
-
77
- # Call Hugging Face Space
78
- result = await run_in_threadpool(
79
- client.predict,
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
80
  query=user_message,
81
- api_key=SPACE_API_KEY,
 
82
  api_name="/chat"
83
  )
84
 
85
- logger.info(f"Response delivered to {request.client.host}")
86
- return {"response": result}
 
 
 
 
 
 
 
 
 
 
 
 
87
 
88
  except Exception as e:
89
- logger.error(f"Space call failed: {str(e)}")
90
- return JSONResponse(
91
- status_code=500,
92
- content={"error": "Internal Server Error", "details": str(e)},
93
- )
94
 
95
- # ---------------- ENTRY ----------------
96
  if __name__ == "__main__":
97
- logger.info("🚀 Starting Production Chat API...")
98
- uvicorn.run("main:app", host="0.0.0.0", port=7860, reload=False, workers=2)
 
 
 
 
 
 
1
  import os
2
+ import json
3
+ import traceback # Import traceback for detailed error logging
4
+ from fastapi import FastAPI, Request, HTTPException, status # Import status for clearer HTTP status codes
 
 
 
5
  import uvicorn
6
+ from gradio_client import Client
 
 
 
 
 
 
 
 
7
 
8
+ # Keys
9
+ VALID_API_KEY = os.getenv("APP_API_KEY") # for your FastAPI security
 
 
 
 
10
 
11
+ # Connect to hosted space
12
+ # Ensure this URL is correct and accessible
13
+ GRADIO_SPACE_URL = "Futuresony/Mr.Events"
14
+ try:
15
+ client = Client(GRADIO_SPACE_URL)
16
+ print(f"Successfully connected to Gradio Space: {GRADIO_SPACE_URL}")
17
+ except Exception as e:
18
+ print(f"Error connecting to Gradio Space at {GRADIO_SPACE_URL}: {e}")
19
+ print(traceback.format_exc())
20
+ client = None # Set client to None if connection fails
21
 
 
22
  app = FastAPI(
23
+ title="LLM with Tools API",
24
+ description="API to interact with the LLM chatbot with tool capabilities.",
25
+ version="1.0.0",
26
  )
27
 
28
+ @app.post("/chat", summary="Send a message to the chatbot")
29
+ async def chat(request: Request):
30
+ """
31
+ Processes incoming chat messages, validates the API key,
32
+ and forwards the request to the Gradio Space for processing.
33
+ Returns the chatbot's response.
34
+ """
35
+ print("\n--- Received new request to /chat ---")
36
+ api_key = None
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
37
  try:
38
+ data = await request.json()
39
+ print(f"Request body: {data}")
40
+
41
+ # API key check from headers or JSON
42
+ api_key = request.headers.get("X-API-Key") or data.get("api_key")
43
+ print(f"API Key received: {'Yes' if api_key else 'No'}")
44
+ print(f"Validating against configured API Key: {'Yes' if VALID_API_KEY else 'No'}")
45
+
46
+ if not api_key or api_key != VALID_API_KEY:
47
+ print("API key validation failed.")
48
+ raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="Invalid or missing API Key")
49
+
50
+ user_message = data.get("message")
51
+ if not user_message:
52
+ print("Error: 'message' field is required.")
53
+ raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="'message' field is required")
54
+
55
+ # Although the Gradio space loads history internally using the API key,
56
+ # you might consider if you need to pass any history from the FastAPI client.
57
+ # For this setup, we rely on the Space's internal history management per API key.
58
+ # Passing an empty list is acceptable if the Space ignores it and uses its internal state.
59
+ chat_history = [] # Assuming the Space manages history internally based on api_key
60
+
61
+ if client is None:
62
+ print("Error: Gradio client is not initialized.")
63
+ raise HTTPException(status_code=status.HTTP_503_SERVICE_UNAVAILABLE, detail="Chat service is unavailable. Gradio client failed to initialize.")
64
+
65
+ print(f"Calling Gradio Space API /chat with message='{user_message}' and api_key='{api_key}'...")
66
+
67
+ # Call your hosted Space
68
+ # Pass the api_key received in the FastAPI request to the Gradio chat function
69
+ # Ensure the parameter names match your Gradio function signature if using keywords
70
+ # The gradio_client's predict method maps arguments positionally unless keywords are used and matched by the target function
71
+ # Assuming the Gradio `chat` function signature is chat(query, chat_history, api_key)
72
+ # If your Gradio Space's chat function parameters are different, adjust the keywords here.
73
+ result = client.predict(
74
  query=user_message,
75
+ chat_history=chat_history, # Pass the history (empty or from request)
76
+ api_key=api_key, # Pass the received api_key for history separation
77
  api_name="/chat"
78
  )
79
 
80
+ print(f"Received response from Gradio Space: {result}")
81
+
82
+ # Assuming the response from the Space is the final text response
83
+ # If the Space returns a more complex object, you might need to extract the text.
84
+ if isinstance(result, str):
85
+ return {"response": result}
86
+ else:
87
+ # Handle cases where the Gradio space might return something other than a string
88
+ print(f"Warning: Gradio Space returned non-string result: {result}")
89
+ return {"response": json.dumps(result)} # Return as JSON string if it's not a simple string
90
+
91
+ except HTTPException as http_exc:
92
+ print(f"HTTP Exception: {http_exc.detail}")
93
+ raise http_exc # Re-raise FastAPI HTTP exceptions
94
 
95
  except Exception as e:
96
+ print(f"An unexpected error occurred: {e}")
97
+ print(traceback.format_exc()) # Log the full traceback
98
+ raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=f"An internal server error occurred: {e}")
 
 
99
 
 
100
  if __name__ == "__main__":
101
+ # Ensure this is run in a suitable environment for uvicorn (e.g., not directly in Colab)
102
+ # If running in a production environment like a Docker container or a proper server,
103
+ # you would typically use a command like 'uvicorn main:app --host 0.0.0.0 --port 80'
104
+ # This block is mainly for local testing.
105
+ print("Starting FastAPI server with uvicorn...")
106
+ #uvicorn.run(app, host="0.0.0.0", port=7860) # Commented out for Colab execution
107
+ print("Uvicorn run command is commented out. To run the FastAPI server, execute this script in a suitable environment.")