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.
Files changed (62) hide show
  1. frogml_storage/__init__.py +1 -0
  2. frogml_storage/artifactory/__init__.py +1 -0
  3. frogml_storage/artifactory/_artifactory_api.py +315 -0
  4. frogml_storage/authentication/login/__init__.py +1 -0
  5. frogml_storage/authentication/login/_login_cli.py +239 -0
  6. frogml_storage/authentication/login/_login_command.py +74 -0
  7. frogml_storage/authentication/models/__init__.py +3 -0
  8. frogml_storage/authentication/models/_auth.py +24 -0
  9. frogml_storage/authentication/models/_auth_config.py +70 -0
  10. frogml_storage/authentication/models/_login.py +22 -0
  11. frogml_storage/authentication/utils/__init__.py +17 -0
  12. frogml_storage/authentication/utils/_authentication_utils.py +281 -0
  13. frogml_storage/authentication/utils/_login_checks_utils.py +114 -0
  14. frogml_storage/base_storage.py +140 -0
  15. frogml_storage/constants.py +56 -0
  16. frogml_storage/exceptions/checksum_verification_error.py +3 -0
  17. frogml_storage/exceptions/validation_error.py +4 -0
  18. frogml_storage/frog_ml.py +668 -0
  19. frogml_storage/http/__init__.py +1 -0
  20. frogml_storage/http/http_client.py +83 -0
  21. frogml_storage/logging/__init__.py +1 -0
  22. frogml_storage/logging/_log_config.py +45 -0
  23. frogml_storage/logging/log_utils.py +21 -0
  24. frogml_storage/models/__init__.py +1 -0
  25. frogml_storage/models/_download_context.py +54 -0
  26. frogml_storage/models/dataset_manifest.py +13 -0
  27. frogml_storage/models/entity_manifest.py +93 -0
  28. frogml_storage/models/frogml_dataset_version.py +21 -0
  29. frogml_storage/models/frogml_entity_type_info.py +50 -0
  30. frogml_storage/models/frogml_entity_version.py +34 -0
  31. frogml_storage/models/frogml_model_version.py +21 -0
  32. frogml_storage/models/model_manifest.py +60 -0
  33. frogml_storage/models/serialization_metadata.py +15 -0
  34. frogml_storage/utils/__init__.py +12 -0
  35. frogml_storage/utils/_environment.py +21 -0
  36. frogml_storage/utils/_input_checks_utility.py +104 -0
  37. frogml_storage/utils/_storage_utils.py +15 -0
  38. frogml_storage/utils/_url_utils.py +27 -0
  39. qwak/__init__.py +1 -1
  40. qwak/clients/instance_template/client.py +6 -4
  41. qwak/clients/prompt_manager/model_descriptor_mapper.py +21 -19
  42. qwak/feature_store/_common/artifact_utils.py +3 -3
  43. qwak/feature_store/data_sources/base.py +4 -4
  44. qwak/feature_store/data_sources/batch/athena.py +3 -3
  45. qwak/feature_store/feature_sets/streaming.py +3 -3
  46. qwak/feature_store/feature_sets/streaming_backfill.py +1 -1
  47. qwak/feature_store/online/client.py +6 -6
  48. qwak/feature_store/sinks/streaming/factory.py +1 -1
  49. qwak/inner/build_logic/phases/phase_010_fetch_model/fetch_strategy_manager/strategy/git/git_strategy.py +3 -3
  50. qwak/inner/di_configuration/account.py +23 -24
  51. qwak/inner/tool/auth.py +2 -2
  52. qwak/llmops/provider/openai/provider.py +3 -3
  53. qwak/model/tools/adapters/output.py +1 -1
  54. qwak/model/utils/feature_utils.py +12 -8
  55. qwak/model_loggers/artifact_logger.py +7 -7
  56. qwak/tools/logger/logger.py +1 -1
  57. qwak_core-0.4.273.dist-info/METADATA +415 -0
  58. {qwak_core-0.4.272.dist-info → qwak_core-0.4.273.dist-info}/RECORD +59 -23
  59. _qwak_proto/__init__.py +0 -0
  60. _qwak_proto/qwak/__init__.py +0 -0
  61. qwak_core-0.4.272.dist-info/METADATA +0 -53
  62. {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
@@ -1,7 +1,7 @@
1
1
  """Top-level package for qwak-core."""
2
2
 
3
3
  __author__ = "Qwak.ai"
4
- __version__ = "0.4.272"
4
+ __version__ = "0.4.273"
5
5
 
6
6
  from qwak.inner.di_configuration import wire_dependencies
7
7
  from qwak.model.experiment_tracking import log_metric, log_param
@@ -49,10 +49,12 @@ class InstanceTemplateManagementClient:
49
49
 
50
50
  def list_instance_templates(self) -> List[InstanceTemplateSpec]:
51
51
  try:
52
- result: ListInstanceTemplatesResponse = self._instance_template_service.ListInstanceTemplates(
53
- ListInstanceTemplatesRequest(
54
- optional_instance_filter=InstanceFilter(
55
- instance_type_filter=InstanceTypeFilter.INSTANCE_TYPE_FILTER_ALL
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
- str, ChatCompletionNamedToolChoiceParam
49
- ] = ModelDescriptorMapper._from_tool_choice(
50
- openai_chat_params=openai_chat_params
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=p.frequency_penalty
61
- if p.HasField("frequency_penalty")
62
- else None,
63
- logit_bias={k: int(v) for k, v in p.logit_bias.items()}
64
- if p.HasField("logit_bias")
65
- else None,
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=p.presence_penalty
70
- if p.HasField("presence_penalty")
71
- else None,
72
- response_format=p.response_format
73
- if p.HasField("response_format")
74
- else None, # noqa
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=d.response_format
154
- if d.response_format
155
- else None, # noqa
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
- List[Callable[..., Any]]
50
- ] = transformation.get_functions()
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: str = (
67
- FeatureRegistryClient().get_datasource_source_code_presign_url(
68
- ds_name=self.name
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
- TimePartitionColumns
123
- ] = AthenaSource._extract_partition_column(proto_athena_source)
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
- ] = StreamingBackfill.get_streaming_backfill_from_function(function=function)
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
- Tuple[RequestedEntitiesMatrix, pd.DataFrame]
187
- ] = OnlineClient._split_entities(
188
- entity_names=ordered_entities,
189
- population_df=df,
190
- max_entities_per_split=max_keys_per_request,
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
- "GIT_SSH_COMMAND"
168
- ] = f"ssh -i {ssh_key_file_path} -o StrictHostKeyChecking=no"
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.cli._login_cli import login as frogml_login
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._authentication_utils import (
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._auth.read(self._auth_file)
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._authentication_utils import get_credentials
9
- from frogml_storage.authentication.models._auth_config import AuthConfig
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
- OpenAIApiKeySystemSecret
31
- ] = IntegrationUtils().get_openai_api_keys()
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 type(return_result) == list and len(return_result) > 0
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
- cast(
34
- FeatureStoreInput,
35
- dataclasses.replace(
36
- feature,
37
- name=validate_and_sanitize_feature_name(feature.name, current_env_name),
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: GetBuildVersioningDownloadURLResponse = (
96
- BuildOrchestratorClient().get_build_versioning_download_url(
97
- build_id=build_id,
98
- model_id=model_id,
99
- tag=tag,
100
- tag_type=BuildVersioningTagsType.FILE_TAG_TYPE,
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:
@@ -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 type(existing_handler) != RotatingFileHandler:
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
  )