Skip to content

Commit 7f27b6c

Browse files
committed
Enhance README and backend services
Updated README to clarify terminal setup instructions, adding details for five terminals. Refactored webhook notification logic in saas_api.py to use async HTTP requests with httpx. Improved error handling and logging in various backend services. Added new routes for app registration and usage analytics in the webhook service. Updated frontend to reflect changes in API endpoints and improved user experience in the dashboard.
1 parent d5866ef commit 7f27b6c

27 files changed

+1429
-249
lines changed

README.md

Lines changed: 46 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1,59 +1,65 @@
11
# How to run this application
22

3-
## Make 4 terminals
4-
```
5-
cd Backend/back2
6-
```
7-
```
8-
cd Backend/back2
9-
```
10-
```
11-
cd backend/web_services
12-
```
13-
```
14-
cd frontend/ai-rec
15-
```
3+
## Setup - Open 5 terminals
164

17-
## In first terminal run(uvicorn terminal (api))
18-
```
5+
### Terminal 1: FastAPI Backend (ML Recommendations)
6+
```bash
7+
cd ai-rec/backend/back2
198
pip install -r requirements.txt
9+
uvicorn saas_api:app --reload
2010
```
11+
This runs the ML recommender API on **http://localhost:8000**
2112

22-
## In third terminal run(frontend/s)
13+
### Terminal 2: MLflow UI (Model Registry)
14+
```bash
15+
cd ai-rec/backend/back2
16+
mlflow ui --backend-store-uri sqlite:///mlflow.db --default-artifact-root ./mlflow_artifacts
2317
```
24-
npm i
18+
This runs MLflow UI on **http://localhost:5000**
2519

20+
### Terminal 3: Webhook & App Registration Service
21+
```bash
22+
cd ai-rec/backend/webhooks_services
23+
npm install
24+
node server.js
2625
```
26+
This runs the webhook and app registration service on **http://localhost:3001**
2727

28-
## In fourth terminal
28+
### Terminal 4: Frontend (Vite)
29+
```bash
30+
cd ai-rec/frontend/s
31+
npm install
32+
npm run dev
33+
```
34+
This runs the frontend on **http://localhost:5173**
2935

30-
~~~
31-
npm init -y
32-
npm install express sqlite3 axios body-parser cors dotenv
33-
~~~
34-
## Running the application
36+
### Terminal 5: Mock Webhook Server (Optional)
37+
```bash
38+
cd ai-rec/backend/webhooks_services
39+
node mock_webhook_server.js
40+
```
41+
This runs a mock webhook listener on **http://localhost:4000** to test webhook notifications
3542

43+
## Running the application
3644

37-
### In first terminal(uvicorn)
38-
```
39-
uvicorn saas_api:app --reload
40-
```
45+
1. **Start all services** in the order above
46+
2. Open **http://localhost:5173** in your browser
47+
3. Go to the **Webhook Dashboard** tab
48+
4. Register a new app with:
49+
- App Name: "My Music App" (or any name)
50+
- Webhook URL: "http://localhost:4000/api/music"
51+
5. Copy the generated **API Key**
52+
6. Use the **API Key** in the external client or test with curl
4153

42-
### In second terminal(mlflow)
43-
```
44-
mlflow ui --backend-store-uri sqlite:///mlflow.db --default-artifact-root ./mlflow_artifacts
54+
## Testing the External Client
4555

46-
```
47-
### In third terminal(frontend)
48-
```
49-
npm run dev
50-
```
51-
### In fourth terminal(frontend)
52-
```
53-
node server.js
54-
```
56+
1. Open `xternal_client/index.html` in a browser
57+
2. Paste your API Key
58+
3. Enter a song title (e.g., "Fader")
59+
4. Enter the Project ID (e.g., 7)
60+
5. Click "Get Recommendations"
5561

56-
## Go to localhost:5173 or frontend luanched port, to view mlflow go to localhost:5000
62+
You should see recommendations and webhook notifications in the mock webhook server terminal!
5763

5864
## Upload dataset(if not with you, use datasets in example_dataset folder) and enter into required fields,select ready model and select required fields and click get recommendations
5965

-3.79 KB
Binary file not shown.
-3.18 KB
Binary file not shown.
-1.38 KB
Binary file not shown.
-1.3 KB
Binary file not shown.
-8.95 KB
Binary file not shown.
-2.89 KB
Binary file not shown.
-25.6 KB
Binary file not shown.
-3.18 KB
Binary file not shown.

backend/back2/saas_api.py

Lines changed: 61 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -17,24 +17,42 @@
1717
import models
1818
import schemas
1919
import database
20-
21-
20+
import httpx
2221
# --- Import your classes ---
2322
from Content import ContentBasedRecommender
2423
from Collaborative import CollaborativeFilteringRecommender
2524
# --- Import the MLflow wrapper ---
2625
from dynamic_recommender import MLflowRecommenderWrapper
26+
from datetime import datetime
27+
2728

28-
import requests
29-
def notify_webhooks(event, data):
30-
31-
url = "http://localhost:5000/api/webhooks/trigger" # your Node.js webhook microservice
32-
try:
33-
response = requests.post(url, json={"event": event, "data": data}, timeout=5)
34-
print(f"Webhook triggered: {event}, status={response.status_code}")
35-
except Exception as e:
36-
print(f"⚠️ Webhook trigger failed: {e}")
3729

30+
async def notify_webhooks(event_type: str, payload: dict):
31+
"""Send event payload to all registered external apps via the Node webhook service."""
32+
try:
33+
async with httpx.AsyncClient() as client:
34+
res = await client.get("http://localhost:3001/api/apps")
35+
if res.status_code != 200:
36+
print("⚠️ Could not fetch registered apps from webhook service")
37+
return
38+
apps = res.json()
39+
40+
for app in apps:
41+
try:
42+
await client.post(
43+
app["webhook_url"],
44+
json={
45+
"event": event_type,
46+
"data": payload,
47+
"api_key": app["api_key"],
48+
},
49+
timeout=10.0,
50+
)
51+
print(f"✅ Notified {app['app_name']} at {app['webhook_url']}")
52+
except Exception as e:
53+
print(f"❌ Failed to send to {app['app_name']}: {e}")
54+
except Exception as e:
55+
print(f"❌ notify_webhooks failed: {e}")
3856
# --- App Setup & MLflow Configuration (Unchanged) ---
3957
os.makedirs("user_uploads", exist_ok=True)
4058
os.makedirs("mlflow_artifacts", exist_ok=True)
@@ -79,7 +97,7 @@ async def lifespan(app: FastAPI):
7997
app = FastAPI(lifespan=lifespan)
8098
app.add_middleware(
8199
CORSMiddleware,
82-
allow_origins=["http://localhost:3000", "http://127.0.0.1:3000","http://localhost:8000","http://localhost:5000","http://localhost:5173"],
100+
allow_origins=["http://localhost:3000", "http://127.0.0.1:3000","http://localhost:8000","http://localhost:3001","http://localhost:5173"],
83101
allow_credentials=True,
84102
allow_methods=["*"],
85103
allow_headers=["*"],
@@ -117,22 +135,22 @@ async def save_file_and_schema(
117135
raise HTTPException(status_code=400, detail=f"Invalid schema JSON for {file_type} file.")
118136

119137
for app_key, user_col in schema_map.items():
120-
if isinstance(user_col, list):
138+
if isinstance(user_col, list):
121139
for col in user_col:
122140
db_schema = models.SchemaMapping(
123-
app_schema_key='feature_col',
141+
app_schema_key='feature_col',
124142
user_csv_column=col,
125-
file_id=db_file.id
143+
file_id=db_file.id,
126144
)
127145
db.add(db_schema)
128146
else:
129-
db_schema = models.SchemaMapping(
147+
db_schema = models.SchemaMapping(
130148
app_schema_key=app_key,
131149
user_csv_column=user_col,
132-
file_id=db_file.id
150+
file_id=db_file.id,
133151
)
134-
db.add(db_schema)
135-
152+
db.add(db_schema)
153+
136154
db.commit()
137155
return db_file
138156

@@ -271,12 +289,18 @@ async def process_project(project_id: int, db: Session):
271289
db_project.status = models.ProjectStatus.READY
272290
db.commit()
273291
print(f"[Task {project_id}]: Processing complete.")
274-
notify_webhooks("model_trained", {
275-
"project_id": project_id,
276-
"project_name": db_project.project_name,
277-
"model_type": db_project.model_type.value,
278-
"status": db_project.status.value
279-
})
292+
293+
# --- Send webhook notification ---
294+
try:
295+
await notify_webhooks("model_ready", {
296+
"project_id": db_project.id,
297+
"project_name": db_project.project_name,
298+
"model_type": db_project.model_type,
299+
"timestamp": str(datetime.utcnow()),
300+
})
301+
except Exception as notify_err:
302+
print(f"[Task {project_id}]: Failed to notify webhooks - {notify_err}")
303+
280304
except Exception as e:
281305
print(f"[Task {project_id}]: ERROR processing project. {e}")
282306
if db_project:
@@ -427,22 +451,25 @@ def get_recommendations(
427451
try:
428452
model_uri = f"models:/{db_project.mlflow_model_name}/{db_project.mlflow_model_version}"
429453
print(f"Loading model from URI: {model_uri}")
454+
print(f"MLflow tracking URI: {mlflow.get_tracking_uri()}")
455+
print(f"Project details: {db_project.project_name} (ID: {db_project.id})")
456+
print(f"Model type: {model_type}, User ID: {user_id}, Item title: {item_title}")
457+
430458
model = mlflow.pyfunc.load_model(model_uri)
459+
print("Model loaded successfully")
431460

432461
model_input = pd.DataFrame([{"user_id": user_id, "item_title": item_title, "n": n}])
462+
print(f"Model input: {model_input.to_dict('records')}")
433463

434464
result_json = model.predict(model_input)[0]
465+
print(f"Raw prediction result: {result_json}")
466+
435467
result = json.loads(result_json)
468+
print(f"Parsed result: {result}")
436469

437-
if result["error"]:
470+
if result.get("error"):
438471
raise ValueError(result["error"])
439-
# 🔔 Notify external apps when recommendations are generated
440-
notify_webhooks("recommendations_generated", {
441-
"project_id": project_id,
442-
"user_id": user_id,
443-
"item_title": item_title,
444-
"recommendations": result["recommendations"]
445-
})
472+
446473
return schemas.RecommendationResponse(
447474
input_item_title=item_title,
448475
input_user_id=user_id,
@@ -454,6 +481,4 @@ def get_recommendations(
454481
raise HTTPException(status_code=404, detail=str(e))
455482
except Exception as e:
456483
print(f"Error loading model or predicting: {e}")
457-
raise HTTPException(status_code=500, detail=f"Error generating recommendations: {e}")
458-
459-
484+
raise HTTPException(status_code=500, detail=f"Error generating recommendations: {e}")

0 commit comments

Comments
 (0)