salesforce-data-customcode 6.0.4.dev3__py3-none-any.whl → 6.0.6.dev1__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.
- datacustomcode/__init__.py +15 -0
- datacustomcode/client.py +116 -0
- datacustomcode/config.yaml +3 -0
- datacustomcode/einstein_predictions/__init__.py +8 -0
- datacustomcode/einstein_predictions/errors.py +36 -0
- datacustomcode/einstein_predictions/spark_base.py +72 -0
- datacustomcode/einstein_predictions/spark_default.py +239 -0
- datacustomcode/einstein_predictions_config.py +45 -1
- datacustomcode/templates/script/payload/entrypoint.py +17 -0
- {salesforce_data_customcode-6.0.4.dev3.dist-info → salesforce_data_customcode-6.0.6.dev1.dist-info}/METADATA +57 -6
- {salesforce_data_customcode-6.0.4.dev3.dist-info → salesforce_data_customcode-6.0.6.dev1.dist-info}/RECORD +14 -11
- {salesforce_data_customcode-6.0.4.dev3.dist-info → salesforce_data_customcode-6.0.6.dev1.dist-info}/WHEEL +0 -0
- {salesforce_data_customcode-6.0.4.dev3.dist-info → salesforce_data_customcode-6.0.6.dev1.dist-info}/entry_points.txt +0 -0
- {salesforce_data_customcode-6.0.4.dev3.dist-info → salesforce_data_customcode-6.0.6.dev1.dist-info}/licenses/LICENSE.txt +0 -0
datacustomcode/__init__.py
CHANGED
|
@@ -17,10 +17,13 @@ __all__ = [
|
|
|
17
17
|
"AuthType",
|
|
18
18
|
"Client",
|
|
19
19
|
"Credentials",
|
|
20
|
+
"DefaultSparkEinsteinPredictions",
|
|
20
21
|
"DefaultSparkLLMGateway",
|
|
21
22
|
"PrintDataCloudWriter",
|
|
22
23
|
"QueryAPIDataCloudReader",
|
|
24
|
+
"SparkEinsteinPredictions",
|
|
23
25
|
"SparkLLMGateway",
|
|
26
|
+
"einstein_predict_col",
|
|
24
27
|
"llm_gateway_generate_text_col",
|
|
25
28
|
]
|
|
26
29
|
|
|
@@ -59,4 +62,16 @@ def __getattr__(name: str):
|
|
|
59
62
|
from datacustomcode.client import llm_gateway_generate_text_col
|
|
60
63
|
|
|
61
64
|
return llm_gateway_generate_text_col
|
|
65
|
+
elif name == "SparkEinsteinPredictions":
|
|
66
|
+
from datacustomcode.einstein_predictions import SparkEinsteinPredictions
|
|
67
|
+
|
|
68
|
+
return SparkEinsteinPredictions
|
|
69
|
+
elif name == "DefaultSparkEinsteinPredictions":
|
|
70
|
+
from datacustomcode.einstein_predictions import DefaultSparkEinsteinPredictions
|
|
71
|
+
|
|
72
|
+
return DefaultSparkEinsteinPredictions
|
|
73
|
+
elif name == "einstein_predict_col":
|
|
74
|
+
from datacustomcode.client import einstein_predict_col
|
|
75
|
+
|
|
76
|
+
return einstein_predict_col
|
|
62
77
|
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
|
datacustomcode/client.py
CHANGED
|
@@ -17,6 +17,7 @@ from __future__ import annotations
|
|
|
17
17
|
from enum import Enum
|
|
18
18
|
from typing import (
|
|
19
19
|
TYPE_CHECKING,
|
|
20
|
+
Any,
|
|
20
21
|
ClassVar,
|
|
21
22
|
Dict,
|
|
22
23
|
Optional,
|
|
@@ -24,6 +25,7 @@ from typing import (
|
|
|
24
25
|
)
|
|
25
26
|
|
|
26
27
|
from datacustomcode.config import config
|
|
28
|
+
from datacustomcode.einstein_predictions_config import spark_einstein_predictions_config
|
|
27
29
|
from datacustomcode.file.path.default import DefaultFindFilePath
|
|
28
30
|
from datacustomcode.io.reader.base import BaseDataCloudReader
|
|
29
31
|
from datacustomcode.llm_gateway_config import spark_llm_gateway_config
|
|
@@ -34,6 +36,8 @@ if TYPE_CHECKING:
|
|
|
34
36
|
|
|
35
37
|
from pyspark.sql import Column, DataFrame as PySparkDataFrame
|
|
36
38
|
|
|
39
|
+
from datacustomcode.einstein_predictions.spark_base import SparkEinsteinPredictions
|
|
40
|
+
from datacustomcode.einstein_predictions.types import PredictionType
|
|
37
41
|
from datacustomcode.io.reader.base import BaseDataCloudReader
|
|
38
42
|
from datacustomcode.io.writer.base import BaseDataCloudWriter, WriteMode
|
|
39
43
|
from datacustomcode.llm_gateway.spark_base import SparkLLMGateway
|
|
@@ -99,6 +103,70 @@ def llm_gateway_generate_text_col(
|
|
|
99
103
|
return gateway.llm_gateway_generate_text_col(template, values, model_id=model_id)
|
|
100
104
|
|
|
101
105
|
|
|
106
|
+
def _build_spark_einstein_predictions() -> "SparkEinsteinPredictions":
|
|
107
|
+
"""Instantiate the SDK-configured :class:`SparkEinsteinPredictions`.
|
|
108
|
+
|
|
109
|
+
Raises:
|
|
110
|
+
RuntimeError: If no ``spark_einstein_predictions_config`` has been loaded.
|
|
111
|
+
"""
|
|
112
|
+
cfg = spark_einstein_predictions_config.spark_einstein_predictions_config
|
|
113
|
+
if cfg is None:
|
|
114
|
+
raise RuntimeError(
|
|
115
|
+
"spark_einstein_predictions_config is not configured. Add a "
|
|
116
|
+
"'spark_einstein_predictions_config' section to config.yaml."
|
|
117
|
+
)
|
|
118
|
+
return cfg.to_object()
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
def einstein_predict_col(
|
|
122
|
+
model_api_name: str,
|
|
123
|
+
prediction_type: "PredictionType",
|
|
124
|
+
features: Dict[str, "Column"],
|
|
125
|
+
settings: Optional[Dict[str, Any]] = None,
|
|
126
|
+
) -> "Column":
|
|
127
|
+
"""Build a Spark Column that runs an Einstein prediction per row.
|
|
128
|
+
|
|
129
|
+
The returned Column yields a struct ``{status, response, error_code,
|
|
130
|
+
error_message}`` for each row. Use ``[...]`` (or ``getField``) to pick the
|
|
131
|
+
field you want, e.g. ``einstein_predict_col(...)["response"]``. ``response``
|
|
132
|
+
holds the prediction response payload as a JSON string. Per-row failures
|
|
133
|
+
populate ``status`` / ``error_code`` / ``error_message`` so a single bad row
|
|
134
|
+
does not abort the whole Spark job.
|
|
135
|
+
|
|
136
|
+
Example:
|
|
137
|
+
|
|
138
|
+
>>> from datacustomcode.einstein_predictions.types import PredictionType
|
|
139
|
+
>>> result = einstein_predict_col(
|
|
140
|
+
... "my_regression_model",
|
|
141
|
+
... PredictionType.REGRESSION,
|
|
142
|
+
... {"square_feet": col("square_feet__c"), "beds": col("beds__c")},
|
|
143
|
+
... )
|
|
144
|
+
>>> df.withColumn("prediction__c", result["response"])
|
|
145
|
+
>>> # …or keep the struct around and inspect failures:
|
|
146
|
+
>>> df.withColumn("pred", result).select(
|
|
147
|
+
... "pred.status", "pred.response", "pred.error_message"
|
|
148
|
+
... )
|
|
149
|
+
|
|
150
|
+
Args:
|
|
151
|
+
model_api_name: API name of the Einstein model to invoke.
|
|
152
|
+
prediction_type: The :class:`PredictionType` of the model.
|
|
153
|
+
features: A mapping from model feature column name to a Spark ``Column``
|
|
154
|
+
supplying that feature's per-row value.
|
|
155
|
+
settings: Optional prediction settings forwarded to the model.
|
|
156
|
+
|
|
157
|
+
Returns:
|
|
158
|
+
A Spark ``Column`` of ``StructType`` with fields ``status``,
|
|
159
|
+
``response``, ``error_code``, and ``error_message`` (all nullable
|
|
160
|
+
strings). On success, ``status == "SUCCESS"`` and ``response`` holds
|
|
161
|
+
the JSON-serialized prediction payload; on failure, ``status ==
|
|
162
|
+
"ERROR"`` and the ``error_*`` fields carry diagnostic detail.
|
|
163
|
+
"""
|
|
164
|
+
predictions = Client()._get_spark_einstein_predictions()
|
|
165
|
+
return predictions.einstein_predict_col(
|
|
166
|
+
model_api_name, prediction_type, features, settings=settings
|
|
167
|
+
)
|
|
168
|
+
|
|
169
|
+
|
|
102
170
|
class DataCloudObjectType(Enum):
|
|
103
171
|
DLO = "dlo"
|
|
104
172
|
DMO = "dmo"
|
|
@@ -158,6 +226,8 @@ class Client:
|
|
|
158
226
|
reader: A custom reader to use for reading Data Cloud objects.
|
|
159
227
|
writer: A custom writer to use for writing Data Cloud objects.
|
|
160
228
|
spark_llm_gateway: Optional custom :class:`SparkLLMGateway`.
|
|
229
|
+
spark_einstein_predictions: Optional custom
|
|
230
|
+
:class:`SparkEinsteinPredictions`.
|
|
161
231
|
|
|
162
232
|
Example:
|
|
163
233
|
>>> client = Client()
|
|
@@ -172,6 +242,7 @@ class Client:
|
|
|
172
242
|
_writer: BaseDataCloudWriter
|
|
173
243
|
_file: DefaultFindFilePath
|
|
174
244
|
_spark_llm_gateway: Optional[SparkLLMGateway]
|
|
245
|
+
_spark_einstein_predictions: Optional[SparkEinsteinPredictions]
|
|
175
246
|
_data_layer_history: dict[DataCloudObjectType, set[str]]
|
|
176
247
|
_code_type: str
|
|
177
248
|
|
|
@@ -181,12 +252,14 @@ class Client:
|
|
|
181
252
|
writer: Optional[BaseDataCloudWriter] = None,
|
|
182
253
|
spark_provider: Optional[BaseSparkSessionProvider] = None,
|
|
183
254
|
spark_llm_gateway: Optional[SparkLLMGateway] = None,
|
|
255
|
+
spark_einstein_predictions: Optional[SparkEinsteinPredictions] = None,
|
|
184
256
|
code_type: str = "script",
|
|
185
257
|
) -> Client:
|
|
186
258
|
|
|
187
259
|
if cls._instance is None:
|
|
188
260
|
cls._instance = super().__new__(cls)
|
|
189
261
|
cls._instance._spark_llm_gateway = spark_llm_gateway
|
|
262
|
+
cls._instance._spark_einstein_predictions = spark_einstein_predictions
|
|
190
263
|
# Initialize Readers and Writers from config
|
|
191
264
|
# and/or provided reader and writer
|
|
192
265
|
if reader is None or writer is None:
|
|
@@ -358,6 +431,49 @@ class Client:
|
|
|
358
431
|
self._spark_llm_gateway = _build_spark_llm_gateway()
|
|
359
432
|
return self._spark_llm_gateway
|
|
360
433
|
|
|
434
|
+
def einstein_predict(
|
|
435
|
+
self,
|
|
436
|
+
model_api_name: str,
|
|
437
|
+
prediction_type: "PredictionType",
|
|
438
|
+
features: Dict[str, Any],
|
|
439
|
+
settings: Optional[Dict[str, Any]] = None,
|
|
440
|
+
) -> Dict[str, Any]:
|
|
441
|
+
"""Issue a one-shot Einstein prediction. This is the scalar counterpart
|
|
442
|
+
to :func:`einstein_predict_col`: it runs **once** — not per row. Use the
|
|
443
|
+
column helper method instead when you want to fan a prediction out
|
|
444
|
+
across every row of a DataFrame.
|
|
445
|
+
|
|
446
|
+
Example:
|
|
447
|
+
|
|
448
|
+
>>> from datacustomcode.einstein_predictions.types import PredictionType
|
|
449
|
+
>>> response = Client().einstein_predict(
|
|
450
|
+
... "my_regression_model",
|
|
451
|
+
... PredictionType.REGRESSION,
|
|
452
|
+
... {"square_feet": 1800, "beds": 3},
|
|
453
|
+
... )
|
|
454
|
+
|
|
455
|
+
Args:
|
|
456
|
+
model_api_name: API name of the Einstein model to invoke.
|
|
457
|
+
prediction_type: The :class:`PredictionType` of the model.
|
|
458
|
+
features: A mapping from model feature column name to a single
|
|
459
|
+
scalar value (``str`` / ``float`` / ``bool``).
|
|
460
|
+
settings: Optional prediction settings forwarded to the model.
|
|
461
|
+
|
|
462
|
+
Returns:
|
|
463
|
+
The prediction response payload as a plain Python ``dict``.
|
|
464
|
+
|
|
465
|
+
Raises:
|
|
466
|
+
EinsteinPredictionsCallError: If the prediction call fails.
|
|
467
|
+
"""
|
|
468
|
+
return self._get_spark_einstein_predictions().einstein_predict(
|
|
469
|
+
model_api_name, prediction_type, features, settings=settings
|
|
470
|
+
)
|
|
471
|
+
|
|
472
|
+
def _get_spark_einstein_predictions(self) -> SparkEinsteinPredictions:
|
|
473
|
+
if self._spark_einstein_predictions is None:
|
|
474
|
+
self._spark_einstein_predictions = _build_spark_einstein_predictions()
|
|
475
|
+
return self._spark_einstein_predictions
|
|
476
|
+
|
|
361
477
|
def _validate_data_layer_history_does_not_contain(
|
|
362
478
|
self, data_cloud_object_type: DataCloudObjectType
|
|
363
479
|
) -> None:
|
datacustomcode/config.yaml
CHANGED
|
@@ -14,9 +14,17 @@
|
|
|
14
14
|
# limitations under the License.
|
|
15
15
|
|
|
16
16
|
from datacustomcode.einstein_predictions.base import EinsteinPredictions
|
|
17
|
+
from datacustomcode.einstein_predictions.errors import EinsteinPredictionsCallError
|
|
17
18
|
from datacustomcode.einstein_predictions.impl.default import DefaultEinsteinPredictions
|
|
19
|
+
from datacustomcode.einstein_predictions.spark_base import SparkEinsteinPredictions
|
|
20
|
+
from datacustomcode.einstein_predictions.spark_default import (
|
|
21
|
+
DefaultSparkEinsteinPredictions,
|
|
22
|
+
)
|
|
18
23
|
|
|
19
24
|
__all__ = [
|
|
20
25
|
"DefaultEinsteinPredictions",
|
|
26
|
+
"DefaultSparkEinsteinPredictions",
|
|
21
27
|
"EinsteinPredictions",
|
|
28
|
+
"EinsteinPredictionsCallError",
|
|
29
|
+
"SparkEinsteinPredictions",
|
|
22
30
|
]
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# Copyright (c) 2025, Salesforce, Inc.
|
|
2
|
+
# SPDX-License-Identifier: Apache-2
|
|
3
|
+
#
|
|
4
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
# you may not use this file except in compliance with the License.
|
|
6
|
+
# You may obtain a copy of the License at
|
|
7
|
+
#
|
|
8
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
#
|
|
10
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
# See the License for the specific language governing permissions and
|
|
14
|
+
# limitations under the License.
|
|
15
|
+
"""Exceptions raised by Einstein Predictions implementations."""
|
|
16
|
+
|
|
17
|
+
from __future__ import annotations
|
|
18
|
+
|
|
19
|
+
from typing import Optional
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class EinsteinPredictionsCallError(RuntimeError):
|
|
23
|
+
"""Raised when an Einstein Predictions call returns an error."""
|
|
24
|
+
|
|
25
|
+
def __init__(
|
|
26
|
+
self,
|
|
27
|
+
message: str,
|
|
28
|
+
*,
|
|
29
|
+
status: Optional[object] = None,
|
|
30
|
+
error_code: Optional[str] = None,
|
|
31
|
+
error_message: Optional[str] = None,
|
|
32
|
+
) -> None:
|
|
33
|
+
super().__init__(message)
|
|
34
|
+
self.status = status
|
|
35
|
+
self.error_code = error_code
|
|
36
|
+
self.error_message = error_message
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
# Copyright (c) 2025, Salesforce, Inc.
|
|
2
|
+
# SPDX-License-Identifier: Apache-2
|
|
3
|
+
#
|
|
4
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
# you may not use this file except in compliance with the License.
|
|
6
|
+
# You may obtain a copy of the License at
|
|
7
|
+
#
|
|
8
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
#
|
|
10
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
# See the License for the specific language governing permissions and
|
|
14
|
+
# limitations under the License.
|
|
15
|
+
from __future__ import annotations
|
|
16
|
+
|
|
17
|
+
from abc import ABC, abstractmethod
|
|
18
|
+
from typing import (
|
|
19
|
+
TYPE_CHECKING,
|
|
20
|
+
Any,
|
|
21
|
+
Dict,
|
|
22
|
+
Optional,
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
from datacustomcode.mixin import UserExtendableNamedConfigMixin
|
|
26
|
+
|
|
27
|
+
if TYPE_CHECKING:
|
|
28
|
+
from pyspark.sql import Column
|
|
29
|
+
|
|
30
|
+
from datacustomcode.einstein_predictions.types import PredictionType
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class SparkEinsteinPredictions(ABC, UserExtendableNamedConfigMixin):
|
|
34
|
+
CONFIG_NAME: str
|
|
35
|
+
|
|
36
|
+
def __init__(self, **kwargs: Any) -> None:
|
|
37
|
+
pass
|
|
38
|
+
|
|
39
|
+
@abstractmethod
|
|
40
|
+
def einstein_predict(
|
|
41
|
+
self,
|
|
42
|
+
model_api_name: str,
|
|
43
|
+
prediction_type: PredictionType,
|
|
44
|
+
features: Dict[str, Any],
|
|
45
|
+
settings: Optional[Dict[str, Any]] = None,
|
|
46
|
+
) -> Dict[str, Any]:
|
|
47
|
+
"""Issue a one-shot Einstein prediction and return the response data.
|
|
48
|
+
|
|
49
|
+
``features`` maps each model feature column name to a single scalar
|
|
50
|
+
value (``str``/``float``/``bool``). The value is wrapped into a
|
|
51
|
+
single-element prediction column of the appropriate type.
|
|
52
|
+
"""
|
|
53
|
+
|
|
54
|
+
@abstractmethod
|
|
55
|
+
def einstein_predict_col(
|
|
56
|
+
self,
|
|
57
|
+
model_api_name: str,
|
|
58
|
+
prediction_type: PredictionType,
|
|
59
|
+
features: Dict[str, "Column"],
|
|
60
|
+
settings: Optional[Dict[str, Any]] = None,
|
|
61
|
+
) -> "Column":
|
|
62
|
+
"""Build a Spark ``Column`` that invokes Einstein predict per row and
|
|
63
|
+
yields a struct ``{status, response, error_code, error_message}``.
|
|
64
|
+
|
|
65
|
+
``features`` maps each model feature column name to a Spark ``Column``
|
|
66
|
+
supplying that feature's per-row value. Select an individual field,
|
|
67
|
+
e.g. ``einstein_predict_col(...)["response"]``. ``response`` holds the
|
|
68
|
+
prediction response payload as a JSON string. Returning a struct means
|
|
69
|
+
a single failing row leaves the rest of the DataFrame intact — callers
|
|
70
|
+
can inspect ``status`` / ``error_code`` per row instead of having the
|
|
71
|
+
Spark job abort.
|
|
72
|
+
"""
|
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
# Copyright (c) 2025, Salesforce, Inc.
|
|
2
|
+
# SPDX-License-Identifier: Apache-2
|
|
3
|
+
#
|
|
4
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
# you may not use this file except in compliance with the License.
|
|
6
|
+
# You may obtain a copy of the License at
|
|
7
|
+
#
|
|
8
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
#
|
|
10
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
# See the License for the specific language governing permissions and
|
|
14
|
+
# limitations under the License.
|
|
15
|
+
from __future__ import annotations
|
|
16
|
+
|
|
17
|
+
import json
|
|
18
|
+
from typing import (
|
|
19
|
+
TYPE_CHECKING,
|
|
20
|
+
Any,
|
|
21
|
+
Dict,
|
|
22
|
+
Optional,
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
from datacustomcode.einstein_predictions.spark_base import SparkEinsteinPredictions
|
|
26
|
+
from datacustomcode.einstein_predictions.types import (
|
|
27
|
+
PredictionColumn,
|
|
28
|
+
PredictionRequest,
|
|
29
|
+
PredictionType,
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
if TYPE_CHECKING:
|
|
33
|
+
from pyspark.sql import Column
|
|
34
|
+
|
|
35
|
+
from datacustomcode.einstein_predictions.base import EinsteinPredictions
|
|
36
|
+
from datacustomcode.einstein_predictions.types import PredictionResponse
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
_STATUS_SUCCESS = "SUCCESS"
|
|
40
|
+
_STATUS_ERROR = "ERROR"
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
class DefaultSparkEinsteinPredictions(SparkEinsteinPredictions):
|
|
44
|
+
|
|
45
|
+
CONFIG_NAME = "DefaultSparkEinsteinPredictions"
|
|
46
|
+
|
|
47
|
+
def __init__(
|
|
48
|
+
self,
|
|
49
|
+
einstein_predictions: Optional["EinsteinPredictions"] = None,
|
|
50
|
+
**kwargs: Any,
|
|
51
|
+
) -> None:
|
|
52
|
+
super().__init__(**kwargs)
|
|
53
|
+
if einstein_predictions is None:
|
|
54
|
+
einstein_predictions = _build_underlying_predictions()
|
|
55
|
+
self._einstein_predictions: "EinsteinPredictions" = einstein_predictions
|
|
56
|
+
|
|
57
|
+
def einstein_predict(
|
|
58
|
+
self,
|
|
59
|
+
model_api_name: str,
|
|
60
|
+
prediction_type: PredictionType,
|
|
61
|
+
features: Dict[str, Any],
|
|
62
|
+
settings: Optional[Dict[str, Any]] = None,
|
|
63
|
+
) -> Dict[str, Any]:
|
|
64
|
+
return _invoke_predictions(
|
|
65
|
+
self._einstein_predictions,
|
|
66
|
+
model_api_name,
|
|
67
|
+
prediction_type,
|
|
68
|
+
features,
|
|
69
|
+
settings,
|
|
70
|
+
)
|
|
71
|
+
|
|
72
|
+
def einstein_predict_col(
|
|
73
|
+
self,
|
|
74
|
+
model_api_name: str,
|
|
75
|
+
prediction_type: PredictionType,
|
|
76
|
+
features: Dict[str, "Column"],
|
|
77
|
+
settings: Optional[Dict[str, Any]] = None,
|
|
78
|
+
) -> "Column":
|
|
79
|
+
"""Build a per-row UDF that returns a struct ``{status, response,
|
|
80
|
+
error_code, error_message}`` so per-row failures do not abort the
|
|
81
|
+
Spark job. Callers select the field they want, e.g.
|
|
82
|
+
``einstein_predict_col(...)["response"]``. ``response`` carries the
|
|
83
|
+
prediction response payload serialized as a JSON string.
|
|
84
|
+
"""
|
|
85
|
+
from pyspark.sql.functions import struct, udf
|
|
86
|
+
from pyspark.sql.types import (
|
|
87
|
+
StringType,
|
|
88
|
+
StructField,
|
|
89
|
+
StructType,
|
|
90
|
+
)
|
|
91
|
+
|
|
92
|
+
feature_names = list(features.keys())
|
|
93
|
+
values_col = struct(*[features[name].alias(name) for name in feature_names])
|
|
94
|
+
|
|
95
|
+
predictions = self._einstein_predictions
|
|
96
|
+
result_schema = StructType(
|
|
97
|
+
[
|
|
98
|
+
StructField("status", StringType(), True),
|
|
99
|
+
StructField("response", StringType(), True),
|
|
100
|
+
StructField("error_code", StringType(), True),
|
|
101
|
+
StructField("error_message", StringType(), True),
|
|
102
|
+
]
|
|
103
|
+
)
|
|
104
|
+
|
|
105
|
+
def _predict(values_row: Any) -> Dict[str, Optional[str]]:
|
|
106
|
+
if values_row is None:
|
|
107
|
+
return {
|
|
108
|
+
"status": _STATUS_ERROR,
|
|
109
|
+
"response": None,
|
|
110
|
+
"error_code": None,
|
|
111
|
+
"error_message": "features column was null for this row",
|
|
112
|
+
}
|
|
113
|
+
row_features = (
|
|
114
|
+
values_row.asDict()
|
|
115
|
+
if hasattr(values_row, "asDict")
|
|
116
|
+
else dict(values_row)
|
|
117
|
+
)
|
|
118
|
+
return _invoke_predictions_as_struct(
|
|
119
|
+
predictions,
|
|
120
|
+
model_api_name,
|
|
121
|
+
prediction_type,
|
|
122
|
+
row_features,
|
|
123
|
+
settings,
|
|
124
|
+
)
|
|
125
|
+
|
|
126
|
+
return udf(_predict, result_schema)(values_col)
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
def _build_underlying_predictions() -> "EinsteinPredictions":
|
|
130
|
+
from datacustomcode.einstein_predictions_config import einstein_predictions_config
|
|
131
|
+
|
|
132
|
+
cfg = einstein_predictions_config.einstein_predictions_config
|
|
133
|
+
if cfg is None:
|
|
134
|
+
raise RuntimeError(
|
|
135
|
+
"einstein_predictions_config is not configured. Add an "
|
|
136
|
+
"'einstein_predictions_config' section to config.yaml."
|
|
137
|
+
)
|
|
138
|
+
return cfg.to_object()
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
def _feature_to_prediction_column(name: str, value: Any) -> PredictionColumn:
|
|
142
|
+
"""Wrap a single scalar feature value into a one-element prediction column.
|
|
143
|
+
|
|
144
|
+
The value's Python type selects the prediction column value type. ``bool``
|
|
145
|
+
is checked before ``int``/``float`` because ``bool`` is a subclass of
|
|
146
|
+
``int`` in Python.
|
|
147
|
+
"""
|
|
148
|
+
if isinstance(value, bool):
|
|
149
|
+
return PredictionColumn(column_name=name, boolean_values=[value])
|
|
150
|
+
if isinstance(value, (int, float)):
|
|
151
|
+
return PredictionColumn(column_name=name, double_values=[float(value)])
|
|
152
|
+
return PredictionColumn(column_name=name, string_values=[str(value)])
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
def _build_request(
|
|
156
|
+
model_api_name: str,
|
|
157
|
+
prediction_type: PredictionType,
|
|
158
|
+
features: Dict[str, Any],
|
|
159
|
+
settings: Optional[Dict[str, Any]],
|
|
160
|
+
) -> PredictionRequest:
|
|
161
|
+
prediction_columns = [
|
|
162
|
+
_feature_to_prediction_column(name, value) for name, value in features.items()
|
|
163
|
+
]
|
|
164
|
+
return PredictionRequest(
|
|
165
|
+
prediction_type=prediction_type,
|
|
166
|
+
model_api_name=model_api_name,
|
|
167
|
+
prediction_columns=prediction_columns,
|
|
168
|
+
settings=settings,
|
|
169
|
+
)
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
def _call_predictions(
|
|
173
|
+
predictions: "EinsteinPredictions",
|
|
174
|
+
model_api_name: str,
|
|
175
|
+
prediction_type: PredictionType,
|
|
176
|
+
features: Dict[str, Any],
|
|
177
|
+
settings: Optional[Dict[str, Any]],
|
|
178
|
+
) -> "PredictionResponse":
|
|
179
|
+
"""Build the request and dispatch it to the underlying predictions resource."""
|
|
180
|
+
request = _build_request(model_api_name, prediction_type, features, settings)
|
|
181
|
+
return predictions.predict(request)
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
def _invoke_predictions(
|
|
185
|
+
predictions: "EinsteinPredictions",
|
|
186
|
+
model_api_name: str,
|
|
187
|
+
prediction_type: PredictionType,
|
|
188
|
+
features: Dict[str, Any],
|
|
189
|
+
settings: Optional[Dict[str, Any]],
|
|
190
|
+
) -> Dict[str, Any]:
|
|
191
|
+
from datacustomcode.einstein_predictions.errors import EinsteinPredictionsCallError
|
|
192
|
+
|
|
193
|
+
response = _call_predictions(
|
|
194
|
+
predictions, model_api_name, prediction_type, features, settings
|
|
195
|
+
)
|
|
196
|
+
if not response.is_success:
|
|
197
|
+
error_code = _extract_error_code(response)
|
|
198
|
+
raise EinsteinPredictionsCallError(
|
|
199
|
+
f"Einstein Predictions call failed: "
|
|
200
|
+
f"status_code={response.status_code}, "
|
|
201
|
+
f"error_code={error_code!r}, message={response.data!r}",
|
|
202
|
+
status=response.status_code,
|
|
203
|
+
error_code=error_code,
|
|
204
|
+
error_message=str(response.data) if response.data else None,
|
|
205
|
+
)
|
|
206
|
+
return response.data or {}
|
|
207
|
+
|
|
208
|
+
|
|
209
|
+
def _invoke_predictions_as_struct(
|
|
210
|
+
predictions: "EinsteinPredictions",
|
|
211
|
+
model_api_name: str,
|
|
212
|
+
prediction_type: PredictionType,
|
|
213
|
+
features: Dict[str, Any],
|
|
214
|
+
settings: Optional[Dict[str, Any]],
|
|
215
|
+
) -> Dict[str, Optional[str]]:
|
|
216
|
+
response = _call_predictions(
|
|
217
|
+
predictions, model_api_name, prediction_type, features, settings
|
|
218
|
+
)
|
|
219
|
+
if not response.is_success:
|
|
220
|
+
return {
|
|
221
|
+
"status": _STATUS_ERROR,
|
|
222
|
+
"response": None,
|
|
223
|
+
"error_code": _extract_error_code(response),
|
|
224
|
+
"error_message": str(response.data) if response.data else None,
|
|
225
|
+
}
|
|
226
|
+
return {
|
|
227
|
+
"status": _STATUS_SUCCESS,
|
|
228
|
+
"response": json.dumps(response.data) if response.data is not None else None,
|
|
229
|
+
"error_code": None,
|
|
230
|
+
"error_message": None,
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
|
|
234
|
+
def _extract_error_code(response: "PredictionResponse") -> Optional[str]:
|
|
235
|
+
if response.data:
|
|
236
|
+
error_code = response.data.get("errorCode")
|
|
237
|
+
if error_code is not None:
|
|
238
|
+
return str(error_code)
|
|
239
|
+
return None
|
|
@@ -21,11 +21,17 @@ from typing import (
|
|
|
21
21
|
Union,
|
|
22
22
|
)
|
|
23
23
|
|
|
24
|
-
from datacustomcode.common_config import
|
|
24
|
+
from datacustomcode.common_config import (
|
|
25
|
+
BaseConfig,
|
|
26
|
+
BaseObjectConfig,
|
|
27
|
+
default_config_file,
|
|
28
|
+
)
|
|
25
29
|
from datacustomcode.einstein_platform_config import CredentialsObjectConfig
|
|
26
30
|
from datacustomcode.einstein_predictions.base import EinsteinPredictions
|
|
31
|
+
from datacustomcode.einstein_predictions.spark_base import SparkEinsteinPredictions
|
|
27
32
|
|
|
28
33
|
_E = TypeVar("_E", bound=EinsteinPredictions)
|
|
34
|
+
_S = TypeVar("_S", bound=SparkEinsteinPredictions)
|
|
29
35
|
|
|
30
36
|
|
|
31
37
|
class EinsteinPredictionsObjectConfig(CredentialsObjectConfig[_E], Generic[_E]):
|
|
@@ -54,6 +60,44 @@ class EinsteinPredictionsConfig(BaseConfig):
|
|
|
54
60
|
return self
|
|
55
61
|
|
|
56
62
|
|
|
63
|
+
class SparkEinsteinPredictionsObjectConfig(BaseObjectConfig, Generic[_S]):
|
|
64
|
+
type_to_create: ClassVar[Type[SparkEinsteinPredictions]] = SparkEinsteinPredictions # type: ignore[type-abstract]
|
|
65
|
+
|
|
66
|
+
def to_object(self) -> SparkEinsteinPredictions:
|
|
67
|
+
type_ = self.type_to_create.subclass_from_config_name(self.type_config_name)
|
|
68
|
+
return type_(**self.options)
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
class SparkEinsteinPredictionsConfig(BaseConfig):
|
|
72
|
+
spark_einstein_predictions_config: Union[
|
|
73
|
+
SparkEinsteinPredictionsObjectConfig[SparkEinsteinPredictions], None
|
|
74
|
+
] = None
|
|
75
|
+
|
|
76
|
+
def update(
|
|
77
|
+
self, other: "SparkEinsteinPredictionsConfig"
|
|
78
|
+
) -> "SparkEinsteinPredictionsConfig":
|
|
79
|
+
def merge(
|
|
80
|
+
config_a: Union[SparkEinsteinPredictionsObjectConfig, None],
|
|
81
|
+
config_b: Union[SparkEinsteinPredictionsObjectConfig, None],
|
|
82
|
+
) -> Union[SparkEinsteinPredictionsObjectConfig, None]:
|
|
83
|
+
if config_a is not None and config_a.force:
|
|
84
|
+
return config_a
|
|
85
|
+
if config_b:
|
|
86
|
+
return config_b
|
|
87
|
+
return config_a
|
|
88
|
+
|
|
89
|
+
self.spark_einstein_predictions_config = merge(
|
|
90
|
+
self.spark_einstein_predictions_config,
|
|
91
|
+
other.spark_einstein_predictions_config,
|
|
92
|
+
)
|
|
93
|
+
return self
|
|
94
|
+
|
|
95
|
+
|
|
57
96
|
# Global Einstein Predictions config instance
|
|
58
97
|
einstein_predictions_config = EinsteinPredictionsConfig()
|
|
59
98
|
einstein_predictions_config.load(default_config_file())
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
# Global Spark Einstein Predictions config instance
|
|
102
|
+
spark_einstein_predictions_config = SparkEinsteinPredictionsConfig()
|
|
103
|
+
spark_einstein_predictions_config.load(default_config_file())
|
|
@@ -38,6 +38,23 @@ def main():
|
|
|
38
38
|
>>> generated_text = client.llm_gateway_generate_text(
|
|
39
39
|
... prompt, model_id
|
|
40
40
|
... )
|
|
41
|
+
|
|
42
|
+
You can also score rows with an Einstein Studio model. The per-row helper
|
|
43
|
+
returns the same ``{status, response, error_code, error_message}`` struct,
|
|
44
|
+
where ``response`` is the prediction payload as a JSON string.
|
|
45
|
+
|
|
46
|
+
Example:
|
|
47
|
+
|
|
48
|
+
>>> from datacustomcode.client import einstein_predict_col
|
|
49
|
+
>>> from datacustomcode.einstein_predictions.types import PredictionType
|
|
50
|
+
df_scored = df.withColumn(
|
|
51
|
+
... "prediction__c",
|
|
52
|
+
... einstein_predict_col(
|
|
53
|
+
... "my_regression_model",
|
|
54
|
+
... PredictionType.REGRESSION,
|
|
55
|
+
... {"beds": col("beds__c"), "baths": col("baths__c")},
|
|
56
|
+
... )["response"],
|
|
57
|
+
... )
|
|
41
58
|
"""
|
|
42
59
|
|
|
43
60
|
# Drop specific columns related to relationships
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: salesforce-data-customcode
|
|
3
|
-
Version: 6.0.
|
|
3
|
+
Version: 6.0.6.dev1
|
|
4
4
|
Summary: Data Cloud Custom Code SDK
|
|
5
5
|
License-Expression: Apache-2.0
|
|
6
6
|
License-File: LICENSE.txt
|
|
7
7
|
Requires-Python: >=3.10,<3.12
|
|
8
|
-
Classifier: Development Status ::
|
|
8
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
9
9
|
Classifier: Operating System :: Unix
|
|
10
10
|
Classifier: Programming Language :: Python
|
|
11
11
|
Classifier: Programming Language :: Python :: 3
|
|
@@ -23,7 +23,7 @@ Requires-Dist: salesforce-cdp-connector (>=1.0.19)
|
|
|
23
23
|
Requires-Dist: setuptools_scm (>=7.1.0,<8.0.0)
|
|
24
24
|
Description-Content-Type: text/markdown
|
|
25
25
|
|
|
26
|
-
# Data Cloud Custom Code SDK
|
|
26
|
+
# Data Cloud Custom Code SDK
|
|
27
27
|
|
|
28
28
|
This package provides a development kit for creating custom data transformations in [Data Cloud](https://www.salesforce.com/data/). It allows you to write your own data processing logic in Python while leveraging Data Cloud's infrastructure for data access and running data transformations, mapping execution into Data Cloud data structures like [Data Model Objects](https://help.salesforce.com/s/articleView?id=data.c360_a_data_model_objects.htm&type=5) and [Data Lake Objects](https://help.salesforce.com/s/articleView?id=sf.c360_a_data_lake_objects.htm&language=en_US&type=5).
|
|
29
29
|
|
|
@@ -36,7 +36,7 @@ Use of this project with Salesforce is subject to the [TERMS OF USE](./TERMS_OF_
|
|
|
36
36
|
- **Python 3.11 only** (currently supported version - if your system version is different, we recommend using [pyenv](https://github.com/pyenv/pyenv) to configure 3.11)
|
|
37
37
|
- JDK 17
|
|
38
38
|
- Docker support like [Docker Desktop](https://docs.docker.com/desktop/)
|
|
39
|
-
- A salesforce org with some DLOs or DMOs with data and this feature enabled
|
|
39
|
+
- A salesforce org with some DLOs or DMOs with data and this feature enabled
|
|
40
40
|
- **One of the following** for authentication:
|
|
41
41
|
- A Salesforce org already authenticated via the [Salesforce CLI](https://developer.salesforce.com/tools/salesforcecli)
|
|
42
42
|
(simplest — no External Client App needed)
|
|
@@ -116,7 +116,7 @@ After modifying the `entrypoint.py` as needed, using any dependencies you add in
|
|
|
116
116
|
```zsh
|
|
117
117
|
cd my_package
|
|
118
118
|
datacustomcode scan ./payload/entrypoint.py
|
|
119
|
-
datacustomcode deploy --path ./payload --name my_custom_script --cpu-size CPU_L
|
|
119
|
+
datacustomcode deploy --path ./payload --name my_custom_script --cpu-size CPU_L --sf-cli-org myorg
|
|
120
120
|
```
|
|
121
121
|
|
|
122
122
|
> [!TIP]
|
|
@@ -345,6 +345,7 @@ Options:
|
|
|
345
345
|
- `--description TEXT`: Description of the transformation job (default: "")
|
|
346
346
|
- `--network TEXT`: docker network (default: "default")
|
|
347
347
|
- `--cpu-size TEXT`: CPU size for the deployment (default: `CPU_2XL`). Available options: CPU_L(Large), CPU_XL(Extra Large), CPU_2XL(2X Large), CPU_4XL(4X Large)
|
|
348
|
+
- `--sf-cli-org TEXT`: Salesforce CLI org alias or username (e.g. `myorg`). Fetches credentials via `sf org display` — no `datacustomcode configure` step needed. Takes precedence over `--profile` if both are supplied.
|
|
348
349
|
- `--function-invoke-opt TEXT`: Currently we support only `UnstructuredChunking` for functions.
|
|
349
350
|
|
|
350
351
|
|
|
@@ -397,6 +398,56 @@ datacustomcode run ./payload/entrypoint.py --sf-cli-org myorg
|
|
|
397
398
|
```
|
|
398
399
|
|
|
399
400
|
|
|
401
|
+
## Testing Einstein Predictions
|
|
402
|
+
|
|
403
|
+
You can use AI models configured in Einstein Studio to score your data while
|
|
404
|
+
transforming it. As with the LLM Gateway, there are two flavors: a one-shot
|
|
405
|
+
scalar call (`client.einstein_predict`) and a per-row column helper
|
|
406
|
+
(`einstein_predict_col`). Below is a sample code example:
|
|
407
|
+
|
|
408
|
+
```
|
|
409
|
+
from datacustomcode.client import Client, einstein_predict_col
|
|
410
|
+
from datacustomcode.einstein_predictions.types import PredictionType
|
|
411
|
+
|
|
412
|
+
|
|
413
|
+
def main():
|
|
414
|
+
client = Client()
|
|
415
|
+
df = client.read_dlo("Input__dll")
|
|
416
|
+
# einstein_predict_col returns a struct
|
|
417
|
+
# {status, response, error_code, error_message} per row, so per-row
|
|
418
|
+
# failures don't abort the Spark job. `response` is the prediction
|
|
419
|
+
# payload as a JSON string. Pick the field you want with [].
|
|
420
|
+
df_scored = df.withColumn(
|
|
421
|
+
"prediction__c",
|
|
422
|
+
einstein_predict_col(
|
|
423
|
+
"my_regression_model", # An AI model in your org
|
|
424
|
+
PredictionType.REGRESSION,
|
|
425
|
+
{"square_feet": col("square_feet__c"), "beds": col("beds__c")},
|
|
426
|
+
)["response"],
|
|
427
|
+
)
|
|
428
|
+
|
|
429
|
+
dlo_name = "Output_dll"
|
|
430
|
+
client.write_to_dlo(dlo_name, df_scored, write_mode=WriteMode.APPEND)
|
|
431
|
+
|
|
432
|
+
# One-shot scalar prediction returns the response payload as a dict
|
|
433
|
+
prediction = client.einstein_predict(
|
|
434
|
+
"my_regression_model",
|
|
435
|
+
PredictionType.REGRESSION,
|
|
436
|
+
{"square_feet": 1800, "beds": 3},
|
|
437
|
+
)
|
|
438
|
+
|
|
439
|
+
if __name__ == "__main__":
|
|
440
|
+
main()
|
|
441
|
+
```
|
|
442
|
+
|
|
443
|
+
Testing this code locally uses the same External Client App setup described in
|
|
444
|
+
[Testing LLM Gateway](#testing-llm-gateway). Once your `myorg` alias is set up,
|
|
445
|
+
run:
|
|
446
|
+
```
|
|
447
|
+
datacustomcode run ./payload/entrypoint.py --sf-cli-org myorg
|
|
448
|
+
```
|
|
449
|
+
|
|
450
|
+
|
|
400
451
|
## Docker usage
|
|
401
452
|
|
|
402
453
|
The SDK provides Docker-based development options that allow you to test your code in an environment that closely resembles Data Cloud's execution environment.
|
|
@@ -508,7 +559,7 @@ sf --version
|
|
|
508
559
|
**Browser-based (recommended for developer orgs and sandboxes):**
|
|
509
560
|
```zsh
|
|
510
561
|
# Production / Developer Edition
|
|
511
|
-
sf org login web --alias myorg
|
|
562
|
+
sf org login web --alias myorg --instance-url <your-instance-url>
|
|
512
563
|
|
|
513
564
|
# Sandbox
|
|
514
565
|
sf org login web --alias mysandbox --instance-url https://test.salesforce.com
|
|
@@ -1,21 +1,24 @@
|
|
|
1
|
-
datacustomcode/__init__.py,sha256=
|
|
1
|
+
datacustomcode/__init__.py,sha256=SGsDnWDTihWkLNnWdqvH08zklQgxykar73JAcPL2plI,2666
|
|
2
2
|
datacustomcode/auth.py,sha256=fpSjhIBdv9trC8yq2vuljAix_Euu-4Ah7HDCGhYjOxI,8309
|
|
3
3
|
datacustomcode/cli.py,sha256=i7hPoQqJskTmx3Odz2wVgkxBnf_PFaIawI12FXhaRUE,13264
|
|
4
|
-
datacustomcode/client.py,sha256=
|
|
4
|
+
datacustomcode/client.py,sha256=x0JN_hHXfpEdjHeINQEdUQt-as3j17v6uoDJHjfLe-Y,20241
|
|
5
5
|
datacustomcode/cmd.py,sha256=ZMs46aydJw2EaU26JgCtZmnqESQFHvvaJz10hnjZTBk,3537
|
|
6
6
|
datacustomcode/common_config.py,sha256=SAUnxj3kqmOeWwPmFoYq4tuxokMgURVm4QwcGi-avL4,1928
|
|
7
7
|
datacustomcode/config.py,sha256=2Pk61ieQsEWSxKDxlS66_rliKE8iX0j-9LweEm0aaGo,4072
|
|
8
|
-
datacustomcode/config.yaml,sha256=
|
|
8
|
+
datacustomcode/config.yaml,sha256=ldssmQYPghmYSSPaelm3F2mC99njavhrWsINRSzWymM,933
|
|
9
9
|
datacustomcode/constants.py,sha256=SskQpUPnQE7LjKh4Z3AdZ2p191_0Kf73jV8gdBXDPLU,1436
|
|
10
10
|
datacustomcode/credentials.py,sha256=D-7Zd3Nh_wStdj8_wUy5cnC8M_mdbfBijhIAi-2EbXs,9467
|
|
11
11
|
datacustomcode/deploy.py,sha256=TZ6L4VrAnL-SlmAUMbEYguVpCgOm_Ncv8NdaBR3LX4Y,21906
|
|
12
12
|
datacustomcode/einstein_platform_client.py,sha256=ON2B_m--vdbCVVMVcLc81T-BRZ5-UuxVCer8E6OEQPA,4083
|
|
13
13
|
datacustomcode/einstein_platform_config.py,sha256=6lb_FRbEzdt5a8-2cR15f13ZKw674uvRJ4Xq0o_pQXE,1490
|
|
14
|
-
datacustomcode/einstein_predictions/__init__.py,sha256=
|
|
14
|
+
datacustomcode/einstein_predictions/__init__.py,sha256=_RIDTqcEHDRu6AsCsj9lRBykNkHI8Ch8JBILL1ioPtA,1237
|
|
15
15
|
datacustomcode/einstein_predictions/base.py,sha256=OBV445dpZRtCF5cpdHH49w_6RU_jZY1A-e7VtHTQCFU,1061
|
|
16
|
+
datacustomcode/einstein_predictions/errors.py,sha256=EoxVU5Mf14AIAyVBedI1JnbsS_o853kY-iMA1u-KPcw,1224
|
|
16
17
|
datacustomcode/einstein_predictions/impl/default.py,sha256=fEFhrosozhG0yT87w4Gu8wbe1NgHxBqdAduwWHdWGN4,3011
|
|
18
|
+
datacustomcode/einstein_predictions/spark_base.py,sha256=MnFH2-pJ7ugFnc9iUvCdMt1vPAPAmiImptjjIGh9bvo,2567
|
|
19
|
+
datacustomcode/einstein_predictions/spark_default.py,sha256=tYL6u1DY3Ogu8xF7o1VqXqTGrcP2VUUMI6BShjXWvns,8119
|
|
17
20
|
datacustomcode/einstein_predictions/types.py,sha256=W7H3sFzm_NdBtnia7O576w0OvM7jwzfGVQmFiZTO8Og,6098
|
|
18
|
-
datacustomcode/einstein_predictions_config.py,sha256=
|
|
21
|
+
datacustomcode/einstein_predictions_config.py,sha256=7zUot5fUnXiihiu_9NStvk0-e9Otv55-Xk7dKqrMfBY,3758
|
|
19
22
|
datacustomcode/file/__init__.py,sha256=gamfOD1VnAtEslRBpqh-yKiVjkG_wYWYdSatGIfsN-w,621
|
|
20
23
|
datacustomcode/file/base.py,sha256=2IUlQufPvpHccgyoeZlKhcktO4pS4NKdha03Whq33Zk,745
|
|
21
24
|
datacustomcode/file/path/__init__.py,sha256=gamfOD1VnAtEslRBpqh-yKiVjkG_wYWYdSatGIfsN-w,621
|
|
@@ -87,13 +90,13 @@ datacustomcode/templates/script/examples/employee_hierarchy/employee_data.csv,sh
|
|
|
87
90
|
datacustomcode/templates/script/examples/employee_hierarchy/entrypoint.py,sha256=Mfm3iQtEHTQRW6cmZTwRKdTh3IeGWR-teXrd6x-YLSY,2223
|
|
88
91
|
datacustomcode/templates/script/jupyterlab.sh,sha256=IHR3YQ8d_busuyvesByhJgzCKULIFPI-Hqogs0NPhCs,2432
|
|
89
92
|
datacustomcode/templates/script/payload/config.json,sha256=0d2mEMt4NHIeOUTsgiPMuRKdOLIQxmh-Wq-IfEqU-gc,28
|
|
90
|
-
datacustomcode/templates/script/payload/entrypoint.py,sha256=
|
|
93
|
+
datacustomcode/templates/script/payload/entrypoint.py,sha256=4Uph0ILa5ukk_6C90paK3uzQn0zZvNLr9ho3o_ZwErQ,2474
|
|
91
94
|
datacustomcode/templates/script/requirements-dev.txt,sha256=OWwuy1awesqOZOS3Zizmdd6BDnSePpGFN5bq5IrALvc,176
|
|
92
95
|
datacustomcode/templates/script/requirements.txt,sha256=qJbqzs5z0Hrx590U3T7dHq_m8UQEx2TdhgrJSz-QHIQ,40
|
|
93
96
|
datacustomcode/token_provider.py,sha256=kGPxdxGZEr6VFknW7jFb9P6wOvRaRo-sfdBN2cUjEZY,6792
|
|
94
97
|
datacustomcode/version.py,sha256=9LlbVrzwBvut1L308QbwNBgxdQk5CrghH39JARVSxms,989
|
|
95
|
-
salesforce_data_customcode-6.0.
|
|
96
|
-
salesforce_data_customcode-6.0.
|
|
97
|
-
salesforce_data_customcode-6.0.
|
|
98
|
-
salesforce_data_customcode-6.0.
|
|
99
|
-
salesforce_data_customcode-6.0.
|
|
98
|
+
salesforce_data_customcode-6.0.6.dev1.dist-info/METADATA,sha256=k11RadLqAMw2inLBjUV6rLcjor-Itqf3wX7u1EJhuO8,25115
|
|
99
|
+
salesforce_data_customcode-6.0.6.dev1.dist-info/WHEEL,sha256=eY7nduwzv-ldUxpzbRlxwvC693Hg6PX8bWDjEHjZ_dk,88
|
|
100
|
+
salesforce_data_customcode-6.0.6.dev1.dist-info/entry_points.txt,sha256=WpQ94UB7UuRCYGOLtJV3vAgm1tl7iVf43VYRWIuNu5Y,74
|
|
101
|
+
salesforce_data_customcode-6.0.6.dev1.dist-info/licenses/LICENSE.txt,sha256=iOi8EmQpfkFhMENi7VYtkl2EqS14pEOccoXHiW2dyPU,11443
|
|
102
|
+
salesforce_data_customcode-6.0.6.dev1.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|