parthnuwal7 commited on
Commit
c08a089
·
1 Parent(s): baecac6

ADD: FastAPI server for HF Spaces API endpoints

Browse files

Added api_server.py - FastAPI backend with REST endpoints
/process-reviews endpoint for frontend integration
/health endpoint for status checking
Updated Dockerfile to run FastAPI server
Added FastAPI, uvicorn, pydantic to requirements
CORS enabled for frontend connections

Frontend can now connect: POST /process-reviews
Status: API architecture complete, ready for testing

Files changed (3) hide show
  1. Dockerfile +3 -2
  2. api_server.py +142 -0
  3. requirements-docker.txt +4 -1
Dockerfile CHANGED
@@ -20,6 +20,7 @@ COPY requirements-docker.txt ./requirements.txt
20
  # Install Python dependencies (optimized for Docker)
21
  RUN pip install --no-cache-dir --upgrade pip && \
22
  pip install --no-cache-dir -r requirements.txt && \
 
23
  python -m spacy download en_core_web_sm
24
 
25
  # Copy the rest of the application
@@ -43,5 +44,5 @@ EXPOSE 7860
43
  # Health check
44
  HEALTHCHECK CMD curl --fail http://localhost:7860/_stcore/health
45
 
46
- # Run the Streamlit app
47
- ENTRYPOINT ["streamlit", "run", "app_enhanced.py", "--server.port=7860", "--server.address=0.0.0.0"]
 
20
  # Install Python dependencies (optimized for Docker)
21
  RUN pip install --no-cache-dir --upgrade pip && \
22
  pip install --no-cache-dir -r requirements.txt && \
23
+ pip install --no-cache-dir fastapi uvicorn pydantic && \
24
  python -m spacy download en_core_web_sm
25
 
26
  # Copy the rest of the application
 
44
  # Health check
45
  HEALTHCHECK CMD curl --fail http://localhost:7860/_stcore/health
46
 
47
+ # Run the FastAPI server (which also starts Streamlit)
48
+ ENTRYPOINT ["python", "api_server.py"]
api_server.py ADDED
@@ -0,0 +1,142 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ FastAPI backend wrapper for HF Spaces
3
+ Provides REST API endpoints while keeping Streamlit UI
4
+ """
5
+
6
+ from fastapi import FastAPI, HTTPException
7
+ from fastapi.middleware.cors import CORSMiddleware
8
+ from pydantic import BaseModel
9
+ from typing import Dict, List, Any, Optional
10
+ import pandas as pd
11
+ import sys
12
+ import os
13
+ import uvicorn
14
+ from threading import Thread
15
+ import subprocess
16
+
17
+ # Add src to path for imports
18
+ current_dir = os.path.dirname(os.path.abspath(__file__))
19
+ src_path = os.path.join(current_dir, 'src')
20
+ if src_path not in sys.path:
21
+ sys.path.insert(0, src_path)
22
+
23
+ from utils.data_processor import DataProcessor
24
+
25
+ app = FastAPI(title="ABSA ML Backend API", version="1.0.0")
26
+
27
+ # Add CORS middleware
28
+ app.add_middleware(
29
+ CORSMiddleware,
30
+ allow_origins=["*"],
31
+ allow_credentials=True,
32
+ allow_methods=["*"],
33
+ allow_headers=["*"],
34
+ )
35
+
36
+ # Initialize processor (cached)
37
+ processor = None
38
+
39
+ def get_processor():
40
+ global processor
41
+ if processor is None:
42
+ processor = DataProcessor()
43
+ return processor
44
+
45
+ class ReviewData(BaseModel):
46
+ id: int
47
+ reviews_title: str
48
+ review: str
49
+ date: str
50
+ user_id: str
51
+
52
+ class ProcessRequest(BaseModel):
53
+ data: List[ReviewData]
54
+ options: Optional[Dict[str, Any]] = {}
55
+
56
+ class ProcessResponse(BaseModel):
57
+ status: str
58
+ data: Optional[Dict[str, Any]] = None
59
+ message: Optional[str] = None
60
+
61
+ @app.get("/")
62
+ async def root():
63
+ return {"message": "ABSA ML Backend API", "status": "running"}
64
+
65
+ @app.get("/health")
66
+ async def health_check():
67
+ try:
68
+ proc = get_processor()
69
+ return {
70
+ "status": "healthy",
71
+ "translation_service": "available" if hasattr(proc.translator, 'model') else "unavailable",
72
+ "absa_service": "available" if hasattr(proc.absa_processor, 'aspect_extractor') else "unavailable"
73
+ }
74
+ except Exception as e:
75
+ return {"status": "error", "message": str(e)}
76
+
77
+ @app.post("/process-reviews", response_model=ProcessResponse)
78
+ async def process_reviews(request: ProcessRequest):
79
+ try:
80
+ # Convert request data to DataFrame
81
+ data_list = [item.dict() for item in request.data]
82
+ df = pd.DataFrame(data_list)
83
+
84
+ # Process data
85
+ proc = get_processor()
86
+ results = proc.process_uploaded_data(df)
87
+
88
+ if 'error' in results:
89
+ raise HTTPException(status_code=400, detail=results['error'])
90
+
91
+ # Serialize for API response
92
+ serialized_results = serialize_for_api(results)
93
+
94
+ return ProcessResponse(
95
+ status="success",
96
+ data=serialized_results
97
+ )
98
+
99
+ except Exception as e:
100
+ raise HTTPException(status_code=500, detail=str(e))
101
+
102
+ def serialize_for_api(results: Dict) -> Dict:
103
+ """Convert complex objects to JSON-serializable format."""
104
+ serialized = {}
105
+
106
+ for key, value in results.items():
107
+ if key == 'processed_data':
108
+ # Convert DataFrame to dict
109
+ serialized[key] = value.to_dict('records') if hasattr(value, 'to_dict') else value
110
+ elif key == 'aspect_network':
111
+ # Convert NetworkX graph to dict
112
+ import networkx as nx
113
+ if hasattr(value, 'nodes'):
114
+ serialized[key] = nx.node_link_data(value)
115
+ else:
116
+ serialized[key] = value
117
+ elif hasattr(value, 'to_dict'):
118
+ # Convert DataFrames
119
+ serialized[key] = value.to_dict('records')
120
+ elif isinstance(value, pd.DataFrame):
121
+ serialized[key] = value.to_dict('records')
122
+ else:
123
+ # Keep as is for basic types
124
+ serialized[key] = value
125
+
126
+ return serialized
127
+
128
+ def run_streamlit():
129
+ """Run Streamlit in a separate thread"""
130
+ subprocess.run([
131
+ "streamlit", "run", "app_enhanced.py",
132
+ "--server.port=8502",
133
+ "--server.address=0.0.0.0"
134
+ ])
135
+
136
+ if __name__ == "__main__":
137
+ # Start Streamlit in background
138
+ streamlit_thread = Thread(target=run_streamlit, daemon=True)
139
+ streamlit_thread.start()
140
+
141
+ # Start FastAPI
142
+ uvicorn.run(app, host="0.0.0.0", port=7860)
requirements-docker.txt CHANGED
@@ -33,4 +33,7 @@ pillow>=10.0.0,<10.2.0
33
  requests>=2.31.0
34
  faker>=18.0.0
35
  openpyxl>=3.1.0
36
- reportlab>=4.0.0
 
 
 
 
33
  requests>=2.31.0
34
  faker>=18.0.0
35
  openpyxl>=3.1.0
36
+ reportlab>=4.0.0
37
+ fastapi>=0.104.0
38
+ uvicorn>=0.24.0
39
+ pydantic>=2.0.0