Skip to content

Commit 4fbe028

Browse files
Adding Personalizer SDK for Python. (Azure#26719)
* Adding Personalizer SDK for Python. * record tests * remove default_language on async client * remove locale from link * update dependencies and shared reqs overrides * get mypy clean * Enable multislot before making multi slot rank and reward calls. * update recordings for personalizer * Separate client into PersonalizerClient and PersonalizerAdministrationClient. Remove helper methods from samples and link to the simulation tutorial. * Fix errors from tox commands. * Reacting to feedback. * Fixed pylint errors. * Removed operation groups and added tests for feature importance. * Rename get_model to export_model. * Enable create feature importance tests. * Rename functions in swagger to avoid creating operation groups. * Adding examples in doc strings and examples in README file. * Add send_request method. * Use variables to store random guids for evaluations and feature importances. * Addressed all feedback and generated the recordings again. * Changing location to Canary in the tests.yml file instead of test-resources.json. * Use preset resources for creating evaluations and feaure importance as they depend on cooked logs. * Updated samples to use environment variables output after creating test resources. Co-authored-by: Krista Pratico <krpratic@microsoft.com>
1 parent ae5480c commit 4fbe028

File tree

96 files changed

+19001
-0
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

96 files changed

+19001
-0
lines changed

.vscode/cspell.json

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@
6161
"sdk/graphrbac/azure-graphrbac/**",
6262
"sdk/formrecognizer/azure-ai-formrecognizer/samples/sample_forms/**",
6363
"sdk/formrecognizer/azure-ai-formrecognizer/tests/sample_forms/**",
64+
"sdk/personalizer/azure-ai-personalizer/azure/ai/personalizer/**",
6465
"sdk/identity/azure-identity/images/**",
6566
"sdk/identity/azure-identity/tests/pod-identity/**",
6667
"sdk/identity/azure-identity/tests/managed-identity-live/service-fabric/**",
@@ -1139,6 +1140,12 @@
11391140
"words": [
11401141
"azconfig"
11411142
]
1143+
},
1144+
{
1145+
"filename": "sdk/personalizer/test-resources.json",
1146+
"words": [
1147+
"euap"
1148+
]
11421149
}
11431150
],
11441151
"allowCompoundWords": true

eng/tox/mypy_hard_failure_packages.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
"azure-servicebus",
1717
"azure-ai-textanalytics",
1818
"azure-ai-formrecognizer",
19+
"azure-ai-personalizer",
1920
"azure-ai-translation-document",
2021
"azure-ai-metricsadvisor",
2122
"azure-eventgrid",
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
# Release History
2+
3+
## 1.0.0b1 (2022-11-07)
4+
5+
### Features Added
6+
- New namespace/package name:
7+
- The namespace/package name for the Personalizer client library has changed from
8+
`azure.cognitiveservices.personalizer` to `azure.ai.personalizer`
9+
- Asynchronous APIs added under `azure.ai.personalizer.aio` namespace
10+
- Authentication with AAD supported
11+
- Authentication with API key supported using `AzureKeyCredential("<api_key>")` from `azure.core.credentials`
12+
- New underlying REST pipeline implementation based on the azure-core library
13+
- New error hierarchy:
14+
- All service errors will now use the base type: `azure.core.exceptions.HttpResponseError`
15+
16+
### Breaking Changes
17+
- Version (1.0.0b1) is the first preview of our efforts to create a user-friendly and Pythonic client library for Azure Personalizer.
18+
- This library replaces the package found here: https://pypi.org/project/azure-cognitiveservices-personalizer/
19+
20+
For more information about this, and preview releases of other Azure SDK libraries, please visit
21+
https://azure.github.io/azure-sdk/releases/latest/python.html.
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
Copyright (c) Microsoft Corporation.
2+
3+
MIT License
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
recursive-include tests *.py
2+
include *.md
3+
include LICENSE
4+
include azure/__init__.py
5+
include azure/ai/__init__.py
6+
recursive-include samples *.py *.md
7+
recursive-include doc *.rst
8+
include azure/ai/personalizer/py.typed
Lines changed: 200 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,200 @@
1+
# Azure Personalizer client library for Python
2+
3+
[Azure Personalizer][personalizer_doc]
4+
is a cloud-based service that helps your applications choose the best content item to show your users. You can use the Personalizer service to determine what product to suggest to shoppers or to figure out the optimal position for an advertisement. After the content is shown to the user, your application monitors the user's reaction and reports a reward score back to the Personalizer service. This ensures continuous improvement of the machine learning model, and Personalizer's ability to select the best content item based on the contextual information it receives.
5+
6+
## Getting started
7+
8+
### Prerequisites
9+
* Python 3.7 or later is required to use this package.
10+
* You must have an [Azure subscription][azure_subscription] and a
11+
[Personalizer resource][personalizer_account] to use this package.
12+
13+
### Install the package
14+
Install the Azure Personalizer client library for Python with [pip][pip]:
15+
16+
```bash
17+
pip install azure-ai-personalizer
18+
```
19+
20+
> Note: This version of the client library defaults to the `2022-09-01-preview` version of the service.
21+
22+
This table shows the relationship between SDK versions and supported API versions of the service:
23+
24+
|SDK version|Supported API version of service
25+
|-|-
26+
|1.0.0b1| 2022-09-01-preview
27+
28+
## Key concepts
29+
30+
### PersonalizerClient
31+
The [synchronous PersonalizerClient][personalizer_sync_client] and
32+
[asynchronous PersonalizerClient][personalizer_async_client] provide synchronous and asynchronous operations to:
33+
- Manage the machine learning model and learning settings for the Personalizer service.
34+
- Manage the properties of the Personalizer service such as the [learning mode][learning_mode], [exploration percentage][exploration].
35+
- Run counterfactual evaluations on prior historical event data.
36+
- Rank a set of actions, then activate and reward the event.
37+
- Use [multi-slot personalization][multi_slot] when there are more than one slots.
38+
- Manage the properties of the Personalizer service.
39+
- Run counterfactual evaluations on prior historical event data.
40+
41+
42+
## Examples
43+
The following examples outline the main scenarios when using personalizer in single-slot and multi-slot configurations.
44+
45+
* [Send rank and reward when using a single slot](#send-rank-and-reward "Send rank and reward")
46+
* [Send rank and reward when using multiple slots](#send-multi-slot-rank-and-reward "Send multi-slot rank and reward")
47+
48+
### Send rank and reward
49+
50+
```python
51+
from azure.ai.personalizer import PersonalizerClient
52+
from azure.core.credentials import AzureKeyCredential
53+
54+
endpoint = "https://<my-personalizer-instance>.cognitiveservices.azure.com/"
55+
credential = AzureKeyCredential("<api_key>")
56+
57+
client = PersonalizerClient(endpoint, credential)
58+
59+
# The list of actions to be ranked with metadata associated for each action.
60+
actions = [
61+
{
62+
"id": "Video1",
63+
"features": [
64+
{"videoType": "documentary", "videoLength": 35, "director": "CarlSagan"},
65+
{"mostWatchedByAge": "50-55"},
66+
],
67+
},
68+
{
69+
"id": "Video2",
70+
"features": [
71+
{"videoType": "movie", "videoLength": 120, "director": "StevenSpielberg"},
72+
{"mostWatchedByAge": "40-45"},
73+
],
74+
},
75+
]
76+
77+
# Context of the user to which the action must be presented.
78+
context_features = [
79+
{"currentContext": {"day": "tuesday", "time": "night", "weather": "rainy"}},
80+
{
81+
"userContext": {
82+
"payingUser": True,
83+
"favoriteGenre": "documentary",
84+
"hoursOnSite": 0.12,
85+
"lastWatchedType": "movie",
86+
},
87+
},
88+
]
89+
90+
request = {
91+
"actions": actions,
92+
"contextFeatures": context_features,
93+
}
94+
95+
rank_response = client.rank(request)
96+
print("Sending reward event")
97+
client.reward(rank_response.get("eventId"), {"value": 1.0})
98+
```
99+
100+
### Send multi-slot rank and reward
101+
102+
```python
103+
from azure.ai.personalizer import PersonalizerClient
104+
from azure.core.credentials import AzureKeyCredential
105+
106+
endpoint = "https://<my-personalizer-instance>.cognitiveservices.azure.com/"
107+
credential = AzureKeyCredential("<api_key>")
108+
109+
client = PersonalizerClient(endpoint, credential)
110+
111+
# We want to rank the actions for two slots.
112+
slots = [
113+
{
114+
"id": "Main Article",
115+
"baselineAction": "NewsArticle",
116+
"positionFeatures": [{"Size": "Large", "Position": "Top Middle"}],
117+
},
118+
{
119+
"id": "Side Bar",
120+
"baselineAction": "SportsArticle",
121+
"positionFeatures": [{"Size": "Small", "Position": "Bottom Right"}],
122+
},
123+
]
124+
125+
# The list of actions to be ranked with metadata associated for each action.
126+
actions = [
127+
{"id": "NewsArticle", "features": [{"type": "News"}]},
128+
{"id": "SportsArticle", "features": [{"type": "Sports"}]},
129+
{"id": "EntertainmentArticle", "features": [{"type": "Entertainment"}]},
130+
]
131+
132+
# Context of the user to which the action must be presented.
133+
context_features = [
134+
{"user": {"profileType": "AnonymousUser", "latLong": "47.6,-122.1"}},
135+
{"environment": {"dayOfMonth": "28", "monthOfYear": "8", "weather": "Sunny"}},
136+
{"device": {"mobile": True, "windows": True}},
137+
{"recentActivity": {"itemsInCart": 3}},
138+
]
139+
140+
request = {
141+
"slots": slots,
142+
"actions": actions,
143+
"contextFeatures": context_features,
144+
}
145+
rank_response = client.rank_multi_slot(request)
146+
print("Sending reward event for Main Article slot")
147+
client.reward_multi_slot(
148+
rank_response.get("eventId"),
149+
{"reward": [{"slotId": "Main Article", "value": 1.0}]})
150+
```
151+
152+
## Troubleshooting
153+
154+
### General
155+
Personalizer client library will raise exceptions defined in [Azure Core][azure_core_exceptions].
156+
157+
### Logging
158+
This library uses the standard [logging][python_logging] library for logging.
159+
160+
Basic information about HTTP sessions (URLs, headers, etc.) is logged at `INFO` level.
161+
162+
Detailed `DEBUG` level logging, including request/response bodies and **unredacted**
163+
headers, can be enabled on the client or per-operation with the `logging_enable` keyword argument.
164+
165+
See full SDK logging documentation with examples [here][sdk_logging_docs].
166+
167+
### Optional Configuration
168+
169+
Optional keyword arguments can be passed in at the client and per-operation level.
170+
The azure-core [reference documentation][azure_core_ref_docs]
171+
describes available configurations for retries, logging, transport protocols, and more.
172+
173+
## Next steps
174+
175+
## Contributing
176+
This project welcomes contributions and suggestions. Most contributions require you to agree to a Contributor License Agreement (CLA) declaring that you have the right to, and actually do, grant us the rights to use your contribution. For details, visit [cla.microsoft.com][cla].
177+
178+
When you submit a pull request, a CLA-bot will automatically determine whether you need to provide a CLA and decorate the PR appropriately (e.g., label, comment). Simply follow the instructions provided by the bot. You will only need to do this once across all repos using our CLA.
179+
180+
This project has adopted the [Microsoft Open Source Code of Conduct][code_of_conduct]. For more information see the [Code of Conduct FAQ][coc_faq] or contact [opencode@microsoft.com][coc_contact] with any additional questions or comments.
181+
182+
<!-- LINKS -->
183+
[personalizer_doc]: https://docs.microsoft.com/azure/cognitive-services/personalizer/
184+
[azure_subscription]: https://azure.microsoft.com/free
185+
[personalizer_account]: https://docs.microsoft.com/azure/cognitive-services/cognitive-services-apis-create-account?tabs=multiservice%2Cwindows
186+
[pip]: https://pypi.org/project/pip/
187+
[personalizer_sync_client]: https://github.com/Azure/azure-sdk-for-python/blob/main/sdk/personalizer/azure-ai-personalizer/azure/ai/personalizer/_client.py
188+
[personalizer_async_client]: https://github.com/Azure/azure-sdk-for-python/blob/main/sdk/personalizer/azure-ai-personalizer/azure/ai/personalizer/aio/_client.py
189+
[learning_mode]: https://docs.microsoft.com/azure/cognitive-services/personalizer/what-is-personalizer#learning-modes
190+
[exploration]: https://docs.microsoft.com/azure/cognitive-services/personalizer/concepts-exploration
191+
[multi_slot]: https://docs.microsoft.com/azure/cognitive-services/personalizer/concept-multi-slot-personalization
192+
[examples]: https://github.com/Azure/azure-sdk-for-python/blob/main/sdk/personalizer/azure-ai-personalizer/samples
193+
[azure_core_exceptions]: https://aka.ms/azsdk/python/core/docs#module-azure.core.exceptions
194+
[python_logging]: https://docs.python.org/3/library/logging.html
195+
[sdk_logging_docs]: https://docs.microsoft.com/azure/developer/python/sdk/azure-sdk-logging
196+
[azure_core_ref_docs]: https://aka.ms/azsdk/python/core/docs
197+
[cla]: https://cla.microsoft.com
198+
[code_of_conduct]: https://opensource.microsoft.com/codeofconduct/
199+
[coc_faq]: https://opensource.microsoft.com/codeofconduct/faq/
200+
[coc_contact]: mailto:opencode@microsoft.com
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
__path__ = __import__('pkgutil').extend_path(__path__, __name__) # type: ignore
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
__path__ = __import__('pkgutil').extend_path(__path__, __name__) # type: ignore
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
# coding=utf-8
2+
# --------------------------------------------------------------------------
3+
# Copyright (c) Microsoft Corporation. All rights reserved.
4+
# Licensed under the MIT License. See License.txt in the project root for license information.
5+
# Code generated by Microsoft (R) AutoRest Code Generator.
6+
# Changes may cause incorrect behavior and will be lost if the code is regenerated.
7+
# --------------------------------------------------------------------------
8+
9+
from ._client import PersonalizerClient
10+
from ._version import VERSION
11+
12+
__version__ = VERSION
13+
14+
try:
15+
from ._patch import __all__ as _patch_all
16+
from ._patch import * # type: ignore # pylint: disable=unused-wildcard-import
17+
except ImportError:
18+
_patch_all = []
19+
from ._patch import patch_sdk as _patch_sdk
20+
21+
__all__ = [
22+
"PersonalizerClient",
23+
]
24+
__all__.extend([p for p in _patch_all if p not in __all__])
25+
26+
_patch_sdk()
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
# coding=utf-8
2+
# --------------------------------------------------------------------------
3+
# Copyright (c) Microsoft Corporation. All rights reserved.
4+
# Licensed under the MIT License. See License.txt in the project root for license information.
5+
# Code generated by Microsoft (R) AutoRest Code Generator.
6+
# Changes may cause incorrect behavior and will be lost if the code is regenerated.
7+
# --------------------------------------------------------------------------
8+
9+
from copy import deepcopy
10+
from typing import Any
11+
12+
from azure.core import PipelineClient
13+
from azure.core.credentials import AzureKeyCredential
14+
from azure.core.rest import HttpRequest, HttpResponse
15+
16+
from ._configuration import PersonalizerClientConfiguration
17+
from ._operations import PersonalizerClientOperationsMixin
18+
from ._serialization import Deserializer, Serializer
19+
20+
21+
class PersonalizerClient(PersonalizerClientOperationsMixin): # pylint: disable=client-accepts-api-version-keyword
22+
"""Personalizer Service is an Azure Cognitive Service that makes it easy to target content and
23+
experiences without complex pre-analysis or cleanup of past data. Given a context and
24+
featurized content, the Personalizer Service returns which content item to show to users in
25+
rewardActionId. As rewards are sent in response to the use of rewardActionId, the reinforcement
26+
learning algorithm will improve the model and improve performance of future rank calls.
27+
28+
:param endpoint: Supported Cognitive Services endpoint. Required.
29+
:type endpoint: str
30+
:param credential: Credential needed for the client to connect to Azure. Required.
31+
:type credential: ~azure.core.credentials.AzureKeyCredential
32+
:keyword api_version: Api Version. Default value is "2022-09-01-preview". Note that overriding
33+
this default value may result in unsupported behavior.
34+
:paramtype api_version: str
35+
"""
36+
37+
def __init__(self, endpoint: str, credential: AzureKeyCredential, **kwargs: Any) -> None:
38+
_endpoint = "{Endpoint}/personalizer"
39+
self._config = PersonalizerClientConfiguration(endpoint=endpoint, credential=credential, **kwargs)
40+
self._client = PipelineClient(base_url=_endpoint, config=self._config, **kwargs)
41+
42+
self._serialize = Serializer()
43+
self._deserialize = Deserializer()
44+
self._serialize.client_side_validation = False
45+
46+
def send_request(self, request: HttpRequest, **kwargs: Any) -> HttpResponse:
47+
"""Runs the network request through the client's chained policies.
48+
49+
>>> from azure.core.rest import HttpRequest
50+
>>> request = HttpRequest("GET", "https://www.example.org/")
51+
<HttpRequest [GET], url: 'https://www.example.org/'>
52+
>>> response = client.send_request(request)
53+
<HttpResponse: 200 OK>
54+
55+
For more information on this code flow, see https://aka.ms/azsdk/dpcodegen/python/send_request
56+
57+
:param request: The network request you want to make. Required.
58+
:type request: ~azure.core.rest.HttpRequest
59+
:keyword bool stream: Whether the response payload will be streamed. Defaults to False.
60+
:return: The response of your network call. Does not do error handling on your response.
61+
:rtype: ~azure.core.rest.HttpResponse
62+
"""
63+
64+
request_copy = deepcopy(request)
65+
path_format_arguments = {
66+
"Endpoint": self._serialize.url("self._config.endpoint", self._config.endpoint, "str", skip_quote=True),
67+
}
68+
69+
request_copy.url = self._client.format_url(request_copy.url, **path_format_arguments)
70+
return self._client.send_request(request_copy, **kwargs)
71+
72+
def close(self):
73+
# type: () -> None
74+
self._client.close()
75+
76+
def __enter__(self):
77+
# type: () -> PersonalizerClient
78+
self._client.__enter__()
79+
return self
80+
81+
def __exit__(self, *exc_details):
82+
# type: (Any) -> None
83+
self._client.__exit__(*exc_details)

0 commit comments

Comments
 (0)