-
Notifications
You must be signed in to change notification settings - Fork 2
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Creation of services in OSCAR #7
base: main
Are you sure you want to change the base?
Changes from 1 commit
8646a4a
113f292
c13c88f
175fb66
d9ea169
a906210
29c067c
1ca2763
90a4689
802091e
1ca9891
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,4 @@ | ||
#multi-platform execution of services | ||
.ONESHELL: | ||
SHELL := /bin/bash | ||
|
||
|
@@ -28,7 +29,9 @@ build-push-dimensionality-reduction: | |
docker buildx build --platform linux/amd64,linux/arm64 -t ifcacomputing/dimensionality-reduction-api --push dimensionality_reduction_api | ||
|
||
build-push-model-inference: | ||
docker buildx build --platform linux/amd64,linux/arm64 -t ifcacomputing/model-inference-api:latest --push model_inference_api | ||
docker build -t ghcr.io/grycap/mls-arm-api model_inference_api | ||
docker push ghcr.io/grycap/mls-arm-api | ||
docker buildx build --platform linux/amd64,linux/arm64 -t ghcr.io/grycap/mls-arm-api --push model_inference_api | ||
|
||
run-data-drift-detector: | ||
docker run --name data-drift-detection -p 5001:8000 ifcacomputing/data-drift-detection-api | ||
|
@@ -37,4 +40,24 @@ run-dimensionality-reduction: | |
docker run --name dimensionality-reduction -p 5002:8000 ifcacomputing/dimensionality-reduction-api | ||
|
||
run-model-inference: | ||
docker run --name model-inference -p 5003:8000 ifcacomputing/model-inference-api | ||
docker run --name model-inference -p 5003:8000 ifcacomputing/model-inference-api | ||
|
||
mls: | ||
docker build -t ghcr.io/grycap/mls-arm-api model_inference_api | ||
docker push ghcr.io/grycap/mls-arm-api | ||
docker buildx build --platform linux/amd64,linux/arm64 -t ghcr.io/grycap/mls-arm-api --push model_inference_api | ||
|
||
dds: | ||
docker build -t ghcr.io/grycap/dds-arm-api detector_api | ||
docker push ghcr.io/grycap/dds-arm-api | ||
docker buildx build --platform linux/amd64,linux/arm64 -t ghcr.io/grycap/dds-arm-api --push detector_api | ||
|
||
emc: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @vicente87 I think that this should be the unique "new" service. The addition of the others (mls, dds and drs) is completely redundant. Because they are already hosted in ifcacomputing/XXXX-api. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @jaime-cespedes-sisniega The problem is that in order to make the asynchronous invocations we create main-service.py (it's from what was put in main.py) and there we introduce small changes to be able to orchestrate the services well among others. For example we create output files in the drs services in json format so that you can use it well. Also, the input data that comes from the MinIO buckets has to accommodate the functions that you have created and that is why we decided to create the new images. I don't know if I will explain well. |
||
docker build -t ghcr.io/grycap/emc-arm-api embedding_matrix | ||
docker push ghcr.io/grycap/emc-arm-api | ||
docker buildx build --platform linux/amd64,linux/arm64 -t ghcr.io/grycap/emc-arm-api --push embedding_matrix | ||
drs: | ||
docker build -t ghcr.io/grycap/drs-arm-api dimensionality_reduction_api | ||
docker push ghcr.io/grycap/drs-arm-api | ||
docker buildx build --platform linux/amd64,linux/arm64 -t ghcr.io/grycap/drs-arm-api --push dimensionality_reduction_api | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @vicente87 This file is empty. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @jaime-cespedes-sisniega You are right, this file does not have to be there. |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
functions: | ||
oscar: | ||
- oscar-cluster: | ||
name: mls-service | ||
memory: 2Gi | ||
cpu: '2' | ||
image: ghcr.io/grycap/mls-arm-api | ||
script: script.sh | ||
log_level: INFO | ||
input: | ||
- storage_provider: minio.default | ||
path: images/input | ||
output: | ||
- suffix: | ||
- txt | ||
storage_provider: minio.default | ||
path: mls/output | ||
- suffix: | ||
- png | ||
storage_provider: minio.minio1 | ||
path: drs/input | ||
|
||
storage_providers: | ||
minio: | ||
minio1: | ||
endpoint: 'https://minio.frosty-grothendieck5.im.grycap.net' | ||
region: us-east-1 | ||
access_key: minio | ||
secret_key: minio123 | ||
verify: false |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
FILE_NAME=`basename "$INPUT_FILE_PATH"` | ||
OUTPUT_FILE="$TMP_OUTPUT_DIR/$FILE_NAME" | ||
cp $INPUT_FILE_PATH $OUTPUT_FILE | ||
python3 main-service.py $INPUT_FILE_PATH $OUTPUT_FILE |
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @vicente87 This file is empty. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @jaime-cespedes-sisniega You are right, this file does not have to be there. |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
functions: | ||
oscar: | ||
- oscar-cluster1: | ||
name: drs-service | ||
memory: 2Gi | ||
cpu: '2' | ||
image: ghcr.io/grycap/drs-arm-api | ||
script: script.sh | ||
log_level: INFO | ||
input: | ||
- storage_provider: minio | ||
path: drs/input | ||
output: | ||
- storage_provider: minio | ||
path: drs/output | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
FILE_NAME=`basename "$INPUT_FILE_PATH"` | ||
OUTPUT_FILE="$TMP_OUTPUT_DIR/$FILE_NAME" | ||
|
||
python3 main-service.py $INPUT_FILE_PATH $OUTPUT_FILE |
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @vicente87 This file is empty. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @jaime-cespedes-sisniega You are right, this file does not have to be there. |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
functions: | ||
oscar: | ||
- oscar-cluster1: | ||
name: dds-service | ||
memory: 2Gi | ||
cpu: '2' | ||
image: ghcr.io/grycap/dds-arm-api | ||
script: script.sh | ||
log_level: INFO | ||
input: | ||
- storage_provider: minio | ||
path: emc/output | ||
output: | ||
- storage_provider: minio.minio-rasp | ||
path: dds/output | ||
- storage_provider: minio.default | ||
path: dds/output | ||
storage_providers: | ||
minio: | ||
minio-rasp: | ||
endpoint: 'https://minio.graspi.im.grycap.net' | ||
region: us-east-1 | ||
access_key: minio | ||
secret_key: minio-aisprint-2021 | ||
verify: false |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
FILE_NAME=`basename "$INPUT_FILE_PATH"` | ||
OUTPUT_FILE="$TMP_OUTPUT_DIR/$FILE_NAME" | ||
python3 main-service.py $INPUT_FILE_PATH $OUTPUT_FILE |
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @vicente87 This file is empty. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @jaime-cespedes-sisniega You are right, this file does not have to be there. |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
functions: | ||
oscar: | ||
- oscar-cluster1: | ||
name: emc-service | ||
memory: 2Gi | ||
cpu: '2' | ||
image: ghcr.io/grycap/emc-arm-api | ||
script: script.sh | ||
log_level: INFO | ||
input: | ||
- storage_provider: minio | ||
path: drs/output | ||
output: | ||
- storage_provider: minio | ||
path: emc/output |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
FILE_NAME=`basename "$INPUT_FILE_PATH"` | ||
OUTPUT_FILE="$TMP_OUTPUT_DIR/$FILE_NAME" | ||
python3 main-service.py $INPUT_FILE_PATH $OUTPUT_FILE |
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @vicente87 This file must not be modified. It is generated by the |
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @vicente87 Why do you create this There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @jaime-cespedes-sisniega The main-service.py is the modification of the main.py created by you, where the functions created have been reused to accommodate them to the invocation of MinIO services. Here output files are created so that they can be used by subsequent services and then conform to the functions created by you. But basically what the main-service.py (of the MLIS, DRS and DDS services) have is to accommodate the inputs and outputs of the files left in the MinIO buckets to be used by the previously created functions. Which perhaps could have been called main.py to follow the same structure that you created. |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,68 @@ | ||
"""API module.""" | ||
# main-service.py for execute DDS (Drift Detection Service) service. | ||
|
||
import logging | ||
import sys | ||
import json | ||
import numpy as np | ||
from detector import Detector | ||
from litestar import Router, get, post | ||
from litestar.status_codes import ( | ||
HTTP_200_OK, | ||
) | ||
from schemas import ( | ||
DetectorInputData, | ||
DistanceBasedResponse, | ||
HealthResponse, | ||
) | ||
from settings import api_settings, detector_settings | ||
|
||
detector = Detector( | ||
settings=detector_settings, | ||
) | ||
|
||
class Variable: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What is this There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @jaime-cespedes-sisniega That was a mistake that stuck with us. |
||
def __init__(self, d1,d2,d3): | ||
self.alpha = d1 | ||
self.values = d2 | ||
self.return_input_values=d3 | ||
|
||
|
||
def check_drift(data: DetectorInputData,out) -> DistanceBasedResponse: | ||
#async | ||
"""Check if drift is present. | ||
|
||
:param data: input data | ||
:type data: DetectorInputData | ||
:return: drift data information | ||
:rtype: BaseCheckDriftResponse | ||
""" | ||
|
||
|
||
logging.info("Checking drift...") | ||
check_drift_result = detector.check_drift( | ||
#The value parameter has to be in an array with the N embedding | ||
values=np.array(data['values']), | ||
alpha=data['alpha'], | ||
) | ||
return_input_values=data['return_input_values'] | ||
value=np.array(data['values']) | ||
check_drift_result["distance"] = check_drift_result["distance"].distance | ||
if return_input_values: | ||
check_drift_result["values"] = value.tolist() # noqa: PD011 | ||
result = DistanceBasedResponse( | ||
**check_drift_result, | ||
) | ||
#Save the results of the algorithm in the output bucket (dds/output) | ||
f = open(str(out)+".txt", "a") | ||
f.write(str(result)) | ||
f.close() | ||
|
||
#Read json object with data from EMC service | ||
with open(sys.argv[1]) as f: | ||
data = json.load(f) | ||
print(data) | ||
|
||
#Pass parameters (data and output bucket) to the drift detection algorithm | ||
check_drift(data,sys.argv[2]) | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -10,9 +10,10 @@ RUN apt-get -y update && \ | |
adduser --no-create-home --shell /bin/false --disabled-password --uid 1001 --system --group app && \ | ||
pip install --upgrade -r requirements.txt --no-cache-dir && \ | ||
rm requirements.txt | ||
RUN pip install Pillow | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @vicente87 Why do you need There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @jaime-cespedes-sisniega Here that stuck to us because we were using it to manipulate images and later we saw that we could solve it with PIL. |
||
|
||
COPY app ./ | ||
|
||
USER app | ||
|
||
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000", "--workers", "1"] | ||
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000", "--workers", "1"] |
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @vicente87 Why do you create this There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @jaime-cespedes-sisniega The main-service.py is the modification of the main.py created by you, where the functions created have been reused to accommodate them to the invocation of MinIO services. Here output files are created so that they can be used by subsequent services and then conform to the functions created by you. But basically what the main-service.py (of the MLIS, DRS and DDS services) have is to accommodate the inputs and outputs of the files left in the MinIO buckets to be used by the previously created functions. Which perhaps could have been called main.py to follow the same structure that you created. |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
# main-service.py for execute DRS (Demensionality Reduction Service) service. | ||
|
||
import logging | ||
import sys | ||
import numpy as np | ||
from PIL import Image | ||
from litestar import Router, get, post | ||
from litestar.enums import RequestEncodingType | ||
from litestar.params import Body | ||
from litestar.status_codes import ( | ||
HTTP_200_OK, | ||
) | ||
|
||
from dr import DimensionalityReduction | ||
from schemas import ( | ||
DimensionalityReductionInputData, | ||
DimensionalityReductionResponse, | ||
HealthResponse, | ||
) | ||
from settings import ( | ||
api_settings, | ||
encoder_settings, | ||
transformer_settings, | ||
) | ||
|
||
dr = DimensionalityReduction( | ||
settings_encoder=encoder_settings, | ||
settings_transformer=transformer_settings, | ||
) | ||
|
||
def dim_red(image, out): | ||
logging.info("Transforming image...") | ||
transformed_image = dr.transform( | ||
data=image, | ||
) | ||
logging.info("Image transformed.") | ||
logging.info("Encoding image...") | ||
reduced_image = dr.encode( | ||
data=transformed_image, | ||
) | ||
logging.info("Image encoded.") | ||
|
||
#Write algorithm results to output bucket | ||
f = open(str(out)+".json", "a") | ||
f.write(str(np.squeeze(reduced_image).tolist())) | ||
f.close() | ||
|
||
#Capture the image from the input bucket (upload/input) and pass as parameters to the dimensionality | ||
#reduction function the content of the image and the output bucket to put the results of the algorithm | ||
img = Image.open(sys.argv[1]) | ||
dim_red(img,sys.argv[2]) |
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @vicente87 This file must not be modified. It is generated by the |
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @vicente87 This file must not be modified. It is generated by the |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
FROM python:3.10-slim | ||
LABEL author="Vicente Rodriguez Benitez <[email protected]>" | ||
|
||
WORKDIR /app | ||
|
||
COPY requirements/requirements.txt . | ||
|
||
RUN apt-get -y update && \ | ||
addgroup --gid 1001 --system app && \ | ||
adduser --no-create-home --shell /bin/false --disabled-password --uid 1001 --system --group app && \ | ||
pip install --upgrade -r requirements.txt --no-cache-dir && \ | ||
rm requirements.txt | ||
|
||
|
||
|
||
COPY app ./ | ||
|
||
USER app | ||
|
||
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000", "--workers", "4"] |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
"""Detector API __init__.""" | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @vicente87 It is not the Detector API. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @jaime-cespedes-sisniega That was a mistake that stuck with us but it has already been revised |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
"""App __init__.""" |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,85 @@ | ||
"""API module.""" | ||
|
||
import logging | ||
|
||
from litestar import Router, get, post | ||
from litestar.enums import RequestEncodingType | ||
from litestar.params import Body | ||
from litestar.status_codes import ( | ||
HTTP_200_OK, | ||
) | ||
|
||
from model import Model | ||
from schemas import ( | ||
HealthResponse, | ||
ModelInputData, | ||
PredictResponse, | ||
) | ||
from settings import ( | ||
api_settings, | ||
model_settings, | ||
transformer_settings, | ||
) | ||
|
||
model = Model( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @vicente87 Why do you use the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @jaime-cespedes-sisniega Here we do not use it but as I told you before that we had taken the pattern of services that you gave us and we did not modify many things and that is why they may not be used |
||
settings_model=model_settings, | ||
settings_transformer=transformer_settings, | ||
) | ||
|
||
|
||
@post( | ||
path="/predict", | ||
status_code=HTTP_200_OK, | ||
) | ||
async def predict( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @vicente87 Same here. Embedding matrix should not perform any prediction. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @jaime-cespedes-sisniega Here we do not use it but as I told you before that we had taken the pattern of services that you gave us and we did not modify many things and that is why they may not be used |
||
data: ModelInputData = Body( | ||
media_type=RequestEncodingType.MULTI_PART, | ||
), | ||
) -> PredictResponse: | ||
"""Predict function. | ||
|
||
:param data: model input data | ||
:type data: ModelInputData | ||
:return: prediction response | ||
:rtype: BasePredictionResponse | ||
""" | ||
image = await data.image | ||
logging.info("Transforming image...") | ||
transformed_image = model.transform( | ||
data=image, | ||
) | ||
logging.info("Image transformed.") | ||
logging.info("Predicting...") | ||
prediction = model.predict( | ||
data=transformed_image, | ||
) | ||
logging.info("Prediction made.") | ||
return PredictResponse( | ||
**prediction, | ||
) | ||
|
||
|
||
@get( | ||
path="/health", | ||
status_code=HTTP_200_OK, | ||
) | ||
async def health() -> HealthResponse: | ||
"""Health check function. | ||
|
||
:return: Health check response | ||
:rtype: HealthResponse | ||
""" | ||
return HealthResponse( | ||
name=api_settings.PROJECT_NAME, | ||
api_version=api_settings.VERSION, | ||
) | ||
|
||
|
||
api_router = Router( | ||
path="/api", | ||
tags=["API"], | ||
route_handlers=[ | ||
predict, | ||
health, | ||
], | ||
) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@vicente87 Why do you change the registry where this image is stored?