qwak-core 0.4.272__py3-none-any.whl → 0.4.273__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- frogml_storage/__init__.py +1 -0
- frogml_storage/artifactory/__init__.py +1 -0
- frogml_storage/artifactory/_artifactory_api.py +315 -0
- frogml_storage/authentication/login/__init__.py +1 -0
- frogml_storage/authentication/login/_login_cli.py +239 -0
- frogml_storage/authentication/login/_login_command.py +74 -0
- frogml_storage/authentication/models/__init__.py +3 -0
- frogml_storage/authentication/models/_auth.py +24 -0
- frogml_storage/authentication/models/_auth_config.py +70 -0
- frogml_storage/authentication/models/_login.py +22 -0
- frogml_storage/authentication/utils/__init__.py +17 -0
- frogml_storage/authentication/utils/_authentication_utils.py +281 -0
- frogml_storage/authentication/utils/_login_checks_utils.py +114 -0
- frogml_storage/base_storage.py +140 -0
- frogml_storage/constants.py +56 -0
- frogml_storage/exceptions/checksum_verification_error.py +3 -0
- frogml_storage/exceptions/validation_error.py +4 -0
- frogml_storage/frog_ml.py +668 -0
- frogml_storage/http/__init__.py +1 -0
- frogml_storage/http/http_client.py +83 -0
- frogml_storage/logging/__init__.py +1 -0
- frogml_storage/logging/_log_config.py +45 -0
- frogml_storage/logging/log_utils.py +21 -0
- frogml_storage/models/__init__.py +1 -0
- frogml_storage/models/_download_context.py +54 -0
- frogml_storage/models/dataset_manifest.py +13 -0
- frogml_storage/models/entity_manifest.py +93 -0
- frogml_storage/models/frogml_dataset_version.py +21 -0
- frogml_storage/models/frogml_entity_type_info.py +50 -0
- frogml_storage/models/frogml_entity_version.py +34 -0
- frogml_storage/models/frogml_model_version.py +21 -0
- frogml_storage/models/model_manifest.py +60 -0
- frogml_storage/models/serialization_metadata.py +15 -0
- frogml_storage/utils/__init__.py +12 -0
- frogml_storage/utils/_environment.py +21 -0
- frogml_storage/utils/_input_checks_utility.py +104 -0
- frogml_storage/utils/_storage_utils.py +15 -0
- frogml_storage/utils/_url_utils.py +27 -0
- qwak/__init__.py +1 -1
- qwak/clients/instance_template/client.py +6 -4
- qwak/clients/prompt_manager/model_descriptor_mapper.py +21 -19
- qwak/feature_store/_common/artifact_utils.py +3 -3
- qwak/feature_store/data_sources/base.py +4 -4
- qwak/feature_store/data_sources/batch/athena.py +3 -3
- qwak/feature_store/feature_sets/streaming.py +3 -3
- qwak/feature_store/feature_sets/streaming_backfill.py +1 -1
- qwak/feature_store/online/client.py +6 -6
- qwak/feature_store/sinks/streaming/factory.py +1 -1
- qwak/inner/build_logic/phases/phase_010_fetch_model/fetch_strategy_manager/strategy/git/git_strategy.py +3 -3
- qwak/inner/di_configuration/account.py +23 -24
- qwak/inner/tool/auth.py +2 -2
- qwak/llmops/provider/openai/provider.py +3 -3
- qwak/model/tools/adapters/output.py +1 -1
- qwak/model/utils/feature_utils.py +12 -8
- qwak/model_loggers/artifact_logger.py +7 -7
- qwak/tools/logger/logger.py +1 -1
- qwak_core-0.4.273.dist-info/METADATA +415 -0
- {qwak_core-0.4.272.dist-info → qwak_core-0.4.273.dist-info}/RECORD +59 -23
- _qwak_proto/__init__.py +0 -0
- _qwak_proto/qwak/__init__.py +0 -0
- qwak_core-0.4.272.dist-info/METADATA +0 -53
- {qwak_core-0.4.272.dist-info → qwak_core-0.4.273.dist-info}/WHEEL +0 -0
@@ -0,0 +1,27 @@
|
|
1
|
+
from typing import Optional, List
|
2
|
+
from urllib.parse import urlparse
|
3
|
+
|
4
|
+
|
5
|
+
def join_url(base_uri: str, *parts: str) -> str:
|
6
|
+
base_uri = base_uri.rstrip("/")
|
7
|
+
|
8
|
+
cleaned_parts: List[str] = [
|
9
|
+
part.strip("/") for part in parts if part is not None and part.strip("/")
|
10
|
+
]
|
11
|
+
uri_parts: List[str] = [base_uri, *cleaned_parts]
|
12
|
+
|
13
|
+
return "/".join(uri_parts)
|
14
|
+
|
15
|
+
|
16
|
+
def assemble_artifact_url(uri: Optional[str]) -> str:
|
17
|
+
if uri is None:
|
18
|
+
raise Exception("Artifactory URI is required")
|
19
|
+
|
20
|
+
parsed_url = urlparse(uri)
|
21
|
+
if parsed_url.scheme not in ["http", "https"]:
|
22
|
+
raise Exception(
|
23
|
+
f"Not a valid Artifactory URI: {uri}. "
|
24
|
+
f"Artifactory URI example: `https://frogger.jfrog.io/artifactory/ml-local`"
|
25
|
+
)
|
26
|
+
|
27
|
+
return f"{parsed_url.scheme}://{parsed_url.netloc}/artifactory"
|
qwak/__init__.py
CHANGED
@@ -49,10 +49,12 @@ class InstanceTemplateManagementClient:
|
|
49
49
|
|
50
50
|
def list_instance_templates(self) -> List[InstanceTemplateSpec]:
|
51
51
|
try:
|
52
|
-
result: ListInstanceTemplatesResponse =
|
53
|
-
|
54
|
-
|
55
|
-
|
52
|
+
result: ListInstanceTemplatesResponse = (
|
53
|
+
self._instance_template_service.ListInstanceTemplates(
|
54
|
+
ListInstanceTemplatesRequest(
|
55
|
+
optional_instance_filter=InstanceFilter(
|
56
|
+
instance_type_filter=InstanceTypeFilter.INSTANCE_TYPE_FILTER_ALL
|
57
|
+
)
|
56
58
|
)
|
57
59
|
)
|
58
60
|
)
|
@@ -44,10 +44,10 @@ class ModelDescriptorMapper:
|
|
44
44
|
model_id: str, openai_chat_params: ProtoOpenAIChatModelParams
|
45
45
|
) -> OpenAIChat:
|
46
46
|
p = openai_chat_params
|
47
|
-
_tool_choice: Union[
|
48
|
-
|
49
|
-
|
50
|
-
|
47
|
+
_tool_choice: Union[str, ChatCompletionNamedToolChoiceParam] = (
|
48
|
+
ModelDescriptorMapper._from_tool_choice(
|
49
|
+
openai_chat_params=openai_chat_params
|
50
|
+
)
|
51
51
|
)
|
52
52
|
_tools: List[ChatCompletionToolParam] = []
|
53
53
|
|
@@ -57,21 +57,23 @@ class ModelDescriptorMapper:
|
|
57
57
|
|
58
58
|
return OpenAIChat(
|
59
59
|
model_id=model_id,
|
60
|
-
frequency_penalty=
|
61
|
-
|
62
|
-
|
63
|
-
logit_bias=
|
64
|
-
|
65
|
-
|
60
|
+
frequency_penalty=(
|
61
|
+
p.frequency_penalty if p.HasField("frequency_penalty") else None
|
62
|
+
),
|
63
|
+
logit_bias=(
|
64
|
+
{k: int(v) for k, v in p.logit_bias.items()}
|
65
|
+
if p.HasField("logit_bias")
|
66
|
+
else None
|
67
|
+
),
|
66
68
|
logprobs=p.logprobs if p.HasField("logprobs") else None,
|
67
69
|
max_tokens=p.max_tokens if p.HasField("max_tokens") else None,
|
68
70
|
n=p.n if p.HasField("n") else None,
|
69
|
-
presence_penalty=
|
70
|
-
|
71
|
-
|
72
|
-
response_format=
|
73
|
-
|
74
|
-
|
71
|
+
presence_penalty=(
|
72
|
+
p.presence_penalty if p.HasField("presence_penalty") else None
|
73
|
+
),
|
74
|
+
response_format=(
|
75
|
+
p.response_format if p.HasField("response_format") else None
|
76
|
+
), # noqa
|
75
77
|
seed=p.seed if p.HasField("seed") else None,
|
76
78
|
stop=[_ for _ in p.stop] if p.HasField("stop") else None,
|
77
79
|
temperature=p.temperature if p.HasField("temperature") else None,
|
@@ -150,9 +152,9 @@ class ModelDescriptorMapper:
|
|
150
152
|
max_tokens=d.max_tokens,
|
151
153
|
n=d.n,
|
152
154
|
presence_penalty=d.presence_penalty,
|
153
|
-
response_format=
|
154
|
-
|
155
|
-
|
155
|
+
response_format=(
|
156
|
+
d.response_format if d.response_format else None
|
157
|
+
), # noqa
|
156
158
|
seed=d.seed,
|
157
159
|
stop=stop_list_value if d.stop else None,
|
158
160
|
temperature=d.temperature,
|
@@ -45,9 +45,9 @@ class ArtifactsUploader:
|
|
45
45
|
featureset_name: str,
|
46
46
|
__instance_module_path__: str,
|
47
47
|
) -> Optional[ArtifactSpec]:
|
48
|
-
transformation_functions: Optional[
|
49
|
-
|
50
|
-
|
48
|
+
transformation_functions: Optional[List[Callable[..., Any]]] = (
|
49
|
+
transformation.get_functions()
|
50
|
+
)
|
51
51
|
if transformation_functions is not None and transformation_functions:
|
52
52
|
return ArtifactSpec(
|
53
53
|
artifact_name=featureset_name,
|
@@ -63,10 +63,10 @@ class BaseSource(ABC):
|
|
63
63
|
uploaded_artifact_url = self._upload_artifact()
|
64
64
|
|
65
65
|
if source_definition_path:
|
66
|
-
presign_url:
|
67
|
-
|
68
|
-
|
69
|
-
|
66
|
+
presign_url: (
|
67
|
+
str
|
68
|
+
) = FeatureRegistryClient().get_datasource_source_code_presign_url(
|
69
|
+
ds_name=self.name
|
70
70
|
)
|
71
71
|
source_code_spec: SourceCodeSpec = (
|
72
72
|
SourceCodeSpecFactory.get_zip_source_code_spec(
|
@@ -118,9 +118,9 @@ class AthenaSource(JdbcSource):
|
|
118
118
|
)
|
119
119
|
)
|
120
120
|
|
121
|
-
time_partition_columns: Optional[
|
122
|
-
|
123
|
-
|
121
|
+
time_partition_columns: Optional[TimePartitionColumns] = (
|
122
|
+
AthenaSource._extract_partition_column(proto_athena_source)
|
123
|
+
)
|
124
124
|
workgroup: Optional[str] = (
|
125
125
|
proto_athena_source.workgroup
|
126
126
|
if proto_athena_source.HasField("workgroup")
|
@@ -120,9 +120,9 @@ def feature_set(
|
|
120
120
|
offline_scheduling_policy=offline_scheduling_policy,
|
121
121
|
)
|
122
122
|
|
123
|
-
streaming_backfill: Optional[
|
124
|
-
StreamingBackfill
|
125
|
-
|
123
|
+
streaming_backfill: Optional[StreamingBackfill] = (
|
124
|
+
StreamingBackfill.get_streaming_backfill_from_function(function=function)
|
125
|
+
)
|
126
126
|
|
127
127
|
fs_name = name or function.__name__
|
128
128
|
streaming_feature_set = StreamingFeatureSet(
|
@@ -218,7 +218,7 @@ class StreamingBackfill:
|
|
218
218
|
|
219
219
|
@staticmethod
|
220
220
|
def _get_normalized_backfill_sources_spec(
|
221
|
-
data_sources: Union[List[str], List[DataSourceBackfillSpec]]
|
221
|
+
data_sources: Union[List[str], List[DataSourceBackfillSpec]],
|
222
222
|
) -> List[DataSourceBackfillSpec]:
|
223
223
|
# reformat all data source specs to 'DataSourceBackfillSpec'
|
224
224
|
return [
|
@@ -182,12 +182,12 @@ class OnlineClient:
|
|
182
182
|
)
|
183
183
|
ordered_entities = [entity[0] for entity in ordered_entities_tuple]
|
184
184
|
|
185
|
-
request_chunks: List[
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
185
|
+
request_chunks: List[Tuple[RequestedEntitiesMatrix, pd.DataFrame]] = (
|
186
|
+
OnlineClient._split_entities(
|
187
|
+
entity_names=ordered_entities,
|
188
|
+
population_df=df,
|
189
|
+
max_entities_per_split=max_keys_per_request,
|
190
|
+
)
|
191
191
|
)
|
192
192
|
|
193
193
|
results: List[pd.DataFrame] = []
|
@@ -25,7 +25,7 @@ class StreamingSinkFactory:
|
|
25
25
|
def get_streaming_sink(proto_streaming_sink: ProtoStreamingSink) -> BaseSink:
|
26
26
|
sink_type = proto_streaming_sink.WhichOneof("sink_type")
|
27
27
|
|
28
|
-
auth_conf: BaseAuthentication
|
28
|
+
auth_conf: BaseAuthentication # noqa: F842
|
29
29
|
if sink_type == "kafka_sink":
|
30
30
|
proto_kafka_sink: ProtoKafkaSink = proto_streaming_sink.kafka_sink
|
31
31
|
auth_configuration: BaseAuthentication = cast(
|
@@ -163,9 +163,9 @@ def make_ssh_key_file(git_ssh_key: str) -> str:
|
|
163
163
|
|
164
164
|
|
165
165
|
def add_ssh_file_to_env(ssh_key_file_path: str) -> None:
|
166
|
-
os.environ[
|
167
|
-
"
|
168
|
-
|
166
|
+
os.environ["GIT_SSH_COMMAND"] = (
|
167
|
+
f"ssh -i {ssh_key_file_path} -o StrictHostKeyChecking=no"
|
168
|
+
)
|
169
169
|
os.environ["GIT_SSH"] = f"ssh -i {ssh_key_file_path} -o StrictHostKeyChecking=no"
|
170
170
|
|
171
171
|
|
@@ -8,7 +8,7 @@ from qwak.exceptions import QwakLoginException
|
|
8
8
|
from qwak.inner.const import QwakConstants
|
9
9
|
from qwak.inner.di_configuration.session import Session
|
10
10
|
from qwak.inner.tool.auth import Auth0ClientBase, FrogMLAuthClient
|
11
|
-
from frogml_storage.
|
11
|
+
from frogml_storage.authentication.login import frogml_login
|
12
12
|
|
13
13
|
|
14
14
|
@dataclass
|
@@ -58,7 +58,7 @@ class UserAccountConfiguration:
|
|
58
58
|
if not self._auth_client:
|
59
59
|
# Determine auth client based on FrogML configuration
|
60
60
|
try:
|
61
|
-
from frogml_storage.authentication.
|
61
|
+
from frogml_storage.authentication.utils import (
|
62
62
|
get_frogml_configuration,
|
63
63
|
)
|
64
64
|
|
@@ -77,28 +77,7 @@ class UserAccountConfiguration:
|
|
77
77
|
"""
|
78
78
|
if issubclass(self._auth_client, Auth0ClientBase):
|
79
79
|
# Existing Qwak authentication flow
|
80
|
-
self.
|
81
|
-
self._auth.remove_section(self._environment)
|
82
|
-
with self._safe_open(self._auth_file) as authfile:
|
83
|
-
self._auth.write(authfile)
|
84
|
-
|
85
|
-
self._auth_client(
|
86
|
-
api_key=user_account.api_key,
|
87
|
-
auth_file=self._auth_file,
|
88
|
-
).login()
|
89
|
-
|
90
|
-
# Store configuration only for Qwak auth
|
91
|
-
self._config.read(self._config_file)
|
92
|
-
with self._safe_open(self._config_file) as configfile:
|
93
|
-
self._config[self._environment] = {}
|
94
|
-
if user_account.username:
|
95
|
-
self._config[self._environment][
|
96
|
-
self.USER_FIELD
|
97
|
-
] = user_account.username
|
98
|
-
self._config[self._environment][
|
99
|
-
self.API_KEY_FIELD
|
100
|
-
] = user_account.api_key
|
101
|
-
self._config.write(configfile)
|
80
|
+
self.__qwak_login(user_account)
|
102
81
|
|
103
82
|
elif issubclass(self._auth_client, FrogMLAuthClient):
|
104
83
|
# Use FrogML's login flow
|
@@ -121,6 +100,26 @@ class UserAccountConfiguration:
|
|
121
100
|
"Authentication with JFrog failed: Only Access Tokens are supported. Please ensure you are using a valid Access Token."
|
122
101
|
)
|
123
102
|
|
103
|
+
def __qwak_login(self, user_account: UserAccount):
|
104
|
+
self._auth.read(self._auth_file)
|
105
|
+
self._auth.remove_section(self._environment)
|
106
|
+
with self._safe_open(self._auth_file) as authfile:
|
107
|
+
self._auth.write(authfile)
|
108
|
+
|
109
|
+
self._auth_client(
|
110
|
+
api_key=user_account.api_key,
|
111
|
+
auth_file=self._auth_file,
|
112
|
+
).login()
|
113
|
+
|
114
|
+
# Store configuration only for Qwak auth
|
115
|
+
self._config.read(self._config_file)
|
116
|
+
with self._safe_open(self._config_file) as configfile:
|
117
|
+
self._config[self._environment] = {}
|
118
|
+
if user_account.username:
|
119
|
+
self._config[self._environment][self.USER_FIELD] = user_account.username
|
120
|
+
self._config[self._environment][self.API_KEY_FIELD] = user_account.api_key
|
121
|
+
self._config.write(configfile)
|
122
|
+
|
124
123
|
@staticmethod
|
125
124
|
def _mkdir_p(path):
|
126
125
|
try:
|
qwak/inner/tool/auth.py
CHANGED
@@ -5,8 +5,8 @@ from typing_extensions import Self
|
|
5
5
|
from qwak.inner.di_configuration.session import Session
|
6
6
|
from abc import ABC, abstractmethod
|
7
7
|
from typing import Optional
|
8
|
-
from frogml_storage.authentication.
|
9
|
-
from frogml_storage.authentication.models
|
8
|
+
from frogml_storage.authentication.utils import get_credentials
|
9
|
+
from frogml_storage.authentication.models import AuthConfig
|
10
10
|
|
11
11
|
warnings.filterwarnings(action="ignore", module=".*jose.*")
|
12
12
|
|
@@ -26,9 +26,9 @@ class OpenAIProvider:
|
|
26
26
|
self.client = OpenAIClient()
|
27
27
|
|
28
28
|
def _get_random_openai_api_key(self) -> Optional[str]:
|
29
|
-
openai_api_keys: List[
|
30
|
-
|
31
|
-
|
29
|
+
openai_api_keys: List[OpenAIApiKeySystemSecret] = (
|
30
|
+
IntegrationUtils().get_openai_api_keys()
|
31
|
+
)
|
32
32
|
if len(openai_api_keys) == 0:
|
33
33
|
return None
|
34
34
|
|
@@ -46,7 +46,7 @@ def get_output_adapter(
|
|
46
46
|
|
47
47
|
first_result = (
|
48
48
|
return_result[0]
|
49
|
-
if
|
49
|
+
if isinstance(return_result, list) and len(return_result) > 0
|
50
50
|
else return_result
|
51
51
|
)
|
52
52
|
if issubclass(type(first_result), Message):
|
@@ -30,15 +30,19 @@ def validate_and_sanitize_features_name(
|
|
30
30
|
ecosystem_utils = EcosystemUtils()
|
31
31
|
current_env_name = ecosystem_utils.get_current_environment_name()
|
32
32
|
return [
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
33
|
+
(
|
34
|
+
cast(
|
35
|
+
FeatureStoreInput,
|
36
|
+
dataclasses.replace(
|
37
|
+
feature,
|
38
|
+
name=validate_and_sanitize_feature_name(
|
39
|
+
feature.name, current_env_name
|
40
|
+
),
|
41
|
+
),
|
42
|
+
)
|
43
|
+
if isinstance(feature, FeatureStoreInput)
|
44
|
+
else feature
|
39
45
|
)
|
40
|
-
if isinstance(feature, FeatureStoreInput)
|
41
|
-
else feature
|
42
46
|
for feature in features
|
43
47
|
]
|
44
48
|
|
@@ -92,13 +92,13 @@ def load_file(
|
|
92
92
|
)
|
93
93
|
|
94
94
|
model_id = validate_model(model_id)
|
95
|
-
download_url_response:
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
95
|
+
download_url_response: (
|
96
|
+
GetBuildVersioningDownloadURLResponse
|
97
|
+
) = BuildOrchestratorClient().get_build_versioning_download_url(
|
98
|
+
build_id=build_id,
|
99
|
+
model_id=model_id,
|
100
|
+
tag=tag,
|
101
|
+
tag_type=BuildVersioningTagsType.FILE_TAG_TYPE,
|
102
102
|
)
|
103
103
|
|
104
104
|
try:
|
qwak/tools/logger/logger.py
CHANGED
@@ -275,7 +275,7 @@ def set_file_handler_log_file(
|
|
275
275
|
logger: logging.Logger, handler_name: str, log_file: Path
|
276
276
|
):
|
277
277
|
existing_handler = get_handler_from_logger(logger, handler_name)
|
278
|
-
if
|
278
|
+
if not isinstance(existing_handler, RotatingFileHandler):
|
279
279
|
raise QwakException(
|
280
280
|
f"Error in setting log file. Error message: handler of name {handler_name} is not a file logger handler"
|
281
281
|
)
|