Omarelrayes commited on
Commit
7896889
·
0 Parent(s):

clean initial commit without large files

Browse files
.gitattributes ADDED
@@ -0,0 +1,35 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ *.7z filter=lfs diff=lfs merge=lfs -text
2
+ *.arrow filter=lfs diff=lfs merge=lfs -text
3
+ *.bin filter=lfs diff=lfs merge=lfs -text
4
+ *.bz2 filter=lfs diff=lfs merge=lfs -text
5
+ *.ckpt filter=lfs diff=lfs merge=lfs -text
6
+ *.ftz filter=lfs diff=lfs merge=lfs -text
7
+ *.gz filter=lfs diff=lfs merge=lfs -text
8
+ *.h5 filter=lfs diff=lfs merge=lfs -text
9
+ *.joblib filter=lfs diff=lfs merge=lfs -text
10
+ *.lfs.* filter=lfs diff=lfs merge=lfs -text
11
+ *.mlmodel filter=lfs diff=lfs merge=lfs -text
12
+ *.model filter=lfs diff=lfs merge=lfs -text
13
+ *.msgpack filter=lfs diff=lfs merge=lfs -text
14
+ *.npy filter=lfs diff=lfs merge=lfs -text
15
+ *.npz filter=lfs diff=lfs merge=lfs -text
16
+ *.onnx filter=lfs diff=lfs merge=lfs -text
17
+ *.ot filter=lfs diff=lfs merge=lfs -text
18
+ *.parquet filter=lfs diff=lfs merge=lfs -text
19
+ *.pb filter=lfs diff=lfs merge=lfs -text
20
+ *.pickle filter=lfs diff=lfs merge=lfs -text
21
+ *.pkl filter=lfs diff=lfs merge=lfs -text
22
+ *.pt filter=lfs diff=lfs merge=lfs -text
23
+ *.pth filter=lfs diff=lfs merge=lfs -text
24
+ *.rar filter=lfs diff=lfs merge=lfs -text
25
+ *.safetensors filter=lfs diff=lfs merge=lfs -text
26
+ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
27
+ *.tar.* filter=lfs diff=lfs merge=lfs -text
28
+ *.tar filter=lfs diff=lfs merge=lfs -text
29
+ *.tflite filter=lfs diff=lfs merge=lfs -text
30
+ *.tgz filter=lfs diff=lfs merge=lfs -text
31
+ *.wasm filter=lfs diff=lfs merge=lfs -text
32
+ *.xz filter=lfs diff=lfs merge=lfs -text
33
+ *.zip filter=lfs diff=lfs merge=lfs -text
34
+ *.zst filter=lfs diff=lfs merge=lfs -text
35
+ *tfevents* filter=lfs diff=lfs merge=lfs -text
.gitignore ADDED
@@ -0,0 +1,20 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # environment
2
+ venv/
3
+ .venv/
4
+
5
+ # ML / MLflow
6
+ mlflow/
7
+ mlartifacts/
8
+ *.keras
9
+ *.h5
10
+ *.pkl
11
+
12
+
13
+
14
+ # Python cache
15
+ __pycache__/
16
+ *.pyc
17
+
18
+ # IDE
19
+ .vscode/
20
+ .idea/
.ipynb_checkpoints/Untitled-checkpoint.ipynb ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ {
2
+ "cells": [],
3
+ "metadata": {},
4
+ "nbformat": 4,
5
+ "nbformat_minor": 5
6
+ }
README.md ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ title: Api
3
+ emoji: 🏆
4
+ colorFrom: yellow
5
+ colorTo: pink
6
+ sdk: docker
7
+ pinned: false
8
+ license: mit
9
+ short_description: fast api for models
10
+ ---
11
+
12
+ Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
Untitled.ipynb ADDED
@@ -0,0 +1,52 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "cells": [
3
+ {
4
+ "cell_type": "code",
5
+ "execution_count": 1,
6
+ "id": "eeb7310e-a5e5-48c0-8bc3-377ecb7a50f5",
7
+ "metadata": {},
8
+ "outputs": [
9
+ {
10
+ "name": "stdout",
11
+ "output_type": "stream",
12
+ "text": [
13
+ "3.12.8 | packaged by Anaconda, Inc. | (main, Dec 11 2024, 16:48:34) [MSC v.1929 64 bit (AMD64)]\n"
14
+ ]
15
+ }
16
+ ],
17
+ "source": [
18
+ "import sys\n",
19
+ "print(sys.version)"
20
+ ]
21
+ },
22
+ {
23
+ "cell_type": "code",
24
+ "execution_count": null,
25
+ "id": "7ef6e852-34a0-43c6-ab8c-412d16b91f6e",
26
+ "metadata": {},
27
+ "outputs": [],
28
+ "source": []
29
+ }
30
+ ],
31
+ "metadata": {
32
+ "kernelspec": {
33
+ "display_name": "Python [conda env:base] *",
34
+ "language": "python",
35
+ "name": "conda-base-py"
36
+ },
37
+ "language_info": {
38
+ "codemirror_mode": {
39
+ "name": "ipython",
40
+ "version": 3
41
+ },
42
+ "file_extension": ".py",
43
+ "mimetype": "text/x-python",
44
+ "name": "python",
45
+ "nbconvert_exporter": "python",
46
+ "pygments_lexer": "ipython3",
47
+ "version": "3.12.8"
48
+ }
49
+ },
50
+ "nbformat": 4,
51
+ "nbformat_minor": 5
52
+ }
api ADDED
@@ -0,0 +1 @@
 
 
1
+ Subproject commit e5938764404acd99fb31c8d96a34ce53099c9c5e
app/.ipynb_checkpoints/__init__-checkpoint.py ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
 
1
+ from .configs import get_classification_model, get_segmentation_model
2
+ from .models import PredictionResponse, HistoryItem
3
+
4
+ __all__ = [
5
+ "get_classification_model",
6
+ "get_segmentation_model",
7
+ "PredictionResponse",
8
+ "HistoryItem"
9
+ ]
app/.ipynb_checkpoints/configs-checkpoint.py ADDED
@@ -0,0 +1,57 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from pathlib import Path
2
+ from typing import Dict, Any, List, Optional
3
+ import mlflow
4
+ import mlflow.pyfunc
5
+
6
+ # =========================
7
+ # MLflow Config
8
+ # =========================
9
+ MLFLOW_URI = "http://127.0.0.1:5000"
10
+ mlflow.set_tracking_uri(MLFLOW_URI)
11
+
12
+ # =========================
13
+ # Lazy Models
14
+ # =========================
15
+ _classification_model: Optional[mlflow.pyfunc.PyFuncModel] = None
16
+ _segmentation_model: Optional[mlflow.pyfunc.PyFuncModel] = None
17
+
18
+
19
+ def get_classification_model():
20
+ global _classification_model
21
+ if _classification_model is None:
22
+ print(" Loading Classification Model........................\n############")
23
+ _classification_model = mlflow.pyfunc.load_model(
24
+ "models:/skin_cancer_classifier/1"
25
+ )
26
+ return _classification_model
27
+
28
+
29
+ def get_segmentation_model():
30
+ global _segmentation_model
31
+ if _segmentation_model is None:
32
+ print("Loading Segmentation Model........................\n############")
33
+ _segmentation_model = mlflow.pyfunc.load_model(
34
+ "models:/skin_cancer_segmenter/1"
35
+ )
36
+ return _segmentation_model
37
+
38
+
39
+ # =========================
40
+ # Metadata
41
+ # =========================
42
+ model_classes: Dict[int, str] = {
43
+ 0: "benign",
44
+ 1: "malicious"
45
+ }
46
+
47
+ request_history: List[Dict[str, Any]] = []
48
+
49
+ # =========================
50
+ # Storage
51
+ # =========================
52
+ STORAGE_DIR = Path("storage")
53
+ IMAGES_DIR = STORAGE_DIR / "images"
54
+ SEGMENTS_DIR = STORAGE_DIR / "segments"
55
+
56
+ for dir_path in [STORAGE_DIR, IMAGES_DIR, SEGMENTS_DIR]:
57
+ dir_path.mkdir(parents=True, exist_ok=True)
app/.ipynb_checkpoints/main-checkpoint.py ADDED
@@ -0,0 +1,141 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import uuid
2
+ from pathlib import Path
3
+ from datetime import datetime
4
+ from fastapi import FastAPI, UploadFile, File, BackgroundTasks, HTTPException
5
+ from typing import List
6
+
7
+ from . import configs as config
8
+ from .models import PredictionResponse, HistoryItem
9
+ from .services.predictor import predict_image
10
+ from .services.segmenter import run_segmentation
11
+
12
+ app = FastAPI(
13
+ title="AI Image Classification API",
14
+ version="2.0.0"
15
+ )
16
+
17
+ # =========================
18
+ # Models Status
19
+ # =========================
20
+ @app.get("/models/status")
21
+ async def models_status():
22
+ classification_model = config.get_classification_model()
23
+ segmentation_model = config.get_segmentation_model()
24
+
25
+ return {
26
+ "classification_loaded": classification_model is not None,
27
+ "segmentation_loaded": segmentation_model is not None,
28
+ "storage_paths": {
29
+ "images": str(config.IMAGES_DIR),
30
+ "segments": str(config.SEGMENTS_DIR)
31
+ }
32
+ }
33
+
34
+ # =========================
35
+ # Prediction Endpoint
36
+ # =========================
37
+ @app.post("/predict", response_model=PredictionResponse)
38
+ async def predict(
39
+ background_tasks: BackgroundTasks,
40
+ file: UploadFile = File(...)
41
+ ):
42
+ file_bytes = await file.read()
43
+
44
+ # validation
45
+ if len(file_bytes) > 10 * 1024 * 1024:
46
+ raise HTTPException(status_code=400, detail="File too large (10MB max)")
47
+
48
+ try:
49
+ prediction, confidence, image_path = predict_image(
50
+ file_bytes,
51
+ file.filename or "image.jpg"
52
+ )
53
+ except Exception as e:
54
+ raise HTTPException(status_code=500, detail=str(e))
55
+
56
+ # get model
57
+ model = config.get_classification_model()
58
+ model_version = getattr(model, "name", "mlflow-production")
59
+
60
+ #create history
61
+ request_id = str(uuid.uuid4())
62
+
63
+ history_item = {
64
+ "request_id": request_id,
65
+ "filename": file.filename or "image.jpg",
66
+ "image_path": image_path,
67
+ "prediction": prediction,
68
+ "confidence": confidence,
69
+ "model_version": model_version,
70
+ "timestamp": datetime.now().isoformat(),
71
+ "status": "classified",
72
+ "segmentation_path": None
73
+ }
74
+
75
+ config.request_history.append(history_item)
76
+
77
+ # run segmentation in background if needed
78
+ if prediction == "malicious":
79
+ background_tasks.add_task(
80
+ run_segmentation,
81
+ request_id,
82
+ Path(image_path).name,
83
+ image_path
84
+ )
85
+
86
+ return PredictionResponse(**history_item)
87
+
88
+ # =========================
89
+ # History
90
+ # =========================
91
+ @app.get("/history", response_model=List[HistoryItem])
92
+ async def get_history():
93
+ return [HistoryItem(**item) for item in config.request_history]
94
+
95
+ @app.get("/history/{request_id}", response_model=HistoryItem)
96
+ async def get_prediction(request_id: str):
97
+ for item in config.request_history:
98
+ if item["request_id"] == request_id:
99
+ return HistoryItem(**item)
100
+
101
+ raise HTTPException(status_code=404, detail="Prediction not found")
102
+
103
+ # =========================
104
+ # Root
105
+ # =========================
106
+ @app.get("/")
107
+ async def root():
108
+ class_ready = "READY" if config.get_classification_model() else "REQUIRED !!!!"
109
+ seg_ready = " READY" if config.get_segmentation_model() else "PTIONAL !"
110
+
111
+ return {
112
+ "service": "AI Image Classification API",
113
+ "classification": class_ready,
114
+ "segmentation": seg_ready,
115
+ "endpoint": "POST /predict",
116
+ "history": f"GET /history ({len(config.request_history)} records)",
117
+ "docs": "/docs"
118
+ }
119
+
120
+ # =========================
121
+ # Health Check
122
+ # =========================
123
+ @app.get("/health")
124
+ async def health():
125
+ return {
126
+ "status": "healthy",
127
+ "predict_ready": config.get_classification_model() is not None,
128
+ "total_predictions": len(config.request_history)
129
+ }
130
+
131
+ # =========================
132
+ # Run Server
133
+ # =========================
134
+ if __name__ == "main":
135
+ import uvicorn
136
+ uvicorn.run(
137
+ "main:app",
138
+ host="0.0.0.0",
139
+ port=8000,
140
+ reload=True
141
+ )
app/.ipynb_checkpoints/models-checkpoint.py ADDED
File without changes
app/.ipynb_checkpoints/storage-checkpoint.py ADDED
@@ -0,0 +1,26 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from pathlib import Path
2
+ import json
3
+ from typing import List, Dict, Any
4
+ from app.configs import IMAGES_DIR, SEGMENTS_DIR, request_history
5
+
6
+ def save_image(image_bytes: bytes, filename: str) -> str:
7
+ """Save image to storage"""
8
+ image_path = IMAGES_DIR / filename
9
+ with open(image_path, "wb") as f:
10
+ f.write(image_bytes)
11
+ return str(image_path)
12
+
13
+ def save_segmentation_result(seg_result: Dict[str, Any], request_id: str, image_filename: str) -> str:
14
+ """Save segmentation JSON result"""
15
+ seg_filename = f"{request_id}_{image_filename}_segment.json"
16
+ seg_path = SEGMENTS_DIR / seg_filename
17
+ with open(seg_path, "w") as f:
18
+ json.dump(seg_result, f, indent=2)
19
+ return str(seg_path)
20
+
21
+ def update_history(request_id: str, **updates: Any) -> None:
22
+ """Update history item by request_id"""
23
+ for item in request_history:
24
+ if item["request_id"] == request_id:
25
+ item.update(updates)
26
+ break
app/__init__.py ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
 
1
+ from .configs import get_classification_model, get_segmentation_model
2
+ from .models import PredictionResponse, HistoryItem
3
+
4
+ __all__ = [
5
+ "get_classification_model",
6
+ "get_segmentation_model",
7
+ "PredictionResponse",
8
+ "HistoryItem"
9
+ ]
app/configs.py ADDED
@@ -0,0 +1,121 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from pathlib import Path
2
+ from typing import Dict, Any, List, Optional
3
+
4
+ import mlflow
5
+ import mlflow.pyfunc
6
+
7
+
8
+ # =========================
9
+ # MLflow Config
10
+ # =========================
11
+
12
+ MLFLOW_URI = "https://omarelrayes-mlflow-server.hf.space"
13
+
14
+ mlflow.set_tracking_uri(
15
+ MLFLOW_URI
16
+ )
17
+
18
+
19
+ # =========================
20
+ # Run IDs from MLflow
21
+ # =========================
22
+
23
+ CLASSIFICATION_MODEL_URI = (
24
+ "runs:/8cae153bb747447b9f9afa237470e00f/model"
25
+ )
26
+
27
+
28
+ SEGMENTATION_MODEL_URI = (
29
+ "runs:/49dba7de7bb04aac851201051a497bf4/model"
30
+ )
31
+
32
+
33
+ # =========================
34
+ # Lazy Models
35
+ # =========================
36
+
37
+ _classification_model: Optional[
38
+ mlflow.pyfunc.PyFuncModel
39
+ ] = None
40
+
41
+
42
+ _segmentation_model: Optional[
43
+ mlflow.pyfunc.PyFuncModel
44
+ ] = None
45
+
46
+
47
+
48
+ def get_classification_model():
49
+
50
+ global _classification_model
51
+
52
+ if _classification_model is None:
53
+
54
+ print(
55
+ "Loading Classification Model..."
56
+ )
57
+
58
+ _classification_model = mlflow.pyfunc.load_model(
59
+ CLASSIFICATION_MODEL_URI
60
+ )
61
+
62
+ return _classification_model
63
+
64
+
65
+
66
+ def get_segmentation_model():
67
+
68
+ global _segmentation_model
69
+
70
+ if _segmentation_model is None:
71
+
72
+ print(
73
+ "Loading Segmentation Model..."
74
+ )
75
+
76
+ _segmentation_model = mlflow.pyfunc.load_model(
77
+ SEGMENTATION_MODEL_URI
78
+ )
79
+
80
+ return _segmentation_model
81
+
82
+
83
+
84
+ # =========================
85
+ # Metadata
86
+ # =========================
87
+
88
+ model_classes: Dict[int, str] = {
89
+
90
+ 0: "benign",
91
+ 1: "malicious"
92
+
93
+ }
94
+
95
+
96
+ request_history: List[Dict[str, Any]] = []
97
+
98
+
99
+
100
+ # =========================
101
+ # Storage
102
+ # =========================
103
+
104
+ STORAGE_DIR = Path("storage")
105
+
106
+ IMAGES_DIR = STORAGE_DIR / "images"
107
+
108
+ SEGMENTS_DIR = STORAGE_DIR / "segments"
109
+
110
+
111
+
112
+ for dir_path in [
113
+ STORAGE_DIR,
114
+ IMAGES_DIR,
115
+ SEGMENTS_DIR
116
+ ]:
117
+
118
+ dir_path.mkdir(
119
+ parents=True,
120
+ exist_ok=True
121
+ )
app/core/.ipynb_checkpoints/preprocessing-checkpoint.py ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
 
1
+ import numpy as np
2
+ import io
3
+ from PIL import Image
4
+
5
+ def preprocess_image(image_bytes: bytes, target_size=(224, 224)) -> np.ndarray:
6
+ image = Image.open(io.BytesIO(image_bytes)).convert("RGB")
7
+ image = image.resize(target_size)
8
+ img_array = np.array(image, dtype=np.float32) / 255.0
9
+ return np.expand_dims(img_array, axis=0)
app/core/.ipynb_checkpoints/validation-checkpoint.py ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
 
1
+ import io
2
+ from PIL import Image
3
+
4
+ def is_valid_image(file_bytes: bytes) -> bool:
5
+ try:
6
+ Image.open(io.BytesIO(file_bytes)).verify()
7
+ return True
8
+ except:
9
+ return False
app/core/preprocessing.py ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
 
1
+ import numpy as np
2
+ import io
3
+ from PIL import Image
4
+
5
+ def preprocess_image(image_bytes: bytes, target_size=(224, 224)) -> np.ndarray:
6
+ image = Image.open(io.BytesIO(image_bytes)).convert("RGB")
7
+ image = image.resize(target_size)
8
+ img_array = np.array(image, dtype=np.float32) / 255.0
9
+ return np.expand_dims(img_array, axis=0)
app/core/validation.py ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
 
1
+ import io
2
+ from PIL import Image
3
+
4
+ def is_valid_image(file_bytes: bytes) -> bool:
5
+ try:
6
+ Image.open(io.BytesIO(file_bytes)).verify()
7
+ return True
8
+ except:
9
+ return False
app/main.py ADDED
@@ -0,0 +1,140 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import uuid
2
+ from pathlib import Path
3
+ from datetime import datetime
4
+ from fastapi import FastAPI, UploadFile, File, BackgroundTasks, HTTPException
5
+ from typing import List
6
+
7
+ from . import configs as config
8
+ from .models import PredictionResponse, HistoryItem
9
+ from .services.predictor import predict_image
10
+ from .services.segmenter import run_segmentation
11
+
12
+ app = FastAPI(
13
+ title="AI Image Classification API",
14
+ version="2.0.0"
15
+ )
16
+
17
+ # =========================
18
+ # Models Status
19
+ # =========================
20
+ @app.get("/models/status")
21
+ async def models_status():
22
+ classification_model = config.get_classification_model()
23
+ segmentation_model = config.get_segmentation_model()
24
+
25
+ return {
26
+ "classification_loaded": classification_model is not None,
27
+ "segmentation_loaded": segmentation_model is not None,
28
+ "storage_paths": {
29
+ "images": str(config.IMAGES_DIR),
30
+ "segments": str(config.SEGMENTS_DIR)
31
+ }
32
+ }
33
+
34
+ # =========================
35
+ # Prediction Endpoint
36
+ # =========================
37
+ @app.post("/predict", response_model=PredictionResponse)
38
+ async def predict(
39
+ background_tasks: BackgroundTasks,
40
+ file: UploadFile = File(...)
41
+ ):
42
+ file_bytes = await file.read()
43
+
44
+ # validation
45
+ if len(file_bytes) > 10 * 1024 * 1024:
46
+ raise HTTPException(status_code=400, detail="File too large (10MB max)")
47
+
48
+ try:
49
+ prediction, confidence, image_path = predict_image(
50
+ file_bytes,
51
+ file.filename or "image.jpg"
52
+ )
53
+ except Exception as e:
54
+ raise HTTPException(status_code=500, detail=str(e))
55
+
56
+ # get model
57
+ model = config.get_classification_model()
58
+ model_version = getattr(model, "name", "mlflow-production")
59
+
60
+ #create history
61
+ request_id = str(uuid.uuid4())
62
+
63
+ history_item = {
64
+ "request_id": request_id,
65
+ "filename": file.filename or "image.jpg",
66
+ "image_path": image_path,
67
+ "prediction": prediction,
68
+ "confidence": confidence,
69
+ "model_version": model_version,
70
+ "timestamp": datetime.now().isoformat(),
71
+ "status": "classified",
72
+ "segmentation_path": None
73
+ }
74
+
75
+ config.request_history.append(history_item)
76
+
77
+ # run segmentation in background if needed
78
+ if prediction == "malicious":
79
+ background_tasks.add_task(
80
+ run_segmentation,
81
+ request_id,
82
+ Path(image_path).name,
83
+ image_path
84
+ )
85
+
86
+ return PredictionResponse(**history_item)
87
+
88
+ # =========================
89
+ # History
90
+ # =========================
91
+ @app.get("/history", response_model=List[HistoryItem])
92
+ async def get_history():
93
+ return [HistoryItem(**item) for item in config.request_history]
94
+
95
+ @app.get("/history/{request_id}", response_model=HistoryItem)
96
+ async def get_prediction(request_id: str):
97
+ for item in config.request_history:
98
+ if item["request_id"] == request_id:
99
+ return HistoryItem(**item)
100
+
101
+ raise HTTPException(status_code=404, detail="Prediction not found")
102
+
103
+ # =========================
104
+ # Root
105
+ # =========================
106
+ @app.get("/")
107
+ async def root():
108
+ class_ready = "READY" if config.get_classification_model() else "REQUIRED !!!!"
109
+ seg_ready = " READY" if config.get_segmentation_model() else "PTIONAL !"
110
+
111
+ return {
112
+ "service": "AI Image Classification API",
113
+ "classification": class_ready,
114
+ "segmentation": seg_ready,
115
+ "endpoint": "POST /predict",
116
+ "history": f"GET /history ({len(config.request_history)} records)",
117
+ "docs": "/docs"
118
+ }
119
+
120
+ # =========================
121
+ # Health Check
122
+ # =========================
123
+ @app.get("/health")
124
+ async def health():
125
+ return {
126
+ "status": "healthy",
127
+ "predict_ready": config.get_classification_model() is not None,
128
+ "total_predictions": len(config.request_history)
129
+ }
130
+
131
+ # =========================
132
+ # Run Server
133
+ # =========================
134
+ if __name__ == "main":
135
+ import uvicorn
136
+ uvicorn.run(
137
+ "app.main:app",
138
+ host="0.0.0.0",
139
+ port=7860
140
+ )
app/models.py ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from pydantic import BaseModel
2
+ from typing import Optional
3
+ from datetime import datetime
4
+
5
+ class PredictionResponse(BaseModel):
6
+ request_id: str
7
+ filename: str
8
+ image_path: str
9
+ prediction: str
10
+ confidence: float
11
+ model_version: str
12
+ status: str = "completed"
13
+
14
+ class HistoryItem(PredictionResponse):
15
+ timestamp: str
16
+ segmentation_path: Optional[str] = None
app/services/.ipynb_checkpoints/predictor-checkpoint.py ADDED
@@ -0,0 +1,34 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import numpy as np
2
+ from typing import Tuple
3
+ from app.configs import get_classification_model, model_classes
4
+ from app.storage import save_image
5
+ from app.core.preprocessing import preprocess_image
6
+ from app.core.validation import is_valid_image
7
+
8
+ def predict_image(file_bytes: bytes, filename: str) -> Tuple[str, float, str]:
9
+ # checking if the image is good or no
10
+ if not is_valid_image(file_bytes):
11
+ raise ValueError("Invalid image")
12
+
13
+ # model loading or ( vonnecting with mlflow )
14
+ model = get_classification_model()
15
+
16
+ if model is None:
17
+ raise ValueError("Classification model not loaded")
18
+
19
+ image_filename = f"{hash(filename)}.jpg"
20
+ image_path = save_image(file_bytes, image_filename)
21
+
22
+ img_array = preprocess_image(file_bytes)
23
+
24
+ # using the model
25
+ predictions = model.predict(img_array)
26
+
27
+ confidence = float(np.max(predictions))
28
+ predicted_class = int(np.argmax(predictions))
29
+
30
+ prediction = model_classes.get(predicted_class, "unknown")
31
+
32
+ print(f"CLASSIFICATION: {prediction} ({confidence:.3f})")
33
+
34
+ return prediction, confidence, image_path
app/services/.ipynb_checkpoints/segmenter-checkpoint.py ADDED
@@ -0,0 +1,55 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import asyncio
2
+ import numpy as np
3
+ from datetime import datetime
4
+ from typing import Dict, Any
5
+ from app.configs import get_segmentation_model
6
+ from app.storage import save_segmentation_result, update_history
7
+ from app.core.preprocessing import preprocess_image
8
+
9
+
10
+ async def run_segmentation(request_id: str, image_filename: str, image_path: str):
11
+ print(f" SEGMENTING {request_id}.......")
12
+
13
+ await asyncio.sleep(1)
14
+
15
+ seg_result: Dict[str, Any] = {
16
+ "request_id": request_id,
17
+ "image_filename": image_filename,
18
+ "timestamp": datetime.now().isoformat(),
19
+ "detections": []
20
+ }
21
+
22
+ # model loading in updated way !!!!!!
23
+ model = get_segmentation_model()
24
+
25
+ if model is None:
26
+ seg_result["error"] = "Segmentation model not loaded"
27
+
28
+ else:
29
+ try:
30
+ with open(image_path, "rb") as f:
31
+ img_bytes = f.read()
32
+
33
+ img_array = preprocess_image(img_bytes, target_size=(256, 256))
34
+
35
+ # prediction using the function of it
36
+ masks = model.predict(img_array)
37
+
38
+ seg_result.update({
39
+ "model_used": True,
40
+ "masks_shape": list(masks.shape),
41
+ "max_confidence": float(np.max(masks))
42
+ })
43
+
44
+ except Exception as e:
45
+ seg_result["error"] = str(e)
46
+
47
+ seg_path = save_segmentation_result(seg_result, request_id, image_filename)
48
+
49
+ update_history(
50
+ request_id,
51
+ segmentation_path=seg_path,
52
+ status="segmented"
53
+ )
54
+
55
+ print(f"✅ SEGMENTATION DONE {request_id}")
app/services/predictor.py ADDED
@@ -0,0 +1,34 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import numpy as np
2
+ from typing import Tuple
3
+ from app.configs import get_classification_model, model_classes
4
+ from app.storage import save_image
5
+ from app.core.preprocessing import preprocess_image
6
+ from app.core.validation import is_valid_image
7
+
8
+ def predict_image(file_bytes: bytes, filename: str) -> Tuple[str, float, str]:
9
+ # checking if the image is good or no
10
+ if not is_valid_image(file_bytes):
11
+ raise ValueError("Invalid image")
12
+
13
+ # model loading or ( vonnecting with mlflow )
14
+ model = get_classification_model()
15
+
16
+ if model is None:
17
+ raise ValueError("Classification model not loaded")
18
+
19
+ image_filename = f"{hash(filename)}.jpg"
20
+ image_path = save_image(file_bytes, image_filename)
21
+
22
+ img_array = preprocess_image(file_bytes)
23
+
24
+ # using the model
25
+ predictions = model.predict(img_array)
26
+
27
+ confidence = float(np.max(predictions))
28
+ predicted_class = int(np.argmax(predictions))
29
+
30
+ prediction = model_classes.get(predicted_class, "unknown")
31
+
32
+ print(f"CLASSIFICATION: {prediction} ({confidence:.3f})")
33
+
34
+ return prediction, confidence, image_path
app/services/segmenter.py ADDED
@@ -0,0 +1,55 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import asyncio
2
+ import numpy as np
3
+ from datetime import datetime
4
+ from typing import Dict, Any
5
+ from app.configs import get_segmentation_model
6
+ from app.storage import save_segmentation_result, update_history
7
+ from app.core.preprocessing import preprocess_image
8
+
9
+
10
+ async def run_segmentation(request_id: str, image_filename: str, image_path: str):
11
+ print(f" SEGMENTING {request_id}.......")
12
+
13
+ await asyncio.sleep(1)
14
+
15
+ seg_result: Dict[str, Any] = {
16
+ "request_id": request_id,
17
+ "image_filename": image_filename,
18
+ "timestamp": datetime.now().isoformat(),
19
+ "detections": []
20
+ }
21
+
22
+ # model loading in updated way !!!!!!
23
+ model = get_segmentation_model()
24
+
25
+ if model is None:
26
+ seg_result["error"] = "Segmentation model not loaded"
27
+
28
+ else:
29
+ try:
30
+ with open(image_path, "rb") as f:
31
+ img_bytes = f.read()
32
+
33
+ img_array = preprocess_image(img_bytes, target_size=(256, 256))
34
+
35
+ # prediction using the function of it
36
+ masks = model.predict(img_array)
37
+
38
+ seg_result.update({
39
+ "model_used": True,
40
+ "masks_shape": list(masks.shape),
41
+ "max_confidence": float(np.max(masks))
42
+ })
43
+
44
+ except Exception as e:
45
+ seg_result["error"] = str(e)
46
+
47
+ seg_path = save_segmentation_result(seg_result, request_id, image_filename)
48
+
49
+ update_history(
50
+ request_id,
51
+ segmentation_path=seg_path,
52
+ status="segmented"
53
+ )
54
+
55
+ print(f"✅ SEGMENTATION DONE {request_id}")
app/storage.py ADDED
@@ -0,0 +1,26 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from pathlib import Path
2
+ import json
3
+ from typing import List, Dict, Any
4
+ from app.configs import IMAGES_DIR, SEGMENTS_DIR, request_history
5
+
6
+ def save_image(image_bytes: bytes, filename: str) -> str:
7
+ """Save image to storage"""
8
+ image_path = IMAGES_DIR / filename
9
+ with open(image_path, "wb") as f:
10
+ f.write(image_bytes)
11
+ return str(image_path)
12
+
13
+ def save_segmentation_result(seg_result: Dict[str, Any], request_id: str, image_filename: str) -> str:
14
+ """Save segmentation JSON result"""
15
+ seg_filename = f"{request_id}_{image_filename}_segment.json"
16
+ seg_path = SEGMENTS_DIR / seg_filename
17
+ with open(seg_path, "w") as f:
18
+ json.dump(seg_result, f, indent=2)
19
+ return str(seg_path)
20
+
21
+ def update_history(request_id: str, **updates: Any) -> None:
22
+ """Update history item by request_id"""
23
+ for item in request_history:
24
+ if item["request_id"] == request_id:
25
+ item.update(updates)
26
+ break
main.py ADDED
@@ -0,0 +1,316 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import uuid
3
+ import asyncio
4
+ import random
5
+ import json
6
+ import numpy as np
7
+ from datetime import datetime
8
+ from pathlib import Path
9
+ from typing import List, Dict, Any, Optional, Tuple
10
+ from PIL import Image
11
+ from fastapi import FastAPI, UploadFile, File, BackgroundTasks, HTTPException
12
+ from pydantic import BaseModel
13
+ import io
14
+ import tensorflow as tf
15
+ from tensorflow import keras
16
+
17
+ # ========================================
18
+ # CONFIGURATION
19
+ # ========================================
20
+ app = FastAPI(title="AI Image Classification + Segmentation (Keras)", version="2.0.0")
21
+
22
+ # Storage directories
23
+ STORAGE_DIR = Path("storage")
24
+ CLASSIFICATION_MODELS_DIR = STORAGE_DIR / "models" / "classification"
25
+ SEGMENTATION_MODELS_DIR = STORAGE_DIR / "models" / "segmentation"
26
+ IMAGES_DIR = STORAGE_DIR / "images"
27
+ SEGMENTS_DIR = STORAGE_DIR / "segments"
28
+
29
+ # Create directories
30
+ for dir_path in [STORAGE_DIR, CLASSIFICATION_MODELS_DIR, SEGMENTATION_MODELS_DIR, IMAGES_DIR, SEGMENTS_DIR]:
31
+ dir_path.mkdir(parents=True, exist_ok=True)
32
+
33
+ # Global Keras models
34
+ classification_model = None
35
+ segmentation_model = None
36
+ model_classes = {0: "benign", 1: "malicious"}
37
+
38
+ print("🤖 TensorFlow/Keras ready. Upload .keras models!")
39
+
40
+ # In-memory history
41
+ request_history: List[Dict[str, Any]] = []
42
+
43
+ # ========================================
44
+ # Pydantic Models
45
+ # ========================================
46
+ class PredictionResponse(BaseModel):
47
+ request_id: str
48
+ filename: str
49
+ image_path: str
50
+ prediction: str
51
+ confidence: float
52
+ model_version: str
53
+ status: str = "completed"
54
+
55
+ class HistoryItem(PredictionResponse):
56
+ timestamp: str
57
+ segmentation_path: Optional[str] = None
58
+
59
+ class ModelLoadResponse(BaseModel):
60
+ status: str
61
+ type: str
62
+ path: str
63
+ message: str
64
+
65
+ # ========================================
66
+ # IMAGE PREPROCESSING
67
+ # ========================================
68
+ def preprocess_image(image_bytes: bytes, target_size=(224, 224)) -> np.ndarray:
69
+ """Preprocess for Keras models"""
70
+ image = Image.open(io.BytesIO(image_bytes)).convert('RGB')
71
+ image = image.resize(target_size)
72
+ img_array = np.array(image, dtype=np.float32) / 255.0
73
+ return np.expand_dims(img_array, axis=0)
74
+
75
+ # ========================================
76
+ # MODEL LOADING
77
+ # ========================================
78
+ def load_keras_model(model_path: str, model_type: str) -> bool:
79
+ """Load .keras model"""
80
+ global classification_model, segmentation_model
81
+
82
+ try:
83
+ model = keras.models.load_model(model_path)
84
+ if model_type == "classification":
85
+ classification_model = model
86
+ elif model_type == "segmentation":
87
+ segmentation_model = model
88
+
89
+ print(f"✅ {model_type.upper()} model loaded: {model_path}")
90
+ print(f" Input: {model.input_shape}, Output: {model.output_shape}")
91
+ return True
92
+ except Exception as e:
93
+ print(f"❌ {model_type} model failed: {e}")
94
+ return False
95
+
96
+ # ========================================
97
+ # MODEL STORAGE
98
+ # ========================================
99
+ def save_classification_model(model_bytes: bytes, filename: str) -> str:
100
+ model_path = CLASSIFICATION_MODELS_DIR / filename
101
+ with open(model_path, "wb") as f:
102
+ f.write(model_bytes)
103
+ return str(model_path)
104
+
105
+ def save_segmentation_model(model_bytes: bytes, filename: str) -> str:
106
+ model_path = SEGMENTATION_MODELS_DIR / filename
107
+ with open(model_path, "wb") as f:
108
+ f.write(model_bytes)
109
+ return str(model_path)
110
+
111
+ # ========================================
112
+ # CORE PREDICTION FUNCTIONS
113
+ # ========================================
114
+ def predict_with_keras(image_bytes: bytes) -> Tuple[str, float]:
115
+ """Classification - BLOCKS if no model"""
116
+ global classification_model
117
+
118
+ if classification_model is None:
119
+ raise ValueError(
120
+ "No CLASSIFICATION model! Upload .keras: POST /models/classification/load"
121
+ )
122
+
123
+ img_array = preprocess_image(image_bytes)
124
+ predictions = classification_model.predict(img_array, verbose=0)
125
+ confidence = float(np.max(predictions))
126
+ predicted_class = np.argmax(predictions)
127
+ prediction = model_classes.get(predicted_class, "unknown")
128
+
129
+ print(f"🔮 CLASSIFICATION: {prediction} ({confidence:.3f})")
130
+ return prediction, confidence
131
+
132
+ async def run_segmentation(request_id: str, image_filename: str, image_path: str):
133
+ """Segmentation using Keras model or dummy"""
134
+ global segmentation_model
135
+
136
+ print(f"🔍 SEGMENTING {request_id}...")
137
+ await asyncio.sleep(random.uniform(2, 5))
138
+
139
+ seg_filename = f"{request_id}_{image_filename}_segment.json"
140
+ seg_path = SEGMENTS_DIR / seg_filename
141
+
142
+ seg_result = {"request_id": request_id, "image_filename": image_filename}
143
+
144
+ if segmentation_model:
145
+ try:
146
+ with open(image_path, "rb") as f:
147
+ img_bytes = f.read()
148
+ img_array = preprocess_image(img_bytes, target_size=(256, 256))
149
+ masks = segmentation_model.predict(img_array, verbose=0)
150
+ seg_result.update({
151
+ "keras_model_used": True,
152
+ "masks_shape": str(masks.shape),
153
+ "max_confidence": float(np.max(masks))
154
+ })
155
+ except Exception as e:
156
+ seg_result["error"] = str(e)
157
+ else:
158
+ seg_result["dummy_mode"] = True
159
+
160
+ seg_result.update({
161
+ "timestamp": datetime.now().isoformat(),
162
+ "detections": [{"type": "malware", "confidence": 0.92}]
163
+ })
164
+
165
+ with open(seg_path, "w") as f:
166
+ json.dump(seg_result, f, indent=2)
167
+
168
+ # Update history
169
+ for item in request_history:
170
+ if item["request_id"] == request_id:
171
+ item["segmentation_path"] = str(seg_path)
172
+ item["status"] = "segmented"
173
+ break
174
+
175
+ # ========================================
176
+ # HELPER FUNCTIONS
177
+ # ========================================
178
+ def is_valid_image(file_bytes: bytes) -> bool:
179
+ try:
180
+ Image.open(io.BytesIO(file_bytes)).verify()
181
+ return True
182
+ except:
183
+ return False
184
+
185
+ def save_image(file_bytes: bytes, filename: str) -> str:
186
+ image_path = IMAGES_DIR / filename
187
+ with open(image_path, "wb") as f:
188
+ f.write(file_bytes)
189
+ return str(image_path)
190
+
191
+ # ========================================
192
+ # API ROUTES
193
+ # ========================================
194
+ @app.post("/models/classification/load", response_model=ModelLoadResponse)
195
+ async def load_classification_model(model_file: UploadFile = File(...)):
196
+ """📤 UPLOAD CLASSIFICATION MODEL (.keras ONLY)"""
197
+ if not model_file.filename.endswith('.keras'):
198
+ raise HTTPException(400, "❌ Classification model must be .keras")
199
+
200
+ model_bytes = await model_file.read()
201
+ model_filename = f"class_{uuid.uuid4()}.keras"
202
+ model_path = save_classification_model(model_bytes, model_filename)
203
+
204
+ if load_keras_model(model_path, "classification"):
205
+ return ModelLoadResponse(
206
+ status="success", type="classification",
207
+ path=model_path,
208
+ message="✅ Classification model ready! Use /predict"
209
+ )
210
+ raise HTTPException(500, "Failed to load classification model")
211
+
212
+ @app.post("/models/segmentation/load", response_model=ModelLoadResponse)
213
+ async def load_segmentation_model(model_file: UploadFile = File(...)):
214
+ """🔍 UPLOAD SEGMENTATION MODEL (.keras ONLY)"""
215
+ if not model_file.filename.endswith('.keras'):
216
+ raise HTTPException(400, "❌ Segmentation model must be .keras")
217
+
218
+ model_bytes = await model_file.read()
219
+ model_filename = f"seg_{uuid.uuid4()}.keras"
220
+ model_path = save_segmentation_model(model_bytes, model_filename)
221
+
222
+ if load_keras_model(model_path, "segmentation"):
223
+ return ModelLoadResponse(
224
+ status="success", type="segmentation",
225
+ path=model_path,
226
+ message="✅ Segmentation model ready for malicious images"
227
+ )
228
+ raise HTTPException(500, "Failed to load segmentation model")
229
+
230
+ @app.get("/models/status")
231
+ async def models_status():
232
+ """📊 MODEL STATUS"""
233
+ return {
234
+ "classification_loaded": classification_model is not None,
235
+ "segmentation_loaded": segmentation_model is not None,
236
+ "paths": {
237
+ "classification": str(CLASSIFICATION_MODELS_DIR),
238
+ "segmentation": str(SEGMENTATION_MODELS_DIR),
239
+ "images": str(IMAGES_DIR),
240
+ "segments": str(SEGMENTS_DIR)
241
+ },
242
+ "predict_ready": classification_model is not None
243
+ }
244
+
245
+ @app.post("/predict", response_model=PredictionResponse)
246
+ async def predict_image(
247
+ background_tasks: BackgroundTasks,
248
+ file: UploadFile = File(...),
249
+ title: Optional[str] = "Untitled"
250
+ ):
251
+ """🎯 CLASSIFY IMAGE (Requires classification model)"""
252
+
253
+ # Validation
254
+ file_bytes = await file.read()
255
+ if len(file_bytes) > 10 * 1024 * 1024:
256
+ raise HTTPException(400, "File too large (10MB max)")
257
+ if not is_valid_image(file_bytes):
258
+ raise HTTPException(400, "Invalid image")
259
+
260
+ # Process
261
+ request_id = str(uuid.uuid4())
262
+ image_filename = f"{uuid.uuid4()}.jpg"
263
+ image_path = save_image(file_bytes, image_filename)
264
+
265
+ # Keras prediction
266
+ prediction, confidence = predict_with_keras(file_bytes)
267
+ model_version = getattr(classification_model, 'name', 'unknown')
268
+
269
+ # Save to history
270
+ history_item = {
271
+ "request_id": request_id, "filename": file.filename or image_filename,
272
+ "image_path": image_path, "prediction": prediction,
273
+ "confidence": confidence, "model_version": model_version,
274
+ "timestamp": datetime.now().isoformat(), "status": "classified",
275
+ "segmentation_path": None
276
+ }
277
+ request_history.append(history_item)
278
+
279
+ # Async segmentation for malicious
280
+ if prediction == "malicious":
281
+ background_tasks.add_task(run_segmentation, request_id, image_filename, image_path)
282
+
283
+ return PredictionResponse(**history_item)
284
+
285
+ @app.get("/history", response_model=List[HistoryItem])
286
+ async def get_history():
287
+ return [HistoryItem(**item) for item in request_history]
288
+
289
+ @app.get("/history/{request_id}")
290
+ async def get_prediction(request_id: str):
291
+ for item in request_history:
292
+ if item["request_id"] == request_id:
293
+ return HistoryItem(**item)
294
+ raise HTTPException(404, "Not found")
295
+
296
+ @app.get("/")
297
+ async def root():
298
+ class_ready = "✅ READY" if classification_model else "❌ REQUIRED"
299
+ seg_ready = "✅ READY" if segmentation_model else "⚠️ OPTIONAL"
300
+ return {
301
+ "🚀 Dual Keras Model API",
302
+ f"📤 Classification: {class_ready}",
303
+ f"🔍 Segmentation: {seg_ready}",
304
+ "📁 Upload .keras models to:",
305
+ f" → POST /models/classification/load",
306
+ f" → POST /models/segmentation/load",
307
+ "📊 Total predictions:", len(request_history)
308
+ }
309
+
310
+ @app.get("/health")
311
+ async def health():
312
+ return {"status": "healthy", "predict_ready": classification_model is not None}
313
+
314
+ if __name__ == "__main__":
315
+ import uvicorn
316
+ uvicorn.run(app, host="0.0.0.0", port=8000)
mini_API/.ipynb_checkpoints/test-checkpoint.py ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
 
1
+ import mlflow
2
+
3
+ mlflow.set_tracking_uri("http://127.0.0.1:5000")
4
+
5
+ model = mlflow.pyfunc.load_model("models:/skin_cancer_classifier/1")
6
+
7
+ print("Model loaded successfully")
mini_API/test.py ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
 
1
+ import mlflow
2
+
3
+ mlflow.set_tracking_uri("http://127.0.0.1:5000")
4
+
5
+ model = mlflow.pyfunc.load_model("models:/skin_cancer_classifier/1")
6
+
7
+ print("Model loaded successfully")
requirements.txt ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Core ML and Data Processing
2
+ numpy==1.26.4
3
+ tensorflow==2.21.0
4
+ scikit-learn>=1.0.0
5
+
6
+ # Image Processing
7
+ Pillow>=10.0.0
8
+
9
+ # API Framework
10
+ fastapi>=0.104.0
11
+ uvicorn>=0.24.0
12
+ pydantic>=2.0.0
13
+
14
+ # MLflow for Model Registry
15
+ mlflow
16
+ cloudpickle==3.0.0
17
+
18
+ # Async Support
19
+ asyncio-contextmanager>=1.0.0
20
+
21
+ # Utility Libraries
22
+ python-multipart>=0.0.6