bldeaw commited on
Commit
c72db2d
·
1 Parent(s): 9f82a53

Deploy to Hugging Face Spaces: Fix model loading and add documentation

Browse files

- Fix model_utils.py: Use self.model_path instead of model_path parameter
- Fix AnomalyDetector: Return all required fields (threshold, top_drivers)
- Update test_api.py: Add support for Hugging Face Spaces URL
- Add comprehensive documentation (TESTING.md, SPACE_INFO.md)
- Update README.md with Hugging Face Spaces frontmatter

SPACE_INFO.md ADDED
@@ -0,0 +1,99 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Hugging Face Space Information
2
+
3
+ ## Space Details
4
+
5
+ - **Username**: `bldeaw`
6
+ - **Space Name**: `ml_service`
7
+ - **Full Space Path**: `bldeaw/ml_service`
8
+ - **Space URL**: `https://bldeaw-ml-service.hf.space`
9
+ - **Git Repository**: `https://huggingface.co/spaces/bldeaw/ml_service`
10
+
11
+ ---
12
+
13
+ ## Quick Commands
14
+
15
+ ### Test API
16
+
17
+ ```bash
18
+ # Test health endpoint
19
+ curl https://bldeaw-ml-service.hf.space/health
20
+
21
+ # Test prediction
22
+ curl -X POST https://bldeaw-ml-service.hf.space/predict/job-fail \
23
+ -H "Content-Type: application/json" \
24
+ -d '{
25
+ "zone": "prod",
26
+ "job_nm": "daily_export",
27
+ "job_start_time": "2026-01-21T01:00:00",
28
+ "duration_sec": 5400,
29
+ "status": "SUCCESS",
30
+ "explain": true
31
+ }'
32
+
33
+ # Test anomaly detection
34
+ curl -X POST https://bldeaw-ml-service.hf.space/detect/anomaly \
35
+ -H "Content-Type: application/json" \
36
+ -d '{
37
+ "features": {
38
+ "duration_sec": 5400,
39
+ "duration_zscore": 1.6,
40
+ "err_msg_len": 0
41
+ }
42
+ }'
43
+ ```
44
+
45
+ ### Using Test Script
46
+
47
+ ```bash
48
+ # Test Hugging Face Space API
49
+ python test_api.py --url https://bldeaw-ml-service.hf.space
50
+ ```
51
+
52
+ ### Git Commands
53
+
54
+ ```bash
55
+ # Clone Space repository
56
+ git clone https://huggingface.co/spaces/bldeaw/ml_service
57
+ cd ml_service
58
+
59
+ # Add remote (if working from local repo)
60
+ git remote add hf https://huggingface.co/spaces/bldeaw/ml_service
61
+
62
+ # Push to Space
63
+ git push hf main
64
+ ```
65
+
66
+ ---
67
+
68
+ ## API Endpoints
69
+
70
+ - **Health Check**: `https://bldeaw-ml-service.hf.space/health`
71
+ - **Job Failure Prediction**: `https://bldeaw-ml-service.hf.space/predict/job-fail`
72
+ - **Anomaly Detection**: `https://bldeaw-ml-service.hf.space/detect/anomaly`
73
+ - **API Documentation**: `https://bldeaw-ml-service.hf.space/docs`
74
+ - **ReDoc**: `https://bldeaw-ml-service.hf.space/redoc`
75
+
76
+ ---
77
+
78
+ ## Space Settings
79
+
80
+ - **SDK**: Docker
81
+ - **Hardware**: CPU Basic (or as configured)
82
+ - **Visibility**: Public/Private (as configured)
83
+ - **Port**: 7860 (Hugging Face Spaces default)
84
+
85
+ ---
86
+
87
+ ## Deployment Status
88
+
89
+ Check deployment status at:
90
+ - **Space Page**: https://huggingface.co/spaces/bldeaw/ml_service
91
+ - **Logs**: https://huggingface.co/spaces/bldeaw/ml_service/logs
92
+
93
+ ---
94
+
95
+ ## Notes
96
+
97
+ - Space URL format: `https://{username}-{space-name}.hf.space`
98
+ - Replace hyphens in space name with hyphens in URL
99
+ - Example: `bldeaw/ml_service` → `bldeaw-ml-service.hf.space`
TESTING.md ADDED
@@ -0,0 +1,380 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # API Testing Guide
2
+
3
+ คู่มือการทดสอบ API สำหรับ Job Failure Prediction & Anomaly Detection
4
+
5
+ ## 🚀 วิธีที่ 1: ใช้ Test Script (แนะนำ)
6
+
7
+ ### Local Testing
8
+
9
+ ```bash
10
+ # Test API ที่รันบน localhost:8000 (default)
11
+ python test_api.py
12
+
13
+ # Test API ที่รันบน localhost:7860 (Hugging Face Spaces port)
14
+ python test_api.py --port 7860
15
+
16
+ # Test API ที่ URL เฉพาะ
17
+ python test_api.py --url http://localhost:8000
18
+ ```
19
+
20
+ ### Hugging Face Spaces Testing
21
+
22
+ ```bash
23
+ # Test API บน Hugging Face Spaces
24
+ python test_api.py --url https://your-username-your-space.hf.space
25
+ ```
26
+
27
+ ---
28
+
29
+ ## 🧪 วิธีที่ 2: ใช้ cURL
30
+
31
+ ### Health Check
32
+
33
+ ```bash
34
+ # Local
35
+ curl http://localhost:8000/health
36
+
37
+ # Hugging Face Spaces
38
+ curl https://your-username-your-space.hf.space/health
39
+ ```
40
+
41
+ ### Job Failure Prediction
42
+
43
+ ```bash
44
+ # Local
45
+ curl -X POST http://localhost:8000/predict/job-fail \
46
+ -H "Content-Type: application/json" \
47
+ -d '{
48
+ "zone": "prod",
49
+ "job_nm": "daily_export_customer",
50
+ "job_start_time": "2026-01-21T01:00:00",
51
+ "duration_sec": 5400,
52
+ "status": "SUCCESS",
53
+ "err_msg": "",
54
+ "explain": true
55
+ }'
56
+
57
+ # Hugging Face Spaces
58
+ curl -X POST https://your-username-your-space.hf.space/predict/job-fail \
59
+ -H "Content-Type: application/json" \
60
+ -d '{
61
+ "zone": "prod",
62
+ "job_nm": "daily_export_customer",
63
+ "job_start_time": "2026-01-21T01:00:00",
64
+ "duration_sec": 5400,
65
+ "status": "SUCCESS",
66
+ "explain": true
67
+ }'
68
+ ```
69
+
70
+ ### Anomaly Detection
71
+
72
+ ```bash
73
+ # Local
74
+ curl -X POST http://localhost:8000/detect/anomaly \
75
+ -H "Content-Type: application/json" \
76
+ -d '{
77
+ "features": {
78
+ "duration_sec": 5400,
79
+ "duration_zscore": 1.6,
80
+ "avg_duration_7": 3000,
81
+ "failure_rate_7": 0.15,
82
+ "err_msg_len": 0,
83
+ "hour_sin": 0.2588,
84
+ "hour_cos": 0.9659
85
+ },
86
+ "threshold": 0.01
87
+ }'
88
+
89
+ # Hugging Face Spaces
90
+ curl -X POST https://your-username-your-space.hf.space/detect/anomaly \
91
+ -H "Content-Type: application/json" \
92
+ -d '{
93
+ "features": {
94
+ "duration_sec": 5400,
95
+ "duration_zscore": 1.6,
96
+ "avg_duration_7": 3000,
97
+ "failure_rate_7": 0.15,
98
+ "err_msg_len": 0
99
+ }
100
+ }'
101
+ ```
102
+
103
+ ---
104
+
105
+ ## 🌐 วิธีที่ 3: ใช้ Swagger UI (Interactive)
106
+
107
+ ### Local
108
+
109
+ 1. เริ่ม API server:
110
+ ```bash
111
+ uvicorn app:app --host 0.0.0.0 --port 8000
112
+ ```
113
+
114
+ 2. เปิด browser ไปที่:
115
+ ```
116
+ http://localhost:8000/docs
117
+ ```
118
+
119
+ 3. ทดสอบ endpoints ผ่าน interactive UI
120
+
121
+ ### Hugging Face Spaces
122
+
123
+ 1. ไปที่ Space page
124
+ 2. คลิก "API" tab หรือไปที่:
125
+ ```
126
+ https://your-username-your-space.hf.space/docs
127
+ ```
128
+ 3. ทดสอบ endpoints ผ่าน interactive UI
129
+
130
+ ---
131
+
132
+ ## 📝 วิธีที่ 4: ใช้ Python Requests
133
+
134
+ ### Example Script
135
+
136
+ ```python
137
+ import requests
138
+ import json
139
+
140
+ # Base URL
141
+ BASE_URL = "http://localhost:8000" # หรือ URL ของ Hugging Face Space
142
+
143
+ # Test Health
144
+ response = requests.get(f"{BASE_URL}/health")
145
+ print("Health Check:", response.json())
146
+
147
+ # Test Prediction
148
+ payload = {
149
+ "zone": "prod",
150
+ "job_nm": "daily_export",
151
+ "job_start_time": "2026-01-21T01:00:00",
152
+ "duration_sec": 5400,
153
+ "status": "SUCCESS",
154
+ "explain": True
155
+ }
156
+ response = requests.post(
157
+ f"{BASE_URL}/predict/job-fail",
158
+ json=payload
159
+ )
160
+ print("Prediction:", response.json())
161
+
162
+ # Test Anomaly Detection
163
+ payload = {
164
+ "features": {
165
+ "duration_sec": 5400,
166
+ "duration_zscore": 1.6,
167
+ "err_msg_len": 0
168
+ }
169
+ }
170
+ response = requests.post(
171
+ f"{BASE_URL}/detect/anomaly",
172
+ json=payload
173
+ )
174
+ print("Anomaly Detection:", response.json())
175
+ ```
176
+
177
+ ---
178
+
179
+ ## 🔍 วิธีที่ 5: ใช้ Postman / Insomnia
180
+
181
+ ### Import Collection
182
+
183
+ 1. สร้าง new collection
184
+ 2. เพิ่ม requests:
185
+
186
+ **Health Check**
187
+ - Method: `GET`
188
+ - URL: `http://localhost:8000/health`
189
+
190
+ **Job Failure Prediction**
191
+ - Method: `POST`
192
+ - URL: `http://localhost:8000/predict/job-fail`
193
+ - Headers: `Content-Type: application/json`
194
+ - Body (JSON):
195
+ ```json
196
+ {
197
+ "zone": "prod",
198
+ "job_nm": "daily_export",
199
+ "job_start_time": "2026-01-21T01:00:00",
200
+ "duration_sec": 5400,
201
+ "status": "SUCCESS",
202
+ "explain": true
203
+ }
204
+ ```
205
+
206
+ **Anomaly Detection**
207
+ - Method: `POST`
208
+ - URL: `http://localhost:8000/detect/anomaly`
209
+ - Headers: `Content-Type: application/json`
210
+ - Body (JSON):
211
+ ```json
212
+ {
213
+ "features": {
214
+ "duration_sec": 5400,
215
+ "duration_zscore": 1.6,
216
+ "err_msg_len": 0
217
+ }
218
+ }
219
+ ```
220
+
221
+ ---
222
+
223
+ ## ✅ Expected Responses
224
+
225
+ ### Health Check Response
226
+
227
+ ```json
228
+ {
229
+ "status": "healthy",
230
+ "service": "job-failure-prediction",
231
+ "models_loaded": {
232
+ "predictor": true,
233
+ "anomaly_detector": true
234
+ }
235
+ }
236
+ ```
237
+
238
+ ### Job Failure Prediction Response
239
+
240
+ ```json
241
+ {
242
+ "fail_probability": 0.79,
243
+ "risk_level": "MEDIUM",
244
+ "top_drivers": [
245
+ {
246
+ "feature": "failure_rate_7",
247
+ "shap_value": 0.30,
248
+ "effect": "increase"
249
+ }
250
+ ],
251
+ "recommended_actions": [
252
+ "Monitor upstream dependencies and recent job history"
253
+ ]
254
+ }
255
+ ```
256
+
257
+ ### Anomaly Detection Response
258
+
259
+ ```json
260
+ {
261
+ "reconstruction_error": 0.0235,
262
+ "is_anomaly": true,
263
+ "threshold": 0.01,
264
+ "top_drivers": [
265
+ {
266
+ "feature": "duration_zscore",
267
+ "error": 0.0142
268
+ }
269
+ ]
270
+ }
271
+ ```
272
+
273
+ ---
274
+
275
+ ## 🐛 Troubleshooting
276
+
277
+ ### Connection Error
278
+
279
+ **ปัญหา:** `ConnectionError: Could not connect to API`
280
+
281
+ **แก้ไข:**
282
+ - ตรวจสอบว่า API server กำลังรันอยู่
283
+ - ตรวจสอบ URL และ port
284
+ - ตรวจสอบ firewall settings
285
+
286
+ ### 404 Not Found
287
+
288
+ **ปัญหา:** `404 Not Found`
289
+
290
+ **แก้ไข:**
291
+ - ตรวจสอบ endpoint path (`/health`, `/predict/job-fail`, `/detect/anomaly`)
292
+ - ตรวจสอบว่า API server รันอยู่
293
+
294
+ ### 500 Internal Server Error
295
+
296
+ **ปัญหา:** `500 Internal Server Error`
297
+
298
+ **แก้ไข:**
299
+ - ตรวจสอบ logs ของ API server
300
+ - ตรวจสอบว่า models ถูก load ถูกต้อง
301
+ - ตรวจสอบ request payload format
302
+
303
+ ### Models Not Loaded
304
+
305
+ **ปัญหา:** `models_loaded: {"predictor": false, "anomaly_detector": false}`
306
+
307
+ **แก้ไข:**
308
+ - ตรวจสอบว่า models อยู่ใน `models/` directory
309
+ - ตรวจสอบว่า model files มีครบถ้วน
310
+ - ตรวจสอบ logs สำหรับ error messages
311
+
312
+ ---
313
+
314
+ ## 📊 Test Cases
315
+
316
+ ### Test Case 1: Basic Health Check
317
+ ```bash
318
+ curl http://localhost:8000/health
319
+ ```
320
+ **Expected:** Status 200, models_loaded = true
321
+
322
+ ### Test Case 2: Prediction with Minimal Data
323
+ ```bash
324
+ curl -X POST http://localhost:8000/predict/job-fail \
325
+ -H "Content-Type: application/json" \
326
+ -d '{"job_nm": "test_job", "job_start_time": "2026-01-21T01:00:00"}'
327
+ ```
328
+ **Expected:** Status 200, fail_probability between 0-1
329
+
330
+ ### Test Case 3: Prediction with Full Data
331
+ ```bash
332
+ curl -X POST http://localhost:8000/predict/job-fail \
333
+ -H "Content-Type: application/json" \
334
+ -d '{
335
+ "zone": "prod",
336
+ "job_nm": "daily_export",
337
+ "job_start_time": "2026-01-21T01:00:00",
338
+ "duration_sec": 5400,
339
+ "status": "SUCCESS",
340
+ "explain": true
341
+ }'
342
+ ```
343
+ **Expected:** Status 200, includes top_drivers and recommended_actions
344
+
345
+ ### Test Case 4: Anomaly Detection
346
+ ```bash
347
+ curl -X POST http://localhost:8000/detect/anomaly \
348
+ -H "Content-Type: application/json" \
349
+ -d '{
350
+ "features": {
351
+ "duration_sec": 10000,
352
+ "duration_zscore": 3.0,
353
+ "err_msg_len": 0
354
+ }
355
+ }'
356
+ ```
357
+ **Expected:** Status 200, is_anomaly = true (for high zscore)
358
+
359
+ ---
360
+
361
+ ## 🎯 Quick Test Checklist
362
+
363
+ - [ ] Health endpoint returns 200
364
+ - [ ] Models are loaded (check health response)
365
+ - [ ] Prediction endpoint accepts requests
366
+ - [ ] Prediction returns valid probability (0-1)
367
+ - [ ] Risk level is one of: MINIMAL, LOW, MEDIUM, CRITICAL
368
+ - [ ] SHAP explanations work (when explain=true)
369
+ - [ ] Anomaly detection accepts feature dict
370
+ - [ ] Anomaly detection returns is_anomaly boolean
371
+ - [ ] Error handling works (invalid requests return 422/500)
372
+ - [ ] CORS works (if testing from browser)
373
+
374
+ ---
375
+
376
+ ## 📚 Additional Resources
377
+
378
+ - **Swagger UI**: `/docs` - Interactive API documentation
379
+ - **ReDoc**: `/redoc` - Alternative API documentation
380
+ - **OpenAPI Schema**: `/openapi.json` - Machine-readable API schema
model_utils.py CHANGED
@@ -305,6 +305,8 @@ class AnomalyDetector:
305
  return {
306
  'reconstruction_error': 0.0,
307
  'is_anomaly': False,
 
 
308
  'error': 'Model not loaded'
309
  }
310
 
@@ -356,5 +358,7 @@ class AnomalyDetector:
356
  return {
357
  'reconstruction_error': 0.0,
358
  'is_anomaly': False,
 
 
359
  'error': str(e)
360
  }
 
305
  return {
306
  'reconstruction_error': 0.0,
307
  'is_anomaly': False,
308
+ 'threshold': float(self.global_threshold) if hasattr(self, 'global_threshold') else 0.01,
309
+ 'top_drivers': [],
310
  'error': 'Model not loaded'
311
  }
312
 
 
358
  return {
359
  'reconstruction_error': 0.0,
360
  'is_anomaly': False,
361
+ 'threshold': float(self.global_threshold) if hasattr(self, 'global_threshold') else 0.01,
362
+ 'top_drivers': [],
363
  'error': str(e)
364
  }
models/anomaly_autoencoder_cpu.keras ADDED
Binary file (94.8 kB). View file
 
models/anomaly_features.joblib ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:f81a93f3be00e1e437383a368faa3f93555b4c74e5066db593f7e9e973d5538a
3
+ size 119
models/anomaly_scaler.joblib ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:a8ce21dfa7ef83111e4ed6c34a97795a0db69bdad7d31b0dd553eb8b4e051e5a
3
+ size 1087
models/anomaly_threshold.joblib ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:edd1017fcc8180ff81f2cad39858d677eba86e8a10cc643b236117e1d9e05e8d
3
+ size 117
models/feature_schema.json ADDED
@@ -0,0 +1,39 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "feature_columns": {
3
+ "numeric": [
4
+ "duration_sec",
5
+ "duration_zscore",
6
+ "avg_duration_7",
7
+ "failure_rate_7",
8
+ "err_msg_len",
9
+ "hour_sin",
10
+ "hour_cos"
11
+ ],
12
+ "categorical": [
13
+ "job_nm",
14
+ "tasksgroup_nm",
15
+ "zone",
16
+ "is_zeppelin",
17
+ "is_weekend"
18
+ ],
19
+ "anomaly_numeric": [
20
+ "duration_sec",
21
+ "duration_zscore",
22
+ "avg_duration_7",
23
+ "failure_rate_7",
24
+ "err_msg_len",
25
+ "hour_sin",
26
+ "hour_cos"
27
+ ]
28
+ },
29
+ "required_fields": [
30
+ "zone",
31
+ "job_nm",
32
+ "tasksgroup_nm",
33
+ "job_start_time",
34
+ "duration",
35
+ "status",
36
+ "err_msg",
37
+ "zeppelin"
38
+ ]
39
+ }
models/job_fail_pipeline_cpu.joblib ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:b83f1647154d65fd13cf34a5a7739fec20819677acd5abccd43befdb7902d207
3
+ size 164484
models/shap_background.npy ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:839f96623b8c5d9a3ffc2f224d7077e9b12b381f99a280bcfb1846610f216b51
3
+ size 791328
test_api.py CHANGED
@@ -1,11 +1,25 @@
1
  """
2
  Simple test script for API endpoints.
3
  Run this after starting the service to verify endpoints work correctly.
 
 
 
 
 
 
 
 
 
 
4
  """
5
  import requests
6
  import json
 
 
7
 
8
- BASE_URL = "http://localhost:8000"
 
 
9
 
10
 
11
  def test_health():
@@ -67,13 +81,54 @@ def test_anomaly():
67
  print()
68
 
69
 
70
- if __name__ == "__main__":
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
71
  try:
72
  test_health()
73
  test_predict()
74
  test_anomaly()
75
- print("All tests completed!")
 
76
  except requests.exceptions.ConnectionError:
77
- print("Error: Could not connect to API. Make sure the service is running on http://localhost:8000")
 
 
78
  except Exception as e:
79
- print(f"Error: {e}")
 
 
 
 
 
 
 
 
1
  """
2
  Simple test script for API endpoints.
3
  Run this after starting the service to verify endpoints work correctly.
4
+
5
+ Usage:
6
+ # Test local API (default port 8000)
7
+ python test_api.py
8
+
9
+ # Test local API on port 7860 (Hugging Face Spaces default)
10
+ python test_api.py --port 7860
11
+
12
+ # Test Hugging Face Spaces API
13
+ python test_api.py --url https://bldeaw-ml-service.hf.space
14
  """
15
  import requests
16
  import json
17
+ import sys
18
+ import argparse
19
 
20
+ # Default configuration
21
+ DEFAULT_URL = "http://localhost:8000"
22
+ DEFAULT_PORT = 8000
23
 
24
 
25
  def test_health():
 
81
  print()
82
 
83
 
84
+ def main():
85
+ """Main test function."""
86
+ parser = argparse.ArgumentParser(description="Test API endpoints")
87
+ parser.add_argument(
88
+ "--url",
89
+ type=str,
90
+ default=None,
91
+ help="Base URL of the API (e.g., http://localhost:8000 or https://bldeaw-ml-service.hf.space)"
92
+ )
93
+ parser.add_argument(
94
+ "--port",
95
+ type=int,
96
+ default=None,
97
+ help="Port number for local API (default: 8000)"
98
+ )
99
+ args = parser.parse_args()
100
+
101
+ # Determine base URL
102
+ if args.url:
103
+ base_url = args.url.rstrip('/')
104
+ elif args.port:
105
+ base_url = f"http://localhost:{args.port}"
106
+ else:
107
+ base_url = DEFAULT_URL
108
+
109
+ global BASE_URL
110
+ BASE_URL = base_url
111
+
112
+ print(f"Testing API at: {BASE_URL}")
113
+ print("=" * 60)
114
+ print()
115
+
116
  try:
117
  test_health()
118
  test_predict()
119
  test_anomaly()
120
+ print("=" * 60)
121
+ print("✅ All tests completed successfully!")
122
  except requests.exceptions.ConnectionError:
123
+ print("Error: Could not connect to API.")
124
+ print(f" Make sure the service is running on {BASE_URL}")
125
+ sys.exit(1)
126
  except Exception as e:
127
+ print(f"Error: {e}")
128
+ import traceback
129
+ traceback.print_exc()
130
+ sys.exit(1)
131
+
132
+
133
+ if __name__ == "__main__":
134
+ main()