Spaces:
Sleeping
Sleeping
# import gradio as gr | |
# from src.main import app as fastapi_app | |
# from src.core.recommender import recommender | |
# import logging | |
# from src.database.mongodb import mongodb | |
# logger = logging.getLogger(__name__) | |
# # Configure basic logging if not already set up elsewhere for Gradio context | |
# logging.basicConfig(level=logging.INFO) | |
# # Manually initialize and attach recommender if not already present | |
# if not hasattr(fastapi_app.state, "recommender"): | |
# recommender.load_components() | |
# fastapi_app.state.recommender = recommender | |
# def get_recommendations_gradio(query: str, k: int = 5): | |
# try: | |
# response = fastapi_app.state.recommender.get_recommendations(query, k) | |
# return response | |
# except Exception as e: | |
# return {"error": str(e)} | |
# def get_recommendations_by_id_gradio(msid: str, k: int = 5): | |
# try: | |
# # Corrected method name here | |
# response = fastapi_app.state.recommender.get_recommendations_by_id(msid, k) | |
# return response | |
# except Exception as e: | |
# return {"error": str(e)} | |
# def get_recommendations_user_feedback_gradio(user_id: str, msid: str, clicked_msid: str, k: int = 5): | |
# """ | |
# Handles user feedback via Gradio: | |
# 1. (Optionally) Computes recommendations based on the feedback (mimicking FastAPI endpoint action). | |
# 2. Saves the feedback to MongoDB. | |
# 3. Returns a success message. | |
# """ | |
# try: | |
# # Step 1: (Optional) Compute recommendations based on feedback, similar to FastAPI endpoint. | |
# # The result of this call is not the final output for this Gradio UI, per the request. | |
# # This ensures the Gradio function performs similar actions to the API endpoint. | |
# _ = fastapi_app.state.recommender.get_recommendations_user_feedback(user_id, msid, clicked_msid, k) | |
# logger.info(f"Gradio: (Computed recommendations for user '{user_id}' based on click, not shown in this UI)") | |
# # Step 2: Save feedback to MongoDB | |
# actual_clicked_msids = [s.strip() for s in clicked_msid.split(',') if s.strip()] | |
# if not actual_clicked_msids: | |
# logger.warning(f"Gradio: Invalid clicked_msid: {clicked_msid} for user {user_id}") | |
# return {"error": "clicked_msid parameter is invalid or does not contain valid MSIDs."} | |
# logger.info( | |
# f"Gradio: Saving feedback for user '{user_id}', context msid: '{msid}', clicked msids: {actual_clicked_msids}" | |
# ) | |
# feedback_collection_name = "user_feedback_tracking" | |
# # Assuming mongodb.db is the PyMongo Database object, consistent with recommender.py | |
# feedback_collection = mongodb.db[feedback_collection_name] | |
# user_doc = feedback_collection.find_one({"user_id": user_id}) | |
# if user_doc: | |
# # This update logic mirrors the one in routes.py | |
# feedback_collection.update_one( | |
# {"user_id": user_id}, | |
# {"$addToSet": {"Articles": {"msid": msid, "Read": {"$each": actual_clicked_msids}}}} | |
# ) | |
# else: | |
# feedback_collection.insert_one({ | |
# "user_id": user_id, | |
# "Articles": [{"msid": msid, "Read": actual_clicked_msids}] | |
# }) | |
# logger.info(f"Gradio: Successfully saved feedback for user {user_id}") | |
# return {"message": "Response saved successfully"} | |
# except Exception as e: | |
# logger.error(f"Gradio: Error in get_recommendations_user_feedback_gradio for user {user_id}: {e}", exc_info=True) | |
# return {"error": f"An error occurred: {str(e)}"} | |
# def get_recommendations_summary_gradio(msid: str, k: int = 5, summary: bool = True, smart_tip: bool = True): | |
# try: | |
# response = fastapi_app.state.recommender.get_recommendations_summary(msid, k, summary, smart_tip) | |
# return response | |
# except Exception as e: | |
# return {"error": str(e)} | |
# iface1 = gr.Interface( | |
# fn=get_recommendations_gradio, | |
# inputs=[ | |
# gr.Textbox(label="Query", placeholder="Enter your search query..."), | |
# gr.Slider(minimum=1, maximum=10, value=5, step=1, label="Number of recommendations") | |
# ], | |
# outputs=gr.JSON(), | |
# title="Recommendation System", | |
# description="Enter a query to get personalized recommendations." | |
# ) | |
# iface2 = gr.Interface( | |
# fn=get_recommendations_by_id_gradio, | |
# inputs=[ | |
# gr.Textbox(label="MSID", placeholder="Enter the MSID..."), | |
# gr.Slider(minimum=1, maximum=10, value=5, step=1, label="Number of recommendations") | |
# ], | |
# outputs=gr.JSON(), | |
# title="Recommendations by MSID", | |
# description="Enter an MSID to get recommendations based on it." | |
# ) | |
# iface3 = gr.Interface( | |
# fn=get_recommendations_user_feedback_gradio, | |
# inputs=[ | |
# gr.Textbox(label="User ID", placeholder="Enter your user ID..."), | |
# gr.Textbox(label="MSID", placeholder="Enter the MSID..."), | |
# gr.Textbox(label="Clicked MSID", placeholder="Enter the clicked MSID..."), | |
# gr.Slider(minimum=1, maximum=10, value=5, step=1, label="Number of recommendations") | |
# ], | |
# outputs=gr.JSON(), | |
# title="User Feedback Recommendations", | |
# description="Enter your user ID, MSID, and clicked MSID to get recommendations based on user feedback." | |
# ) | |
# iface4 = gr.Interface( | |
# fn=get_recommendations_summary_gradio, | |
# inputs=[ | |
# gr.Textbox(label="MSID", placeholder="Enter the MSID..."), | |
# gr.Slider(minimum=1, maximum=10, value=5, step=1, label="Number of recommendations"), | |
# gr.Checkbox(label="Summary", value=True), | |
# gr.Checkbox(label="Smart Tip", value=True) | |
# ], | |
# outputs=gr.JSON(), | |
# title="Recommendations Summary", | |
# description="Enter an MSID to get a summary of recommendations." | |
# ) | |
# demo = gr.TabbedInterface([iface1, iface2, iface3, iface4], ["Query Recommendations", "MSID Recommendations", "User Feedback Recommendations", "Recommendations Summary"]) | |
# if __name__ == "__main__": | |
# demo.launch() | |
# app = fastapi_app | |
import logging | |
import uvicorn | |
from fastapi import HTTPException | |
from pydantic import BaseModel, Field | |
from typing import List | |
from src.main import app as fastapi_app # Existing FastAPI app instance | |
from src.core.recommender import recommender | |
from src.database.mongodb import mongodb | |
logger = logging.getLogger(__name__) | |
# Configure basic logging if not already set up elsewhere | |
if not logger.hasHandlers(): | |
logging.basicConfig(level=logging.INFO) | |
# Manually initialize and attach recommender to the imported fastapi_app's state | |
# This will run once when the module is loaded. | |
if not hasattr(fastapi_app.state, "recommender") or fastapi_app.state.recommender is None: | |
logger.info("Recommender not found on fastapi_app.state, loading components...") | |
recommender.load_components() | |
fastapi_app.state.recommender = recommender | |
if hasattr(fastapi_app.state, "recommender") and fastapi_app.state.recommender is not None: | |
logger.info("Recommender loaded successfully onto fastapi_app.state.") | |
else: | |
logger.error("Failed to load recommender onto fastapi_app.state.") | |
# Depending on the application's needs, you might want to raise an error here | |
# or prevent the app from starting if the recommender is critical. | |
# Pydantic models for request/response bodies | |
class FeedbackPayload(BaseModel): | |
user_id: str | |
msid: str | |
clicked_msid: str # Comma-separated string of MSIDs | |
k: int = Field(default=5, ge=1, le=10) | |
class FeedbackResponse(BaseModel): | |
message: str | |
# API Endpoints | |
async def get_recommendations_api(query: str, k: int = 5): | |
""" | |
Get recommendations based on a textual query. | |
""" | |
try: | |
if not hasattr(fastapi_app.state, "recommender") or fastapi_app.state.recommender is None: | |
logger.error("Recommender is not available.") | |
raise HTTPException(status_code=503, detail="Recommender service not available") | |
response = fastapi_app.state.recommender.get_recommendations(query, k) | |
return response | |
except Exception as e: | |
logger.error(f"API Error in get_recommendations_api for query '{query}': {e}", exc_info=True) | |
raise HTTPException(status_code=500, detail=f"An error occurred: {str(e)}") | |
async def get_recommendations_by_id_api(msid: str, k: int = 5): | |
""" | |
Get recommendations based on a given MSID. | |
""" | |
try: | |
# Validate input parameters | |
if not msid or not isinstance(msid, str): | |
raise HTTPException(status_code=400, detail="Invalid MSID provided") | |
if not isinstance(k, int) or k < 1 or k > 10: | |
raise HTTPException(status_code=400, detail="k must be an integer between 1 and 10") | |
# Check if recommender service is available | |
if not hasattr(fastapi_app.state, "recommender") or fastapi_app.state.recommender is None: | |
logger.error("Recommender is not available.") | |
raise HTTPException(status_code=503, detail="Recommender service not available") | |
# Get recommendations with error handling | |
try: | |
response = fastapi_app.state.recommender.get_recommendations_by_id(msid, k) | |
if not response: | |
raise HTTPException(status_code=404, detail=f"No recommendations found for MSID: {msid}") | |
return response | |
except ValueError as ve: | |
logger.error(f"Value error in get_recommendations_by_id for msid '{msid}': {ve}") | |
raise HTTPException(status_code=400, detail=str(ve)) | |
except Exception as e: | |
logger.error(f"Error getting recommendations for msid '{msid}': {e}", exc_info=True) | |
raise HTTPException(status_code=500, detail="Internal server error while getting recommendations") | |
except HTTPException as he: | |
raise he | |
except Exception as e: | |
logger.error(f"Unexpected error in get_recommendations_by_id_api for msid '{msid}': {e}", exc_info=True) | |
raise HTTPException(status_code=500, detail="An unexpected error occurred") | |
async def submit_user_feedback_api(payload: FeedbackPayload): | |
""" | |
Submit user feedback (e.g., clicked articles) and save it. | |
Optionally, this endpoint can also trigger re-computation of recommendations based on feedback, | |
though the primary response here is the status of feedback submission. | |
""" | |
try: | |
if not hasattr(fastapi_app.state, "recommender") or fastapi_app.state.recommender is None: | |
logger.error("Recommender is not available.") | |
raise HTTPException(status_code=503, detail="Recommender service not available") | |
# (Optional) Compute recommendations based on feedback, similar to Gradio function. | |
# The result of this call is not the primary output of this API endpoint. | |
_ = fastapi_app.state.recommender.get_recommendations_user_feedback( | |
payload.user_id, payload.msid, payload.clicked_msid, payload.k | |
) | |
logger.info(f"API: (Computed recommendations for user '{payload.user_id}' based on click, not part of this response)") | |
# Save feedback to MongoDB | |
actual_clicked_msids = [s.strip() for s in payload.clicked_msid.split(',') if s.strip()] | |
if not actual_clicked_msids: | |
logger.warning(f"API: Invalid clicked_msid: '{payload.clicked_msid}' for user '{payload.user_id}'") | |
raise HTTPException(status_code=400, detail="clicked_msid parameter is invalid or does not contain valid MSIDs.") | |
logger.info( | |
f"API: Saving feedback for user '{payload.user_id}', context msid: '{payload.msid}', clicked msids: {actual_clicked_msids}" | |
) | |
feedback_collection_name = "user_feedback_tracking" | |
# Assuming mongodb.db is the PyMongo Database object | |
if mongodb.db is None: | |
logger.error("MongoDB database connection is not available.") | |
raise HTTPException(status_code=503, detail="Database service not available") | |
feedback_collection = mongodb.db[feedback_collection_name] | |
user_doc = feedback_collection.find_one({"user_id": payload.user_id}) | |
if user_doc: | |
feedback_collection.update_one( | |
{"user_id": payload.user_id}, | |
{"$addToSet": {"Articles": {"msid": payload.msid, "Read": actual_clicked_msids}}} | |
) | |
else: | |
feedback_collection.insert_one({ | |
"user_id": payload.user_id, | |
"Articles": [{"msid": payload.msid, "Read": actual_clicked_msids}] | |
}) | |
logger.info(f"API: Successfully saved feedback for user '{payload.user_id}'") | |
return FeedbackResponse(message="Response saved successfully") | |
except HTTPException: | |
raise # Re-raise HTTPException directly | |
except Exception as e: | |
logger.error(f"API Error in submit_user_feedback_api for user '{payload.user_id}': {e}", exc_info=True) | |
raise HTTPException(status_code=500, detail=f"An error occurred: {str(e)}") | |
async def get_recommendations_summary_api(msid: str, k: int = 5, summary: bool = True, smart_tip: bool = True): | |
""" | |
Get recommendations with optional summary and smart tip for a given MSID. | |
""" | |
try: | |
if not hasattr(fastapi_app.state, "recommender") or fastapi_app.state.recommender is None: | |
logger.error("Recommender is not available.") | |
raise HTTPException(status_code=503, detail="Recommender service not available") | |
try: | |
response = fastapi_app.state.recommender.get_recommendations_summary(msid, k, summary, smart_tip) | |
except RuntimeError as e: | |
# Catch the meta tensor error and return a fallback | |
if "meta tensor" in str(e): | |
logger.error("Summary model error: %s", e) | |
response = { | |
"msid": msid, | |
"recommendations": [], | |
"summary": [], | |
"smart_tip": [], | |
"error": "Summary model is not available on this server." | |
} | |
else: | |
raise | |
return response | |
except Exception as e: | |
logger.error(f"API Error in get_recommendations_summary_api for msid '{msid}': {e}", exc_info=True) | |
raise HTTPException(status_code=500, detail=f"An error occurred: {str(e)}") | |
# This makes fastapi_app (imported from src.main and extended here) available as 'app' | |
# for ASGI servers like Uvicorn. | |
app = fastapi_app | |
if __name__ == "__main__": | |
uvicorn.run("app:app", host="0.0.0.0", port=8000, reload=True) | |