Skip to content
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

Add some package and resource checks #2751

Merged
merged 8 commits into from
Apr 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions docs/reference/pf-command-reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -903,6 +903,6 @@ pf upgrade --yes

To activate autocomplete features for the pf CLI you need to add the following snippet to your ~/.bashrc or ~/.zshrc:

```
source <promptflow_package_install_root>/pf.completion.sh
```bash
source <promptflow_package_install_root>/pf.completion.sh
```
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ def _workspace_default_datastore(self):
if kind not in [AzureWorkspaceKind.DEFAULT, AzureWorkspaceKind.PROJECT]:
raise RunOperationParameterError(
"Failed to get default workspace datastore. Please make sure you are using the right workspace which "
f"is either an azure machine learning studio workspace or an azure ai project. Got {kind!r} instead."
f"is either an azure machine learning workspace or an azure ai project. Got {kind!r} instead."
)
return self._datastore_operations.get_default()

Expand Down
1 change: 1 addition & 0 deletions src/promptflow-core/promptflow/_constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -277,6 +277,7 @@ class ConnectionProviderConfig:
"(/providers/Microsoft.MachineLearningServices)?/workspaces/([^/]+)$"
)
CONNECTION_DATA_CLASS_KEY = "DATA_CLASS"
AML_WORKSPACE_TEMPLATE = "azureml://subscriptions/{}/resourceGroups/{}/providers/Microsoft.MachineLearningServices/workspaces/{}" # noqa: E501


class AzureWorkspaceKind:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@
# ---------------------------------------------------------
import os

from promptflow._constants import PF_NO_INTERACTIVE_LOGIN
from promptflow._constants import PF_NO_INTERACTIVE_LOGIN, AzureWorkspaceKind
from promptflow._utils.user_agent_utils import ClientUserAgentUtil
from promptflow.core._errors import MissingRequiredPackage
from promptflow.core._errors import MissingRequiredPackage, UnsupportedWorkspaceKind
from promptflow.exceptions import ValidationException


Expand All @@ -15,7 +15,7 @@ def check_required_packages():
import azure.identity # noqa: F401
except ImportError as e:
raise MissingRequiredPackage(
message="Please install 'promptflow-core[azureml-serving]' to use workspace related features."
message="Please install 'promptflow-core[azureml-serving]' to use Azure related features."
) from e


Expand Down Expand Up @@ -67,3 +67,14 @@ def interactive_credential_disabled():
def is_from_cli():
"""Check if the current execution is from promptflow-cli."""
return "promptflow-cli" in ClientUserAgentUtil.get_user_agent()


def check_connection_provider_resource(resource_id: str, credential, pkg_name):
from .._utils import get_workspace_from_resource_id

workspace = get_workspace_from_resource_id(resource_id, credential, pkg_name)
if workspace._kind not in [AzureWorkspaceKind.DEFAULT, AzureWorkspaceKind.PROJECT]:
brynn-code marked this conversation as resolved.
Show resolved Hide resolved
raise UnsupportedWorkspaceKind(
message=f"Workspace kind {workspace._kind!r} is not supported. "
f"Please use either an azure machine learning workspace or an azure ai project."
)
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

import requests

from promptflow._constants import ConnectionAuthMode
from promptflow._constants import AML_WORKSPACE_TEMPLATE, ConnectionAuthMode
from promptflow._utils.retry_utils import http_retry_wrapper
from promptflow.core._connection import CustomConnection, _Connection
from promptflow.core._errors import (
Expand All @@ -16,6 +16,7 @@
MissingRequiredPackage,
OpenURLFailed,
OpenURLFailedUserError,
OpenURLNotFoundError,
OpenURLUserAuthenticationError,
UnknownConnectionType,
UnsupportedConnectionAuthType,
Expand All @@ -24,7 +25,12 @@

from ..._utils.credential_utils import get_default_azure_credential
from ._connection_provider import ConnectionProvider
from ._utils import interactive_credential_disabled, is_from_cli, is_github_codespaces
from ._utils import (
check_connection_provider_resource,
interactive_credential_disabled,
is_from_cli,
is_github_codespaces,
)

GET_CONNECTION_URL = (
"/subscriptions/{sub}/resourcegroups/{rg}/providers/Microsoft.MachineLearningServices"
Expand Down Expand Up @@ -76,6 +82,10 @@ def __init__(
self.subscription_id = subscription_id
self.resource_group_name = resource_group_name
self.workspace_name = workspace_name
self.resource_id = AML_WORKSPACE_TEMPLATE.format(
self.subscription_id, self.resource_group_name, self.workspace_name
)
self._workspace_checked = False

@property
def credential(self):
Expand Down Expand Up @@ -131,7 +141,13 @@ def open_url(cls, token, url, action, host="management.azure.com", method="GET",
f"Open url {{url}} failed with status code: {response.status_code}, action: {action}, reason: {{reason}}"
)
if response.status_code == 403:
raise AccessDeniedError(operation=url, target=ErrorTarget.RUNTIME)
raise AccessDeniedError(operation=url, target=ErrorTarget.CORE)
elif response.status_code == 404:
raise OpenURLNotFoundError(
message_format=message_format,
url=url,
reason=response.reason,
)
elif 400 <= response.status_code < 500:
raise OpenURLFailedUserError(
message_format=message_format,
Expand Down Expand Up @@ -404,6 +420,8 @@ def _build_list_connection_dict(
raise OpenURLUserAuthenticationError(message=auth_error_message)
except ClientAuthenticationError as e:
raise UserErrorException(target=ErrorTarget.CORE, message=str(e), error=e) from e
except UserErrorException:
raise
except Exception as e:
raise SystemErrorException(target=ErrorTarget.CORE, message=str(e), error=e) from e

Expand All @@ -417,6 +435,12 @@ def _build_list_connection_dict(
return rest_list_connection_dict

def list(self) -> List[_Connection]:
if not self._workspace_checked:
# Check workspace not 'hub'
check_connection_provider_resource(
resource_id=self.resource_id, credential=self.credential, pkg_name="promptflow-core[azureml-serving]"
)
self._workspace_checked = True
rest_list_connection_dict = self._build_list_connection_dict(
subscription_id=self.subscription_id,
resource_group_name=self.resource_group_name,
Expand All @@ -432,6 +456,12 @@ def list(self) -> List[_Connection]:
return connection_list

def get(self, name: str, **kwargs) -> _Connection:
if not self._workspace_checked:
# Check workspace not 'hub'
check_connection_provider_resource(
resource_id=self.resource_id, credential=self.credential, pkg_name="promptflow-core[azureml-serving]"
)
self._workspace_checked = True
connection_dict = self._build_connection_dict(
name,
subscription_id=self.subscription_id,
Expand Down
12 changes: 12 additions & 0 deletions src/promptflow-core/promptflow/core/_errors.py
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,11 @@ def __init__(self, **kwargs):
super().__init__(target=ErrorTarget.CORE, **kwargs)


class OpenURLNotFoundError(UserErrorException):
def __init__(self, **kwargs):
super().__init__(target=ErrorTarget.CORE, **kwargs)


class UnknownConnectionType(UserErrorException):
def __init__(self, **kwargs):
super().__init__(target=ErrorTarget.CORE, **kwargs)
Expand Down Expand Up @@ -127,6 +132,13 @@ def __init__(self, provider_config, **kwargs):
super().__init__(target=ErrorTarget.CORE, message=message, **kwargs)


class UnsupportedWorkspaceKind(UserErrorException):
"""Exception raised when workspace kind is not supported."""

def __init__(self, message, **kwargs):
super().__init__(target=ErrorTarget.CORE, message=message, **kwargs)


class AccessDeniedError(UserErrorException):
"""Exception raised when run info can not be found in storage"""

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import re
from typing import Any, Tuple

from promptflow._constants import AML_WORKSPACE_TEMPLATE
from promptflow._utils.retry_utils import retry
from promptflow.contracts.flow import Flow
from promptflow.core._serving._errors import InvalidConnectionData, MissingConnectionProvider
Expand All @@ -18,7 +19,6 @@

USER_AGENT = f"promptflow-cloud-serving/{__version__}"
AML_DEPLOYMENT_RESOURCE_ID_REGEX = "/subscriptions/(.*)/resourceGroups/(.*)/providers/Microsoft.MachineLearningServices/workspaces/(.*)/onlineEndpoints/(.*)/deployments/(.*)" # noqa: E501
AML_CONNECTION_PROVIDER_TEMPLATE = "azureml://subscriptions/{}/resourceGroups/{}/providers/Microsoft.MachineLearningServices/workspaces/{}" # noqa: E501


class AzureMLExtension(AppExtension):
Expand Down Expand Up @@ -159,7 +159,7 @@ def _initialize_connection_provider(self):
message="Missing connection provider, please check whether 'PROMPTFLOW_CONNECTION_PROVIDER' "
"is in your environment variable list."
) # noqa: E501
self.connection_provider = AML_CONNECTION_PROVIDER_TEMPLATE.format(
self.connection_provider = AML_WORKSPACE_TEMPLATE.format(
self.subscription_id, self.resource_group, self.workspace_name
) # noqa: E501

Expand Down
9 changes: 9 additions & 0 deletions src/promptflow-devkit/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# Release History

## 1.9.0 (Upcoming)

### Features Added
- Added autocomplete feature for linux, reach [here](https://microsoft.github.io/promptflow/reference/pf-command-reference.html#autocomplete) for more details.

### Bugs Fixed
- Fix run name missing directory name in some scenario of `pf.run`.
2 changes: 1 addition & 1 deletion src/promptflow-devkit/promptflow/_sdk/entities/_run.py
Original file line number Diff line number Diff line change
Expand Up @@ -515,7 +515,7 @@ def _format_display_name(self) -> str:

def _get_flow_dir(self) -> Path:
if not self._use_remote_flow:
flow = Path(self.flow)
flow = Path(str(self.flow)).resolve().absolute()
brynn-code marked this conversation as resolved.
Show resolved Hide resolved
brynn-code marked this conversation as resolved.
Show resolved Hide resolved
if flow.is_dir():
return flow
return flow.parent
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -99,16 +99,16 @@ def sanitize_azure_workspace_triad(value: str) -> str:
flags=re.IGNORECASE,
)
sanitized_ws = re.sub(
r"/(workspaces)/[-\w\._\(\)]+[/?]",
r"/\1/{}/".format("00000"),
r"/(workspaces)/[-\w\._\(\)]+([/?][-\w\._\(\)]*)",
r"/\1/{}\2".format("00000"),
sanitized_rg,
flags=re.IGNORECASE,
)

# workspace name can be the last part of the string
# e.g. xxx/Microsoft.MachineLearningServices/workspaces/<workspace-name>
# apply a special handle here to sanitize
if sanitized_ws.startswith("https://"):
if sanitized_ws == sanitized_rg and sanitized_ws.startswith("https://"):
split1, split2 = sanitized_ws.split("/")[-2:]
if split1 == "workspaces":
sanitized_ws = sanitized_ws.replace(split2, SanitizedValues.WORKSPACE_NAME)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,21 +9,21 @@ interactions:
Connection:
- keep-alive
User-Agent:
- promptflow-sdk/0.0.1 promptflow/0.0.1 azure-ai-ml/1.12.1 azsdk-python-mgmt-machinelearningservices/0.1.0
Python/3.10.13 (Windows-10-10.0.22631-SP0)
- promptflow-azure-sdk/0.1.0b1 azure-ai-ml/1.15.0 azsdk-python-mgmt-machinelearningservices/0.1.0
Python/3.9.19 (Windows-10-10.0.22631-SP0)
method: GET
uri: https://management.azure.com/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/00000/providers/Microsoft.MachineLearningServices/workspaces/00000/api-version=2023-08-01-preview
uri: https://management.azure.com/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/00000/providers/Microsoft.MachineLearningServices/workspaces/00000?api-version=2023-08-01-preview
response:
body:
string: '{"id": "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/00000/providers/Microsoft.MachineLearningServices/workspaces/00000",
"name": "00000", "type": "Microsoft.MachineLearningServices/workspaces", "location":
"eastus", "tags": {}, "etag": null, "kind": "Default", "sku": {"name": "Basic",
"tier": "Basic"}, "properties": {"discoveryUrl": "https://eastus.api.azureml.ms/discovery"}}'
"eastus2euap", "tags": {}, "etag": null, "kind": "Default", "sku": {"name":
"Basic", "tier": "Basic"}, "properties": {"discoveryUrl": "https://eastus.api.azureml.ms/discovery"}}'
headers:
cache-control:
- no-cache
content-length:
- '3630'
- '3697'
content-type:
- application/json; charset=utf-8
expires:
Expand All @@ -39,7 +39,7 @@ interactions:
x-content-type-options:
- nosniff
x-request-time:
- '0.017'
- '0.019'
status:
code: 200
message: OK
Expand All @@ -53,29 +53,21 @@ interactions:
Connection:
- keep-alive
User-Agent:
- promptflow-sdk/0.0.1 promptflow/0.0.1 azure-ai-ml/1.12.1 azsdk-python-mgmt-machinelearningservices/0.1.0
Python/3.10.13 (Windows-10-10.0.22631-SP0)
- azure-ai-ml/1.15.0 azsdk-python-mgmt-machinelearningservices/0.1.0 Python/3.9.19
(Windows-10-10.0.22631-SP0)
method: GET
uri: https://management.azure.com/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/00000/providers/Microsoft.MachineLearningServices/workspaces/00000/datastores?api-version=2023-04-01-preview&count=30&isDefault=true&orderByAsc=false
uri: https://management.azure.com/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/00000/providers/Microsoft.MachineLearningServices/workspaces/00000?api-version=2023-08-01-preview
response:
body:
string: '{"value": [{"id": "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/00000/providers/Microsoft.MachineLearningServices/workspaces/00000/datastores/workspaceblobstore",
"name": "workspaceblobstore", "type": "Microsoft.MachineLearningServices/workspaces/datastores",
"properties": {"description": null, "tags": null, "properties": null, "isDefault":
true, "credentials": {"credentialsType": "AccountKey"}, "intellectualProperty":
null, "subscriptionId": "00000000-0000-0000-0000-000000000000", "resourceGroup":
"00000", "datastoreType": "AzureBlob", "accountName": "fake_account_name",
"containerName": "fake-container-name", "endpoint": "core.windows.net", "protocol":
"https", "serviceDataAccessAuthIdentity": "WorkspaceSystemAssignedIdentity"},
"systemData": {"createdAt": "2023-04-08T02:53:06.5886442+00:00", "createdBy":
"779301c0-18b2-4cdc-801b-a0a3368fee0a", "createdByType": "Application", "lastModifiedAt":
"2023-04-08T02:53:07.521127+00:00", "lastModifiedBy": "779301c0-18b2-4cdc-801b-a0a3368fee0a",
"lastModifiedByType": "Application"}}]}'
string: '{"id": "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/00000/providers/Microsoft.MachineLearningServices/workspaces/00000",
"name": "00000", "type": "Microsoft.MachineLearningServices/workspaces", "location":
"eastus2euap", "tags": {}, "etag": null, "kind": "Default", "sku": {"name":
"Basic", "tier": "Basic"}, "properties": {"discoveryUrl": "https://eastus.api.azureml.ms/discovery"}}'
headers:
cache-control:
- no-cache
content-length:
- '1372'
- '3697'
content-type:
- application/json; charset=utf-8
expires:
Expand All @@ -91,7 +83,7 @@ interactions:
x-content-type-options:
- nosniff
x-request-time:
- '0.070'
- '0.020'
status:
code: 200
message: OK
Expand All @@ -116,18 +108,20 @@ interactions:
"name": "azure_open_ai_connection", "type": "Microsoft.MachineLearningServices/workspaces/connections",
"properties": {"authType": "ApiKey", "credentials": {"key": "_"}, "group":
"AzureAI", "category": "AzureOpenAI", "expiryTime": null, "target": "_", "createdByWorkspaceArmId":
null, "isSharedToAll": false, "sharedUserList": [], "metadata": {"azureml.flow.connection_type":
"AzureOpenAI", "azureml.flow.module": "promptflow.connections", "ApiType":
"azure", "ApiVersion": "2023-07-01-preview", "ResourceId": null, "DeploymentApiVersion":
"2023-10-01-preview"}}, "systemData": {"createdAt": "2023-08-22T10:15:34.5762053Z",
"createdBy": "[email protected]", "createdByType": "User", "lastModifiedAt":
"2023-08-22T10:15:34.5762053Z", "lastModifiedBy": "[email protected]",
"lastModifiedByType": "User"}}'
"/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/00000/providers/Microsoft.MachineLearningServices/workspaces/promptflow-eastus2euap",
"useWorkspaceManagedIdentity": false, "isSharedToAll": false, "sharedUserList":
[], "metadata": {"azureml.flow.connection_type": "AzureOpenAI", "azureml.flow.module":
"promptflow.connections", "ApiType": "azure", "ApiVersion": "2023-07-01-preview",
"ResourceId": "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/00000/providers/Microsoft.CognitiveServices/accounts/gpt-test-eus",
"DeploymentApiVersion": "2023-10-01-preview"}}, "systemData": {"createdAt":
"2024-03-22T14:56:56.0857069Z", "createdBy": "[email protected]", "createdByType":
"User", "lastModifiedAt": "2024-03-22T14:56:56.0857069Z", "lastModifiedBy":
"[email protected]", "lastModifiedByType": "User"}}'
headers:
cache-control:
- no-cache
content-length:
- '1246'
- '1579'
content-type:
- application/json; charset=utf-8
expires:
Expand All @@ -143,7 +137,7 @@ interactions:
x-content-type-options:
- nosniff
x-request-time:
- '0.072'
- '0.772'
status:
code: 200
message: OK
Expand All @@ -168,16 +162,18 @@ interactions:
"name": "custom_connection", "type": "Microsoft.MachineLearningServices/workspaces/connections",
"properties": {"authType": "CustomKeys", "credentials": {"keys": {}}, "group":
"AzureAI", "category": "CustomKeys", "expiryTime": null, "target": "_", "createdByWorkspaceArmId":
null, "isSharedToAll": false, "sharedUserList": [], "metadata": {"azureml.flow.connection_type":
"Custom", "azureml.flow.module": "promptflow.connections"}}, "systemData":
{"createdAt": "2023-06-19T20:56:12.0353964Z", "createdBy": "[email protected]",
"createdByType": "User", "lastModifiedAt": "2023-06-19T20:56:12.0353964Z",
"lastModifiedBy": "[email protected]", "lastModifiedByType": "User"}}'
"/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/00000/providers/Microsoft.MachineLearningServices/workspaces/promptflow-eastus2euap",
"useWorkspaceManagedIdentity": false, "isSharedToAll": false, "sharedUserList":
[], "metadata": {"azureml.flow.connection_type": "Custom", "azureml.flow.module":
"promptflow.connections"}}, "systemData": {"createdAt": "2023-12-18T09:44:22.1118582Z",
"createdBy": "[email protected]", "createdByType": "User", "lastModifiedAt":
"2023-12-18T09:44:22.1118582Z", "lastModifiedBy": "[email protected]",
"lastModifiedByType": "User"}}'
headers:
cache-control:
- no-cache
content-length:
- '1275'
- '1254'
content-type:
- application/json; charset=utf-8
expires:
Expand All @@ -193,7 +189,7 @@ interactions:
x-content-type-options:
- nosniff
x-request-time:
- '0.075'
- '0.090'
status:
code: 200
message: OK
Expand Down
Loading
Loading