LogicGoInfotechSpaces commited on
Commit
18afae5
·
1 Parent(s): 38dfa43

Integrate OpenAI recommendations

Browse files
HUGGINGFACE_DEPLOYMENT.md CHANGED
@@ -20,15 +20,17 @@ This guide will help you deploy the Smart Budget Recommendation API to Hugging F
20
  - **Visibility**: Choose Public or Private
21
  4. Click "Create Space"
22
 
23
- ### Step 2: Add MongoDB URI as Secret
24
 
25
  1. In your Space, go to **Settings** (gear icon)
26
  2. Navigate to **Variables and secrets** section
27
  3. Click **New secret**
28
- 4. Add:
29
  - **Name**: `MONGODB_URI`
30
- - **Value**: `mongodb://expenseuser:Kem_6o%3F%[email protected]:27017/expense?authSource=admin`
31
- 5. Click **Add secret**
 
 
32
 
33
  ### Step 3: Upload Files
34
 
@@ -110,9 +112,9 @@ print(response.json())
110
 
111
  ## Important Notes
112
 
113
- 1. **Port Configuration**: Hugging Face automatically maps port 8000, so your Dockerfile should expose port 8000 (which it does)
114
 
115
- 2. **Environment Variables**: The `MONGODB_URI` secret will be automatically available as an environment variable in your container
116
 
117
  3. **Cold Starts**: The first request after inactivity may take longer (cold start). Subsequent requests will be faster
118
 
 
20
  - **Visibility**: Choose Public or Private
21
  4. Click "Create Space"
22
 
23
+ ### Step 2: Add secrets (MongoDB and optional OpenAI)
24
 
25
  1. In your Space, go to **Settings** (gear icon)
26
  2. Navigate to **Variables and secrets** section
27
  3. Click **New secret**
28
+ 4. Add the following secrets:
29
  - **Name**: `MONGODB_URI`
30
+ - **Value**: `mongodb://expenseuser:Kem_6o%3F%[email protected]:27017/expense?authSource=admin`
31
+ - **Name**: `OPENAI_API_KEY` *(optional, enables GPT-based recommendations)*
32
+ - **Value**: `sk-...`
33
+ 5. Click **Add secret** after each entry
34
 
35
  ### Step 3: Upload Files
36
 
 
112
 
113
  ## Important Notes
114
 
115
+ 1. **Port Configuration**: Hugging Face Spaces expect apps to serve on port **7860**, so the Dockerfile exposes and binds to that port.
116
 
117
+ 2. **Environment Variables**: Secrets such as `MONGODB_URI` (and optional `OPENAI_API_KEY`) are injected automatically into the container environment.
118
 
119
  3. **Cold Starts**: The first request after inactivity may take longer (cold start). Subsequent requests will be faster
120
 
README.md CHANGED
@@ -52,9 +52,11 @@ A FastAPI-based service that provides intelligent budget recommendations based o
52
  pip install -r requirements.txt
53
  ```
54
 
55
- 2. Set environment variable:
56
  ```bash
57
  export MONGODB_URI="mongodb://expenseuser:Kem_6o%3F%[email protected]:27017/expense?authSource=admin"
 
 
58
  ```
59
 
60
  3. Run the application:
@@ -73,7 +75,7 @@ docker build -t smart-budget-recommendation .
73
 
74
  ### Run Docker Container
75
  ```bash
76
- docker run -p 8000:8000 -e MONGODB_URI="your_mongodb_uri" smart-budget-recommendation
77
  ```
78
 
79
  ## Hugging Face Deployment
@@ -85,10 +87,11 @@ docker run -p 8000:8000 -e MONGODB_URI="your_mongodb_uri" smart-budget-recommend
85
 
86
  ### Steps
87
 
88
- 1. **Store MongoDB URI as Secret in Hugging Face**:
89
  - Go to your Hugging Face Space settings
90
  - Navigate to "Variables and secrets"
91
- - Add a new secret named `MONGODB_URI` with your MongoDB connection string
 
92
 
93
  2. **Create Hugging Face Space**:
94
  - Create a new Space on Hugging Face
@@ -107,7 +110,7 @@ docker run -p 8000:8000 -e MONGODB_URI="your_mongodb_uri" smart-budget-recommend
107
  The Dockerfile is configured to:
108
  - Use Python 3.11
109
  - Install all dependencies
110
- - Expose port 8000
111
  - Run the FastAPI application with Uvicorn
112
 
113
  Hugging Face will automatically:
@@ -139,10 +142,13 @@ The Smart Budget Recommendation engine:
139
  4. **Confidence Scoring**:
140
  - Based on data quality (months analyzed, transaction count, consistency)
141
  - Returns score from 0-1
 
 
142
 
143
  ## Environment Variables
144
 
145
  - `MONGODB_URI`: MongoDB connection string (required)
 
146
 
147
  ## License
148
 
 
52
  pip install -r requirements.txt
53
  ```
54
 
55
+ 2. Set environment variables:
56
  ```bash
57
  export MONGODB_URI="mongodb://expenseuser:Kem_6o%3F%[email protected]:27017/expense?authSource=admin"
58
+ # optional
59
+ export OPENAI_API_KEY="your_openai_key"
60
  ```
61
 
62
  3. Run the application:
 
75
 
76
  ### Run Docker Container
77
  ```bash
78
+ docker run -p 7860:7860 -e MONGODB_URI="your_mongodb_uri" smart-budget-recommendation
79
  ```
80
 
81
  ## Hugging Face Deployment
 
87
 
88
  ### Steps
89
 
90
+ 1. **Store secrets in Hugging Face**:
91
  - Go to your Hugging Face Space settings
92
  - Navigate to "Variables and secrets"
93
+ - Add `MONGODB_URI` with your MongoDB connection string
94
+ - *(Optional)* Add `OPENAI_API_KEY` if you want GPT-powered recommendations
95
 
96
  2. **Create Hugging Face Space**:
97
  - Create a new Space on Hugging Face
 
110
  The Dockerfile is configured to:
111
  - Use Python 3.11
112
  - Install all dependencies
113
+ - Expose port 7860 (Hugging Face default)
114
  - Run the FastAPI application with Uvicorn
115
 
116
  Hugging Face will automatically:
 
142
  4. **Confidence Scoring**:
143
  - Based on data quality (months analyzed, transaction count, consistency)
144
  - Returns score from 0-1
145
+ 5. **(Optional) GPT Refinement**:
146
+ - If `OPENAI_API_KEY` is set, GPT reviews the history and suggests increase/decrease/keep along with a natural-language reason.
147
 
148
  ## Environment Variables
149
 
150
  - `MONGODB_URI`: MongoDB connection string (required)
151
+ - `OPENAI_API_KEY`: Optional. If set, GPT-based recommendations augment the rule-based engine.
152
 
153
  ## License
154
 
Smart_Budget_Recommendation_API.postman_collection.json ADDED
@@ -0,0 +1,384 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "info": {
3
+ "_postman_id": "smart-budget-recommendation-api",
4
+ "name": "Smart Budget Recommendation API",
5
+ "description": "API collection for Smart Budget Recommendation service deployed on Hugging Face",
6
+ "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json"
7
+ },
8
+ "item": [
9
+ {
10
+ "name": "Health Check",
11
+ "request": {
12
+ "method": "GET",
13
+ "header": [],
14
+ "url": {
15
+ "raw": "https://logicgoinfotechspaces-smart-budget-recommendation.hf.space/health",
16
+ "protocol": "https",
17
+ "host": [
18
+ "logicgoinfotechspaces-smart-budget-recommendation",
19
+ "hf",
20
+ "space"
21
+ ],
22
+ "path": [
23
+ "health"
24
+ ]
25
+ },
26
+ "description": "Check if the API and database are running"
27
+ }
28
+ },
29
+ {
30
+ "name": "Root Endpoint",
31
+ "request": {
32
+ "method": "GET",
33
+ "header": [],
34
+ "url": {
35
+ "raw": "https://logicgoinfotechspaces-smart-budget-recommendation.hf.space/",
36
+ "protocol": "https",
37
+ "host": [
38
+ "logicgoinfotechspaces-smart-budget-recommendation",
39
+ "hf",
40
+ "space"
41
+ ],
42
+ "path": [
43
+ ""
44
+ ]
45
+ }
46
+ }
47
+ },
48
+ {
49
+ "name": "Create Expense",
50
+ "request": {
51
+ "method": "POST",
52
+ "header": [
53
+ {
54
+ "key": "Content-Type",
55
+ "value": "application/json"
56
+ }
57
+ ],
58
+ "body": {
59
+ "mode": "raw",
60
+ "raw": "{\n \"user_id\": \"68a834c3f4694b11efedacd2\",\n \"amount\": 3800,\n \"category\": \"Groceries\",\n \"description\": \"Monthly groceries\",\n \"date\": \"2025-01-15T00:00:00\",\n \"type\": \"expense\"\n}"
61
+ },
62
+ "url": {
63
+ "raw": "https://logicgoinfotechspaces-smart-budget-recommendation.hf.space/expenses",
64
+ "protocol": "https",
65
+ "host": [
66
+ "logicgoinfotechspaces-smart-budget-recommendation",
67
+ "hf",
68
+ "space"
69
+ ],
70
+ "path": [
71
+ "expenses"
72
+ ]
73
+ },
74
+ "description": "Create a new expense record"
75
+ }
76
+ },
77
+ {
78
+ "name": "Get Expenses",
79
+ "request": {
80
+ "method": "GET",
81
+ "header": [],
82
+ "url": {
83
+ "raw": "https://logicgoinfotechspaces-smart-budget-recommendation.hf.space/expenses?user_id=68a834c3f4694b11efedacd2&limit=20",
84
+ "protocol": "https",
85
+ "host": [
86
+ "logicgoinfotechspaces-smart-budget-recommendation",
87
+ "hf",
88
+ "space"
89
+ ],
90
+ "path": [
91
+ "expenses"
92
+ ],
93
+ "query": [
94
+ {
95
+ "key": "user_id",
96
+ "value": "68a834c3f4694b11efedacd2",
97
+ "description": "User identifier"
98
+ },
99
+ {
100
+ "key": "limit",
101
+ "value": "20",
102
+ "description": "Maximum number of expenses to return"
103
+ }
104
+ ]
105
+ },
106
+ "description": "Get expenses for a specific user"
107
+ }
108
+ },
109
+ {
110
+ "name": "Create Budget",
111
+ "request": {
112
+ "method": "POST",
113
+ "header": [
114
+ {
115
+ "key": "Content-Type",
116
+ "value": "application/json"
117
+ }
118
+ ],
119
+ "body": {
120
+ "mode": "raw",
121
+ "raw": "{\n \"user_id\": \"68a834c3f4694b11efedacd2\",\n \"category\": \"Groceries\",\n \"amount\": 4000,\n \"period\": \"monthly\",\n \"start_date\": \"2025-02-01T00:00:00\",\n \"end_date\": \"2025-02-29T00:00:00\"\n}"
122
+ },
123
+ "url": {
124
+ "raw": "https://logicgoinfotechspaces-smart-budget-recommendation.hf.space/budgets",
125
+ "protocol": "https",
126
+ "host": [
127
+ "logicgoinfotechspaces-smart-budget-recommendation",
128
+ "hf",
129
+ "space"
130
+ ],
131
+ "path": [
132
+ "budgets"
133
+ ]
134
+ },
135
+ "description": "Create a new budget"
136
+ }
137
+ },
138
+ {
139
+ "name": "Get Budgets",
140
+ "request": {
141
+ "method": "GET",
142
+ "header": [],
143
+ "url": {
144
+ "raw": "https://logicgoinfotechspaces-smart-budget-recommendation.hf.space/budgets?user_id=68a834c3f4694b11efedacd2",
145
+ "protocol": "https",
146
+ "host": [
147
+ "logicgoinfotechspaces-smart-budget-recommendation",
148
+ "hf",
149
+ "space"
150
+ ],
151
+ "path": [
152
+ "budgets"
153
+ ],
154
+ "query": [
155
+ {
156
+ "key": "user_id",
157
+ "value": "68a834c3f4694b11efedacd2"
158
+ }
159
+ ]
160
+ },
161
+ "description": "Get budgets for a specific user"
162
+ }
163
+ },
164
+ {
165
+ "name": "Get Smart Budget Recommendations",
166
+ "request": {
167
+ "method": "GET",
168
+ "header": [],
169
+ "url": {
170
+ "raw": "https://logicgoinfotechspaces-smart-budget-recommendation.hf.space/recommendations/68a834c3f4694b11efedacd2?month=2&year=2025",
171
+ "protocol": "https",
172
+ "host": [
173
+ "logicgoinfotechspaces-smart-budget-recommendation",
174
+ "hf",
175
+ "space"
176
+ ],
177
+ "path": [
178
+ "recommendations",
179
+ "68a834c3f4694b11efedacd2"
180
+ ],
181
+ "query": [
182
+ {
183
+ "key": "month",
184
+ "value": "2",
185
+ "description": "Target month (1-12), optional - defaults to next month"
186
+ },
187
+ {
188
+ "key": "year",
189
+ "value": "2025",
190
+ "description": "Target year, optional - defaults to next year"
191
+ }
192
+ ]
193
+ },
194
+ "description": "Get smart budget recommendations based on past spending behavior. Requires at least 2-3 months of expense data."
195
+ }
196
+ },
197
+ {
198
+ "name": "Get Category Expenses",
199
+ "request": {
200
+ "method": "GET",
201
+ "header": [],
202
+ "url": {
203
+ "raw": "https://logicgoinfotechspaces-smart-budget-recommendation.hf.space/category-expenses/68a834c3f4694b11efedacd2?months=3",
204
+ "protocol": "https",
205
+ "host": [
206
+ "logicgoinfotechspaces-smart-budget-recommendation",
207
+ "hf",
208
+ "space"
209
+ ],
210
+ "path": [
211
+ "category-expenses",
212
+ "68a834c3f4694b11efedacd2"
213
+ ],
214
+ "query": [
215
+ {
216
+ "key": "months",
217
+ "value": "3",
218
+ "description": "Number of months to analyze (default: 3)"
219
+ }
220
+ ]
221
+ },
222
+ "description": "Get average expenses by category for the past N months"
223
+ }
224
+ },
225
+ {
226
+ "name": "Sample Expenses - Create Multiple",
227
+ "item": [
228
+ {
229
+ "name": "Groceries - Month 1 (Sept 2024)",
230
+ "request": {
231
+ "method": "POST",
232
+ "header": [
233
+ {
234
+ "key": "Content-Type",
235
+ "value": "application/json"
236
+ }
237
+ ],
238
+ "body": {
239
+ "mode": "raw",
240
+ "raw": "{\n \"user_id\": \"68a834c3f4694b11efedacd2\",\n \"amount\": 3500,\n \"category\": \"Groceries\",\n \"description\": \"Monthly groceries - September 2024\",\n \"date\": \"2024-09-15T00:00:00\",\n \"type\": \"expense\"\n}"
241
+ },
242
+ "url": {
243
+ "raw": "https://logicgoinfotechspaces-smart-budget-recommendation.hf.space/expenses",
244
+ "protocol": "https",
245
+ "host": [
246
+ "logicgoinfotechspaces-smart-budget-recommendation",
247
+ "hf",
248
+ "space"
249
+ ],
250
+ "path": [
251
+ "expenses"
252
+ ]
253
+ }
254
+ }
255
+ },
256
+ {
257
+ "name": "Groceries - Month 2 (Oct 2024)",
258
+ "request": {
259
+ "method": "POST",
260
+ "header": [
261
+ {
262
+ "key": "Content-Type",
263
+ "value": "application/json"
264
+ }
265
+ ],
266
+ "body": {
267
+ "mode": "raw",
268
+ "raw": "{\n \"user_id\": \"68a834c3f4694b11efedacd2\",\n \"amount\": 3800,\n \"category\": \"Groceries\",\n \"description\": \"Monthly groceries - October 2024\",\n \"date\": \"2024-10-15T00:00:00\",\n \"type\": \"expense\"\n}"
269
+ },
270
+ "url": {
271
+ "raw": "https://logicgoinfotechspaces-smart-budget-recommendation.hf.space/expenses",
272
+ "protocol": "https",
273
+ "host": [
274
+ "logicgoinfotechspaces-smart-budget-recommendation",
275
+ "hf",
276
+ "space"
277
+ ],
278
+ "path": [
279
+ "expenses"
280
+ ]
281
+ }
282
+ }
283
+ },
284
+ {
285
+ "name": "Groceries - Month 3 (Nov 2024)",
286
+ "request": {
287
+ "method": "POST",
288
+ "header": [
289
+ {
290
+ "key": "Content-Type",
291
+ "value": "application/json"
292
+ }
293
+ ],
294
+ "body": {
295
+ "mode": "raw",
296
+ "raw": "{\n \"user_id\": \"68a834c3f4694b11efedacd2\",\n \"amount\": 4000,\n \"category\": \"Groceries\",\n \"description\": \"Monthly groceries - November 2024\",\n \"date\": \"2024-11-15T00:00:00\",\n \"type\": \"expense\"\n}"
297
+ },
298
+ "url": {
299
+ "raw": "https://logicgoinfotechspaces-smart-budget-recommendation.hf.space/expenses",
300
+ "protocol": "https",
301
+ "host": [
302
+ "logicgoinfotechspaces-smart-budget-recommendation",
303
+ "hf",
304
+ "space"
305
+ ],
306
+ "path": [
307
+ "expenses"
308
+ ]
309
+ }
310
+ }
311
+ },
312
+ {
313
+ "name": "Transport - Month 1 (Sept 2024)",
314
+ "request": {
315
+ "method": "POST",
316
+ "header": [
317
+ {
318
+ "key": "Content-Type",
319
+ "value": "application/json"
320
+ }
321
+ ],
322
+ "body": {
323
+ "mode": "raw",
324
+ "raw": "{\n \"user_id\": \"68a834c3f4694b11efedacd2\",\n \"amount\": 2000,\n \"category\": \"Transport\",\n \"description\": \"Monthly transport - September 2024\",\n \"date\": \"2024-09-20T00:00:00\",\n \"type\": \"expense\"\n}"
325
+ },
326
+ "url": {
327
+ "raw": "https://logicgoinfotechspaces-smart-budget-recommendation.hf.space/expenses",
328
+ "protocol": "https",
329
+ "host": [
330
+ "logicgoinfotechspaces-smart-budget-recommendation",
331
+ "hf",
332
+ "space"
333
+ ],
334
+ "path": [
335
+ "expenses"
336
+ ]
337
+ }
338
+ }
339
+ },
340
+ {
341
+ "name": "Transport - Month 2 (Oct 2024)",
342
+ "request": {
343
+ "method": "POST",
344
+ "header": [
345
+ {
346
+ "key": "Content-Type",
347
+ "value": "application/json"
348
+ }
349
+ ],
350
+ "body": {
351
+ "mode": "raw",
352
+ "raw": "{\n \"user_id\": \"68a834c3f4694b11efedacd2\",\n \"amount\": 2200,\n \"category\": \"Transport\",\n \"description\": \"Monthly transport - October 2024\",\n \"date\": \"2024-10-20T00:00:00\",\n \"type\": \"expense\"\n}"
353
+ },
354
+ "url": {
355
+ "raw": "https://logicgoinfotechspaces-smart-budget-recommendation.hf.space/expenses",
356
+ "protocol": "https",
357
+ "host": [
358
+ "logicgoinfotechspaces-smart-budget-recommendation",
359
+ "hf",
360
+ "space"
361
+ ],
362
+ "path": [
363
+ "expenses"
364
+ ]
365
+ }
366
+ }
367
+ }
368
+ ]
369
+ }
370
+ ],
371
+ "variable": [
372
+ {
373
+ "key": "base_url",
374
+ "value": "https://logicgoinfotechspaces-smart-budget-recommendation.hf.space",
375
+ "type": "string"
376
+ },
377
+ {
378
+ "key": "user_id",
379
+ "value": "68a834c3f4694b11efedacd2",
380
+ "type": "string"
381
+ }
382
+ ]
383
+ }
384
+
app/models.py CHANGED
@@ -29,6 +29,10 @@ class BudgetRecommendation(BaseModel):
29
  recommended_budget: float
30
  reason: str
31
  confidence: float = Field(..., ge=0, le=1, description="Confidence score (0-1)")
 
 
 
 
32
 
33
  class CategoryExpense(BaseModel):
34
  category: str
 
29
  recommended_budget: float
30
  reason: str
31
  confidence: float = Field(..., ge=0, le=1, description="Confidence score (0-1)")
32
+ action: Optional[str] = Field(
33
+ None,
34
+ description="AI suggestion: increase, decrease, or keep"
35
+ )
36
 
37
  class CategoryExpense(BaseModel):
38
  category: str
app/smart_recommendation.py CHANGED
@@ -1,8 +1,18 @@
1
- from datetime import datetime, timedelta
2
- from typing import List, Dict
 
3
  from collections import defaultdict
 
 
 
 
 
 
4
  from app.models import BudgetRecommendation, CategoryExpense
5
- import math
 
 
 
6
 
7
  class SmartBudgetRecommender:
8
  """
@@ -14,6 +24,7 @@ class SmartBudgetRecommender:
14
 
15
  def __init__(self, db):
16
  self.db = db
 
17
 
18
  def get_recommendations(self, user_id: str, month: int, year: int) -> List[BudgetRecommendation]:
19
  """
@@ -51,12 +62,21 @@ class SmartBudgetRecommender:
51
 
52
  reason = self._generate_reason(category, avg_expense, recommended_budget)
53
 
 
 
 
 
 
 
 
 
54
  recommendations.append(BudgetRecommendation(
55
  category=category,
56
  average_expense=round(avg_expense, 2),
57
  recommended_budget=round(recommended_budget, 2),
58
  reason=reason,
59
- confidence=confidence
 
60
  ))
61
 
62
  # Sort by average expense (highest first)
@@ -216,3 +236,41 @@ class SmartBudgetRecommender:
216
  result.sort(key=lambda x: x.average_monthly_expense, reverse=True)
217
  return result
218
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import json
2
+ import math
3
+ import os
4
  from collections import defaultdict
5
+ from datetime import datetime, timedelta
6
+ from typing import Dict, List
7
+
8
+ from dotenv import load_dotenv
9
+ from openai import OpenAI
10
+
11
  from app.models import BudgetRecommendation, CategoryExpense
12
+
13
+ load_dotenv()
14
+ OPENAI_API_KEY = os.getenv("OPENAI_API_KEY")
15
+ openai_client = OpenAI(api_key=OPENAI_API_KEY) if OPENAI_API_KEY else None
16
 
17
  class SmartBudgetRecommender:
18
  """
 
24
 
25
  def __init__(self, db):
26
  self.db = db
27
+ self.openai_client = openai_client
28
 
29
  def get_recommendations(self, user_id: str, month: int, year: int) -> List[BudgetRecommendation]:
30
  """
 
62
 
63
  reason = self._generate_reason(category, avg_expense, recommended_budget)
64
 
65
+ ai_result = self._get_ai_recommendation(category, data, avg_expense, recommended_budget)
66
+ if ai_result:
67
+ recommended_budget = ai_result.get("recommended_budget", recommended_budget)
68
+ reason = ai_result.get("reason", reason)
69
+ action = ai_result.get("action")
70
+ else:
71
+ action = None
72
+
73
  recommendations.append(BudgetRecommendation(
74
  category=category,
75
  average_expense=round(avg_expense, 2),
76
  recommended_budget=round(recommended_budget, 2),
77
  reason=reason,
78
+ confidence=confidence,
79
+ action=action
80
  ))
81
 
82
  # Sort by average expense (highest first)
 
236
  result.sort(key=lambda x: x.average_monthly_expense, reverse=True)
237
  return result
238
 
239
+ def _get_ai_recommendation(self, category: str, data: Dict, avg_expense: float, fallback_budget: float):
240
+ """Use OpenAI to refine the budget recommendation."""
241
+ if not self.openai_client:
242
+ return None
243
+
244
+ history = ", ".join(f"{value:.0f}" for value in data["monthly_values"])
245
+ summary = (
246
+ f"Category: {category}\n"
247
+ f"Monthly totals: [{history}]\n"
248
+ f"Average spend: {avg_expense:.2f}\n"
249
+ f"Std deviation: {data['std_dev']:.2f}\n"
250
+ f"Months observed: {data['months_analyzed']}\n"
251
+ f"Current suggested budget: {fallback_budget:.2f}\n"
252
+ )
253
+
254
+ prompt = (
255
+ "You are an Indian personal finance coach. "
256
+ "Given the user's spending history, decide whether to increase, decrease, "
257
+ "or keep the upcoming month's budget and provide a short explanation. "
258
+ "Respond strictly as JSON with the following keys:\n"
259
+ '{ "recommended_budget": number, "action": "increase|decrease|keep", "reason": "string" }.\n'
260
+ "Use rupees for all amounts.\n\n"
261
+ f"{summary}"
262
+ )
263
+
264
+ try:
265
+ response = self.openai_client.responses.create(
266
+ model="gpt-4.1-mini",
267
+ input=prompt,
268
+ temperature=0.1,
269
+ response_format={"type": "json_object"},
270
+ )
271
+ content = response.output[0].content[0].text
272
+ return json.loads(content)
273
+ except Exception as exc:
274
+ print(f"OpenAI recommendation error for {category}: {exc}")
275
+ return None
276
+
requirements.txt CHANGED
@@ -3,4 +3,6 @@ uvicorn[standard]==0.24.0
3
  pymongo==4.6.0
4
  pydantic==2.5.0
5
  python-multipart==0.0.6
 
 
6
 
 
3
  pymongo==4.6.0
4
  pydantic==2.5.0
5
  python-multipart==0.0.6
6
+ openai==1.51.0
7
+ python-dotenv==1.0.1
8
 
test_hf_api.py ADDED
@@ -0,0 +1,215 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Test script for Hugging Face deployed API
3
+ Creates sample expenses and tests recommendations
4
+ """
5
+
6
+ import requests
7
+ import json
8
+ from datetime import datetime, timedelta
9
+
10
+ BASE_URL = "https://logicgoinfotechspaces-smart-budget-recommendation.hf.space"
11
+ USER_ID = "68a834c3f4694b11efedacd2"
12
+
13
+ def test_health():
14
+ """Test health endpoint"""
15
+ print("=" * 60)
16
+ print("1. Testing Health Endpoint")
17
+ print("=" * 60)
18
+ try:
19
+ response = requests.get(f"{BASE_URL}/health")
20
+ print(f"Status: {response.status_code}")
21
+ print(f"Response: {json.dumps(response.json(), indent=2)}")
22
+ return response.status_code == 200
23
+ except Exception as e:
24
+ print(f"Error: {e}")
25
+ return False
26
+
27
+ def create_sample_expenses():
28
+ """Create multiple sample expenses across different months"""
29
+ print("\n" + "=" * 60)
30
+ print("2. Creating Sample Expenses")
31
+ print("=" * 60)
32
+
33
+ # Create expenses for the past 4 months
34
+ base_date = datetime(2024, 9, 15) # Start from September 2024
35
+ expenses = []
36
+
37
+ # Groceries - 4 months
38
+ for i in range(4):
39
+ date = base_date + timedelta(days=30 * i)
40
+ expenses.append({
41
+ "user_id": USER_ID,
42
+ "amount": 3500 + (i * 100), # Varying amounts: 3500, 3600, 3700, 3800
43
+ "category": "Groceries",
44
+ "description": f"Monthly groceries - {date.strftime('%B %Y')}",
45
+ "date": date.isoformat(),
46
+ "type": "expense"
47
+ })
48
+
49
+ # Transport - 3 months
50
+ for i in range(3):
51
+ date = base_date + timedelta(days=30 * i)
52
+ expenses.append({
53
+ "user_id": USER_ID,
54
+ "amount": 2000 + (i * 50), # 2000, 2050, 2100
55
+ "category": "Transport",
56
+ "description": f"Monthly transport - {date.strftime('%B %Y')}",
57
+ "date": date.isoformat(),
58
+ "type": "expense"
59
+ })
60
+
61
+ # Utilities - 2 months
62
+ for i in range(2):
63
+ date = base_date + timedelta(days=30 * i)
64
+ expenses.append({
65
+ "user_id": USER_ID,
66
+ "amount": 1500 + (i * 100), # 1500, 1600
67
+ "category": "Utilities",
68
+ "description": f"Monthly utilities - {date.strftime('%B %Y')}",
69
+ "date": date.isoformat(),
70
+ "type": "expense"
71
+ })
72
+
73
+ created_count = 0
74
+ for expense in expenses:
75
+ try:
76
+ response = requests.post(
77
+ f"{BASE_URL}/expenses",
78
+ json=expense,
79
+ headers={"Content-Type": "application/json"}
80
+ )
81
+ if response.status_code == 200:
82
+ print(f"[OK] Created: {expense['category']} - Rs.{expense['amount']} ({expense['date'][:10]})")
83
+ created_count += 1
84
+ else:
85
+ print(f"[FAIL] Failed: {expense['category']} - {response.text}")
86
+ except Exception as e:
87
+ print(f"[ERROR] Error creating expense: {e}")
88
+
89
+ print(f"\nTotal expenses created: {created_count}/{len(expenses)}")
90
+ return created_count > 0
91
+
92
+ def get_expenses():
93
+ """Get all expenses for the user"""
94
+ print("\n" + "=" * 60)
95
+ print("3. Getting All Expenses")
96
+ print("=" * 60)
97
+ try:
98
+ response = requests.get(
99
+ f"{BASE_URL}/expenses",
100
+ params={"user_id": USER_ID, "limit": 100}
101
+ )
102
+ if response.status_code == 200:
103
+ expenses = response.json()
104
+ print(f"Found {len(expenses)} expenses:\n")
105
+ for exp in expenses:
106
+ print(f" - {exp['category']}: Rs.{exp['amount']} ({exp['date'][:10]})")
107
+ return True
108
+ else:
109
+ print(f"Error: {response.status_code} - {response.text}")
110
+ return False
111
+ except Exception as e:
112
+ print(f"Error: {e}")
113
+ return False
114
+
115
+ def test_recommendations():
116
+ """Test getting budget recommendations"""
117
+ print("\n" + "=" * 60)
118
+ print("4. Testing Smart Budget Recommendations")
119
+ print("=" * 60)
120
+ try:
121
+ # Get recommendations for next month
122
+ next_month = datetime.now().month + 1
123
+ next_year = datetime.now().year
124
+ if next_month > 12:
125
+ next_month = 1
126
+ next_year += 1
127
+
128
+ response = requests.get(
129
+ f"{BASE_URL}/recommendations/{USER_ID}",
130
+ params={"month": next_month, "year": next_year}
131
+ )
132
+
133
+ if response.status_code == 200:
134
+ recommendations = response.json()
135
+ print(f"\nFound {len(recommendations)} recommendations:\n")
136
+
137
+ if len(recommendations) == 0:
138
+ print("[WARNING] No recommendations yet. Need more expense data (at least 2-3 months).")
139
+ else:
140
+ for rec in recommendations:
141
+ print(f"Category: {rec['category']}")
142
+ print(f" Average Expense: Rs.{rec['average_expense']:,.0f}")
143
+ print(f" Recommended Budget: Rs.{rec['recommended_budget']:,.0f}")
144
+ print(f" Confidence: {rec['confidence']*100:.0f}%")
145
+ print(f" Reason: {rec['reason']}")
146
+ print()
147
+ return True
148
+ else:
149
+ print(f"Error: {response.status_code} - {response.text}")
150
+ return False
151
+ except Exception as e:
152
+ print(f"Error: {e}")
153
+ return False
154
+
155
+ def test_category_expenses():
156
+ """Test getting category expense averages"""
157
+ print("\n" + "=" * 60)
158
+ print("5. Testing Category Expense Averages")
159
+ print("=" * 60)
160
+ try:
161
+ response = requests.get(
162
+ f"{BASE_URL}/category-expenses/{USER_ID}",
163
+ params={"months": 6}
164
+ )
165
+
166
+ if response.status_code == 200:
167
+ categories = response.json()
168
+ print(f"\nFound {len(categories)} categories:\n")
169
+
170
+ for cat in categories:
171
+ print(f"{cat['category']}:")
172
+ print(f" Average Monthly: Rs.{cat['average_monthly_expense']:,.0f}")
173
+ print(f" Total Transactions: {cat['total_expenses']}")
174
+ print(f" Months Analyzed: {cat['months_analyzed']}")
175
+ print()
176
+ return True
177
+ else:
178
+ print(f"Error: {response.status_code} - {response.text}")
179
+ return False
180
+ except Exception as e:
181
+ print(f"Error: {e}")
182
+ return False
183
+
184
+ if __name__ == "__main__":
185
+ print("\n" + "=" * 60)
186
+ print("Smart Budget Recommendation API - Hugging Face Test")
187
+ print("=" * 60)
188
+ print(f"\nBase URL: {BASE_URL}")
189
+ print(f"User ID: {USER_ID}\n")
190
+
191
+ results = []
192
+
193
+ # Run tests
194
+ results.append(("Health Check", test_health()))
195
+ results.append(("Create Expenses", create_sample_expenses()))
196
+ results.append(("Get Expenses", get_expenses()))
197
+ results.append(("Get Recommendations", test_recommendations()))
198
+ results.append(("Category Averages", test_category_expenses()))
199
+
200
+ # Summary
201
+ print("\n" + "=" * 60)
202
+ print("Test Summary")
203
+ print("=" * 60)
204
+ for test_name, passed in results:
205
+ status = "[PASS]" if passed else "[FAIL]"
206
+ print(f"{status} - {test_name}")
207
+
208
+ passed_count = sum(1 for _, passed in results if passed)
209
+ print(f"\nTotal: {passed_count}/{len(results)} tests passed")
210
+
211
+ print("\n" + "=" * 60)
212
+ print("API Documentation:")
213
+ print(f" Swagger UI: {BASE_URL}/docs")
214
+ print("=" * 60)
215
+