From 5b441282fc9676b05d926abefa164c837972c569 Mon Sep 17 00:00:00 2001 From: Asep Haryana Saputra <90584806+MythEclipse@users.noreply.github.com> Date: Fri, 22 May 2026 19:14:56 +0000 Subject: [PATCH] docs: add ML service design spec Document the agreed FastAPI inference service boundary before implementation planning. Co-Authored-By: Claude Opus 4.7 --- .../specs/2026-05-22-ml-service-design.md | 84 +++++++++++++++++++ 1 file changed, 84 insertions(+) create mode 100644 docs/superpowers/specs/2026-05-22-ml-service-design.md diff --git a/docs/superpowers/specs/2026-05-22-ml-service-design.md b/docs/superpowers/specs/2026-05-22-ml-service-design.md new file mode 100644 index 0000000..9cdd6f9 --- /dev/null +++ b/docs/superpowers/specs/2026-05-22-ml-service-design.md @@ -0,0 +1,84 @@ +# ML Service Design + +## Summary + +Add a separate Python FastAPI inference service at `apps/ml-service`. The existing `Machine_Learning` directory remains responsible for model training, preprocessing, and export artifacts. The new service is responsible only for serving image classification predictions from the trained Keras model. + +## Goals + +- Serve corn leaf disease predictions through a small HTTP API. +- Keep TensorFlow/Keras runtime dependencies out of the Bun/Elysia API app. +- Keep the capstone deployment simple and easy to debug. +- Preserve `Machine_Learning` as the source for model artifacts and training workflow. + +## Non-goals + +- Do not introduce TensorFlow Serving. +- Do not switch to TFLite for the first implementation. +- Do not move the ML training/export pipeline into `apps/ml-service`. +- Do not add frontend UI changes in this scope. + +## Architecture + +`apps/ml-service` will be a standalone FastAPI application. It will load a Keras model from a configurable path, defaulting to the trained model artifact under `Machine_Learning/best_model/best_model.keras`. + +The service exposes three endpoints: + +- `GET /health` returns service health and whether the model is loaded. +- `GET /metadata` returns model/service metadata such as labels, input size, configured model path, and model loaded status. +- `POST /predict` accepts one uploaded image and returns the predicted Indonesian disease label, confidence, and class probabilities. + +The existing TypeScript backend can later call this ML service over HTTP. That integration is outside this first service-scaffolding scope unless explicitly requested after the service exists. + +## Components + +- `apps/ml-service/main.py` creates the FastAPI app and defines routes. +- `apps/ml-service/model.py` owns model loading, image preprocessing, and prediction. +- `apps/ml-service/schemas.py` defines response models for health, metadata, and prediction responses. +- `apps/ml-service/requirements.txt` lists Python runtime dependencies: FastAPI, Uvicorn, TensorFlow, Pillow, and multipart upload support. +- `apps/ml-service/.env.example` documents runtime configuration such as `MODEL_PATH`, `MODEL_INPUT_SIZE`, and port. + +## Runtime configuration + +- `MODEL_PATH` controls the Keras model path. Default: `../../Machine_Learning/best_model/best_model.keras` relative to `apps/ml-service`. +- `MODEL_INPUT_SIZE` controls image resize dimensions. Default should match the trained EfficientNetV2B0 pipeline input size. +- `ML_SERVICE_HOST` and `ML_SERVICE_PORT` document how to run the service locally. + +## Data flow for `POST /predict` + +1. Client uploads an image as multipart form data. +2. Service validates that the upload is an image. +3. Service opens the image with Pillow, converts it to RGB, and resizes it to the configured input size. +4. Image pixels are converted to a batch tensor using the same normalization expected by the Keras model. +5. The loaded model returns class probabilities for the four labels. +6. Service returns JSON containing the top label, confidence, and probability per label. + +## Labels + +The model predicts the existing four Indonesian labels: + +- `Bercak Daun` +- `Hawar Daun` +- `Karat Daun` +- `Daun Sehat` + +The service must preserve this label order consistently with the trained model output. + +## Error handling + +- If the model cannot be loaded, `GET /health` returns a successful response with `model_loaded: false`. +- If the model is not loaded, `POST /predict` returns `503 Service Unavailable`. +- If the uploaded file is not an image or cannot be decoded, `POST /predict` returns `400 Bad Request`. +- If inference fails unexpectedly, `POST /predict` returns `500 Internal Server Error` with a generic message. +- `GET /metadata` still returns static metadata even when the model is not loaded, including `model_loaded` status. + +## Verification + +Manual verification for the initial implementation: + +- Start the service with Uvicorn from `apps/ml-service`. +- Call `GET /health` and confirm the response includes service status and model loaded status. +- Call `GET /metadata` and confirm labels, input size, model path, and model loaded status are present. +- Call `POST /predict` with a real image file when an example corn leaf image is available. + +The repository does not currently have a Python test suite for this new service. Automated tests can be added later if the service grows beyond the initial capstone scope.