Skip to content

Commit

Permalink
Merge pull request #476 from dba/develop
Browse files Browse the repository at this point in the history
Release 2.8.0
  • Loading branch information
Ke LI authored and GitHub Enterprise committed Jan 19, 2023
2 parents 1b77bea + 2acc436 commit 327e01c
Show file tree
Hide file tree
Showing 33 changed files with 1,052 additions and 36 deletions.
10 changes: 8 additions & 2 deletions .whitesource
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,11 @@
"patch"
],
"automerge": true
}]
}
}],
"scanSettings": {
"baseBranches": ["develop"],
"configMode": "LOCAL",
"configExternalURL": "",
"projectToken": ""
}
}
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# Open Prediction Service HUB

## 2.8.0 _2022/11/28_

* [DBACLD-47417] Extend OPS to support downloading of Ruleset Models

## 2.7.1 _2022/09/15_

* [DBACLD-58320] Fix ads ml service rule import
Expand Down
66 changes: 64 additions & 2 deletions open-prediction-service.yaml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
openapi: 3.0.3
info:
title: Open Prediction Service
version: 2.7.4-SNAPSHOT
version: 2.8.0-SNAPSHOT
description: The Open Prediction Service API is an effort to provide an Open API that enables unsupported native ML Providers in Decision Designer or Decision Runtime.
tags:
- name: info
Expand Down Expand Up @@ -213,6 +213,47 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/Error'
/models/{model_id}/download:
get:
tags:
- discover
summary: Download model binary
operationId: get_binary_by_id
parameters:
- $ref: '#/components/parameters/ModelIDParam'
responses:
'200':
description: Successful Response
content:
# A binary file:
application/octet-stream: {}
default:
description: Error
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
/models/{model_id}/metadata:
get:
tags:
- discover
summary: Get parsed model metadata
operationId: get_metadata_by_id
parameters:
- $ref: '#/components/parameters/ModelIDParam'
responses:
'200':
description: Successful Response
content:
application/json:
schema:
$ref: '#/components/schemas/AdditionalModelInfo'
default:
description: Error
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
/endpoints:
get:
tags:
Expand Down Expand Up @@ -823,13 +864,15 @@ components:
unknown_file_size:
type: boolean
default: false
description: Whether the user can upload a file whose length is unknown at the time of the request.
description: Whether the user can upload a file whose length(Content-Length in HTTP header) is unknown at the time of the request.
example:
capabilities:
- info
- discover
- manage
- prediction
- download
- metadata
managed_capabilities:
supported_input_data_structure:
- "auto"
Expand All @@ -854,6 +897,10 @@ components:
- discover
- manage
- run
# Download model binary
- download
# Inspect model specific metadata, for example for PMML model return subType (Scorecard, Ruleset)
- metadata
Parameter:
title: Parameter
description: Parameter for ml model invocation
Expand Down Expand Up @@ -907,6 +954,21 @@ components:
target:
- rel: endpoint
href: 'http://open-prediction-service.org/endpoints/8c2af534-cdce-11ea-87d0-0242ac130003'
AdditionalModelInfo:
type: object
title: Additional Model Information
required:
- modelPackage
- modelType
properties:
modelPackage:
type: string
description: The file format of binary model
example: pmml
modelType:
type: string
description: Model type of binary model
example: RuleSetModel
parameters:
ModelIDParam:
in: path
Expand Down
4 changes: 2 additions & 2 deletions ops-client-sdk/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

<groupId>com.ibm.decision</groupId>
<artifactId>ops-client-sdk</artifactId>
<version>2.7.4-SNAPSHOT</version>
<version>2.8.0-SNAPSHOT</version>
<inceptionYear>2020</inceptionYear>
<build>
<plugins>
Expand Down Expand Up @@ -224,7 +224,7 @@ limitations under the License.IBM Confidential]]>
<maven.compiler.source>${java.version}</maven.compiler.source>
<maven.compiler.target>${java.version}</maven.compiler.target>
<gson-fire-version>1.8.5</gson-fire-version>
<swagger-core-version>1.6.8</swagger-core-version>
<swagger-core-version>1.6.9</swagger-core-version>
<okhttp-version>4.10.0</okhttp-version>
<gson-version>2.10</gson-version>
<commons-lang3-version>3.12.0</commons-lang3-version>
Expand Down
6 changes: 4 additions & 2 deletions ops-implementations/ads-ml-service/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
FROM --platform=${BUILDPLATFORM} python:3.8-slim AS python-base
FROM --platform=${BUILDPLATFORM} python:3.11-slim AS python-base

ENV \
OPS_HOME="/usr/src/ads-ml-service" \
Expand All @@ -15,6 +15,7 @@ FROM python-base AS codegen-image

COPY app/gen/. app/gen/
RUN \
apt-get -q update && apt-get -q install --assume-yes build-essential && \
python3 -m pip install -q --no-cache-dir --upgrade pip && \
python3 -m pip install -q datamodel-code-generator~=0.11.19 && \
datamodel-codegen \
Expand All @@ -30,8 +31,9 @@ COPY requirements.txt ./
COPY logging.yaml /etc/ads-ml-service/logging/logging.yaml
RUN \
python3 -m venv /usr/src/ads-ml-service/venv && \
apt-get -q update && apt-get -q install --assume-yes build-essential libpq-dev python3-numpy && \
python3 -m pip install -q --no-cache-dir --upgrade pip && \
python3 -m pip install -q --no-cache-dir --requirement requirements.txt && \
python3 -m pip install -q --no-cache-dir --prefer-binary --requirement requirements.txt && \
mkdir -p /var/log/ads-ml-service && \
mkdir -p /var/lib/ads-ml-service/models

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,9 @@ async def server_capabilities() -> typing.Dict[typing.Text, typing.Any]:
ops_model.Capability.info,
ops_model.Capability.discover,
ops_model.Capability.manage,
ops_model.Capability.run
ops_model.Capability.run,
ops_model.Capability.download,
ops_model.Capability.metadata
],
'managed_capabilities': {
'supported_input_data_structure': ['auto', 'DataFrame', 'ndarray', 'DMatrix', 'list'],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@

import logging
import typing
import io

import fastapi
import fastapi.encoders as encoders
Expand All @@ -28,10 +29,12 @@
import app.crud as crud
import app.gen.schemas.ops_schemas as ops_schemas
import app.runtime.model_upload as app_model_upload
import app.runtime.inspection as app_runtime_inspection
import app.schemas as schemas
import app.schemas.binary_config as app_binary_config
import app.schemas.impl as impl
import app.core.configuration as app_conf
import app.models as models

router = fastapi.APIRouter()
LOGGER = logging.getLogger(__name__)
Expand Down Expand Up @@ -182,3 +185,76 @@ async def add_binary(
model_id=model_id
)
return impl.EndpointImpl.from_database(crud.endpoint.get(db, id=m))


@router.get(
path='/models/{model_id}/metadata',
response_model=ops_schemas.AdditionalModelInfo,
tags=['discover'])
def get_model_metadata(
model_id: int,
db: saorm.Session = fastapi.Depends(deps.get_db)):
LOGGER.info('Retrieving model metadata for id: %s', model_id)
match crud.binary_ml_model.get(db=db, id=model_id):
case None:
raise fastapi.HTTPException(
status_code=status.HTTP_404_NOT_FOUND, detail=f'Model with id {model_id} is not found')
case models.BinaryMlModel(format=app_binary_config.ModelWrapper.PICKLE | app_binary_config.ModelWrapper.JOBLIB as format):
return ops_schemas.AdditionalModelInfo(
modelPackage=format.value,
modelType='other')
case models.BinaryMlModel(format=app_binary_config.ModelWrapper.PMML, model_b64=binary):
match app_runtime_inspection.inspect_pmml_subtype(binary):
case 'Scorecard' | 'RuleSetModel' as typ:
return ops_schemas.AdditionalModelInfo(
modelPackage='pmml',
modelType=typ)
case _:
return ops_schemas.AdditionalModelInfo(
modelPackage='pmml',
modelType='other')
case _:
return ops_schemas.AdditionalModelInfo(
modelPackage='other',
modelType='other')


@router.get(
path='/models/{model_id}/download',
response_class=responses.StreamingResponse,
tags=['discover'])
def get_model_binary(
model_id: int,
db: saorm.Session = fastapi.Depends(deps.get_db)):
LOGGER.info('Retrieving model binary for id: %s', model_id)

model = crud.model.get(db=db, id=model_id)

if model is None:
raise fastapi.HTTPException(
status_code=status.HTTP_404_NOT_FOUND, detail=f'Model with id {model_id} is not found')

filename = model.config.configuration['name']

try:
binary = model.endpoint.binary
except AttributeError:
raise fastapi.HTTPException(
status_code=status.HTTP_404_NOT_FOUND, detail=f'Model binary with id {model_id} is not found')

if binary.format == app_binary_config.ModelWrapper.PMML:
file_extension = 'pmml'
elif binary.format == app_binary_config.ModelWrapper.PICKLE:
file_extension = 'pickle'
elif binary.format == app_binary_config.ModelWrapper.JOBLIB:
file_extension = 'joblib'
else:
file_extension = 'bin'

LOGGER.info('Downloading model binary with name %s and format %s', filename, file_extension)

return responses.StreamingResponse(
content=io.BytesIO(binary.model_b64),
media_type='application/octet-stream',
headers={'Content-Disposition': 'attachment; filename="{basename}.{extension}"'.format(
basename=filename, extension=file_extension)})
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ async def upload(
model_binary,
input_data_structure,
output_data_structure,
format_,
file_format,
name=model_name
)

Expand Down
6 changes: 5 additions & 1 deletion ops-implementations/ads-ml-service/app/db/session.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,11 @@ def get_db_opts() -> typing.Dict[str, typing.Any]:
return opt


def get_engine():
return create_engine(get_db_url(), **get_db_opts())


SessionLocal = sessionmaker(
autocommit=False,
autoflush=False,
bind=create_engine(get_db_url(), **get_db_opts()))
bind=get_engine())
Loading

0 comments on commit 327e01c

Please sign in to comment.