Skip to content

Commit 1242ccd

Browse files
authored
Merge pull request #122 from SAP/enable-inference-with-ext-url
Enable Inference With External URL
2 parents 018da32 + 482c990 commit 1242ccd

File tree

8 files changed

+160
-4
lines changed

8 files changed

+160
-4
lines changed

CHANGELOG.md

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
66

77
## [Unreleased]
88

9+
## [0.10.0]
10+
11+
### Added
12+
13+
* Support for inference using external URL in `create_inference_request_with_url`
14+
15+
[#122]: https://github.com/SAP/data-attribute-recommendation-python-sdk/pull/122
16+
917
## [0.9.2]
1018

1119
### Added
@@ -17,7 +25,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1725

1826
[#116]: https://github.com/SAP/data-attribute-recommendation-python-sdk/pull/116
1927

20-
2128
## [0.9.1]
2229

2330
### Added
@@ -237,7 +244,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
237244

238245
* First public release
239246

240-
[Unreleased]: https://github.com/SAP/data-attribute-recommendation-python-sdk/compare/rel/0.9.1...HEAD
247+
[Unreleased]: https://github.com/SAP/data-attribute-recommendation-python-sdk/compare/rel/0.10.0...HEAD
248+
[0.10.0]: https://github.com/SAP/data-attribute-recommendation-python-sdk/compare/rel/0.9.2...rel/0.10.0
249+
[0.9.2]: https://github.com/SAP/data-attribute-recommendation-python-sdk/compare/rel/0.9.1...rel/0.9.2
241250
[0.9.1]: https://github.com/SAP/data-attribute-recommendation-python-sdk/compare/rel/0.9.0...rel/0.9.1
242251
[0.9.0]: https://github.com/SAP/data-attribute-recommendation-python-sdk/compare/rel/0.8.2...rel/0.9.0
243252
[0.8.2]: https://github.com/SAP/data-attribute-recommendation-python-sdk/compare/rel/0.8.1...rel/0.8.2

sap/aibus/dar/client/dar_session.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,21 @@ def post_data_to_endpoint(
158158
self._check_status_code(response, url)
159159
return response
160160

161+
def post_to_url(self, url: str, payload: dict, retry: bool = False) -> Response:
162+
"""
163+
Performs **POST** request against fully-qualified URL
164+
165+
:param url: a fully-qualified inference URL
166+
:param payload: request body
167+
:param retry: enables retrying a failed request
168+
"""
169+
connection = self.http
170+
if retry:
171+
connection = self.http_post_retry
172+
response = connection.post(url, headers=self._get_headers(), json=payload)
173+
self._check_status_code(response, url)
174+
return response
175+
161176

162177
def _get_requests_version():
163178
requests_version = None

sap/aibus/dar/client/inference_client.py

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,3 +121,35 @@ def do_bulk_inference(
121121
)
122122
result.extend(response["predictions"])
123123
return result
124+
125+
def create_inference_request_with_url(
126+
self,
127+
url: str,
128+
objects: List[dict],
129+
top_n: int = TOP_N,
130+
retry: bool = False,
131+
) -> dict:
132+
"""
133+
Performs inference for the given *objects* against fully-qualified URL.
134+
A complete inference URL can be the passed to the method inference, instead
135+
of constructing URL from using base url and model name
136+
137+
:param url: fully-qualified inference URL
138+
:param objects: Objects to be classified
139+
:param top_n: How many predictions to return per object
140+
:param retry: whether to retry on errors. Default: False
141+
:return: API response
142+
"""
143+
self.log.debug(
144+
"Submitting Inference request with '%s'"
145+
" objects and top_n '%s' to url %s",
146+
len(objects),
147+
top_n,
148+
url,
149+
)
150+
response = self.session.post_to_url(
151+
url, payload={"topN": top_n, "objects": objects}, retry=retry
152+
)
153+
as_json = response.json()
154+
self.log.debug("Inference response ID: %s", as_json["id"])
155+
return as_json

system_tests/workflow/test_end_to_end.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
import logging
22
import uuid
33
from io import BytesIO
4+
import os
45

56
import pytest
67

8+
from sap.aibus.dar.client.inference_constants import InferencePaths
79
from sap.aibus.dar.client.data_manager_client import DataManagerClient
810
from sap.aibus.dar.client.exceptions import DARHTTPException, ModelAlreadyExists
911
from sap.aibus.dar.client.inference_client import InferenceClient
@@ -257,6 +259,18 @@ def _assert_inference_works(self, inference_client, model_name):
257259
)
258260
assert len(response) == 123
259261

262+
url = os.environ["DAR_URL"]
263+
if url[-1] == "/":
264+
url = url[:-1]
265+
url = url + InferencePaths.format_inference_endpoint_by_name(model_name)
266+
response = inference_client.create_inference_request_with_url(
267+
url=url, objects=to_be_classified
268+
)
269+
logger.info("Inference with URL done. API response: %s", response)
270+
print(response)
271+
# One object has been classified
272+
assert len(response["predictions"]) == 1
273+
260274
def _assert_deployment_exists(self, deployment_id, model_manager_client):
261275
# Look at individual resource
262276
read_response = model_manager_client.read_deployment_by_id(deployment_id)

tests/sap/aibus/dar/client/test_dar_session.py

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -217,6 +217,45 @@ def test_post_data_to_endpoint_error_handling(self):
217217
assert exc.url == self.dar_url[:-1] + endpoint
218218
assert exc.response == sess.http.post()
219219

220+
def test_post_to_url(self):
221+
for allowed_status_code in range(200, 300):
222+
sess = self._prepare()
223+
sess.http.post.return_value.status_code = allowed_status_code
224+
225+
url = self.dar_url[:-1] + "/data-manager/api/v3/datasetSchemas/"
226+
227+
payload = {"a": 1, "b": "ok!"}
228+
229+
response = sess.post_to_url(url=url, payload=payload)
230+
231+
expected_http_call = call(
232+
url,
233+
headers=self.expected_headers,
234+
json=payload,
235+
)
236+
assert getattr(sess.http, "post").call_args_list == [expected_http_call]
237+
assert response == sess.http.post.return_value
238+
239+
def test_post_to_url_with_retry(self):
240+
for allowed_status_code in range(200, 300):
241+
sess = self._prepare()
242+
sess.http_post_retry.post.return_value.status_code = allowed_status_code
243+
244+
url = self.dar_url[:-1] + "/data-manager/api/v3/datasetSchemas/"
245+
246+
payload = {"a": 1, "b": "ok!"}
247+
248+
response = sess.post_to_url(url=url, payload=payload, retry=True)
249+
expected_http_call = call(
250+
url,
251+
headers=self.expected_headers,
252+
json=payload,
253+
)
254+
assert getattr(sess.http_post_retry, "post").call_args_list == [
255+
expected_http_call
256+
]
257+
assert response == sess.http_post_retry.post.return_value
258+
220259
def _assert(self, sess, method, endpoint):
221260
# Validate test-internal assumption
222261
assert self.dar_url[-1] == "/"

tests/sap/aibus/dar/client/test_data_manager_client.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -679,7 +679,7 @@ def prepare_client(dar_url: str, clazz):
679679
mock_session = create_autospec(DARSession, instance=True)
680680
mock_session.get_from_endpoint.return_value = mock_response
681681
mock_session.post_to_endpoint.return_value = mock_response
682-
682+
mock_session.post_to_url.return_value = mock_response
683683
client = clazz.construct_from_jwt(dar_url, "abcd")
684684
client.session = mock_session
685685
return client

tests/sap/aibus/dar/client/test_inference_client.py

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -203,3 +203,50 @@ def _assert_bulk_inference_works(
203203
inference_client.session.post_to_endpoint.call_args_list
204204
== expected_calls_to_post
205205
)
206+
207+
def test_create_inference_with_url_works(self, inference_client: InferenceClient):
208+
"""
209+
Checks inference call.
210+
"""
211+
url = DAR_URL + "inference/api/v3/models/my-model/versions/1"
212+
response = inference_client.create_inference_request_with_url(
213+
url, objects=self.objects
214+
)
215+
216+
expected_call = call(
217+
url,
218+
payload={"topN": 1, "objects": self.objects},
219+
retry=False,
220+
)
221+
222+
assert inference_client.session.post_to_url.call_args_list == [expected_call]
223+
224+
assert (
225+
inference_client.session.post_to_url.return_value.json.return_value
226+
== response
227+
)
228+
229+
def test_create_inference_request_with_url_retry_enabled(
230+
self, inference_client: InferenceClient
231+
):
232+
"""
233+
Checks if retry parameter is passed correctly.
234+
"""
235+
url = DAR_URL + "inference/api/v3/models/my-model/versions/1"
236+
237+
response = inference_client.create_inference_request_with_url(
238+
url=url, objects=self.objects, retry=True
239+
)
240+
241+
expected_call = call(
242+
url,
243+
payload={"topN": 1, "objects": self.objects},
244+
retry=True,
245+
)
246+
247+
assert inference_client.session.post_to_url.call_args_list == [expected_call]
248+
249+
assert (
250+
inference_client.session.post_to_url.return_value.json.return_value
251+
== response
252+
)

version.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
0.9.1
1+
0.10.0

0 commit comments

Comments
 (0)