salesforce-data-customcode 6.0.5.dev1__tar.gz → 6.0.6.dev2__tar.gz
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.
- {salesforce_data_customcode-6.0.5.dev1 → salesforce_data_customcode-6.0.6.dev2}/PKG-INFO +96 -8
- {salesforce_data_customcode-6.0.5.dev1 → salesforce_data_customcode-6.0.6.dev2}/README.md +94 -6
- {salesforce_data_customcode-6.0.5.dev1 → salesforce_data_customcode-6.0.6.dev2}/pyproject.toml +2 -2
- {salesforce_data_customcode-6.0.5.dev1 → salesforce_data_customcode-6.0.6.dev2}/src/datacustomcode/__init__.py +20 -0
- {salesforce_data_customcode-6.0.5.dev1 → salesforce_data_customcode-6.0.6.dev2}/src/datacustomcode/client.py +325 -98
- {salesforce_data_customcode-6.0.5.dev1 → salesforce_data_customcode-6.0.6.dev2}/src/datacustomcode/config.py +5 -0
- {salesforce_data_customcode-6.0.5.dev1 → salesforce_data_customcode-6.0.6.dev2}/src/datacustomcode/config.yaml +3 -0
- {salesforce_data_customcode-6.0.5.dev1 → salesforce_data_customcode-6.0.6.dev2}/src/datacustomcode/einstein_predictions/__init__.py +8 -0
- salesforce_data_customcode-6.0.6.dev2/src/datacustomcode/einstein_predictions/errors.py +36 -0
- salesforce_data_customcode-6.0.6.dev2/src/datacustomcode/einstein_predictions/spark_base.py +72 -0
- salesforce_data_customcode-6.0.6.dev2/src/datacustomcode/einstein_predictions/spark_default.py +239 -0
- {salesforce_data_customcode-6.0.5.dev1 → salesforce_data_customcode-6.0.6.dev2}/src/datacustomcode/einstein_predictions_config.py +45 -1
- salesforce_data_customcode-6.0.6.dev2/src/datacustomcode/io/reader/base.py +85 -0
- {salesforce_data_customcode-6.0.5.dev1 → salesforce_data_customcode-6.0.6.dev2}/src/datacustomcode/io/writer/base.py +33 -0
- {salesforce_data_customcode-6.0.5.dev1 → salesforce_data_customcode-6.0.6.dev2}/src/datacustomcode/run.py +11 -0
- salesforce_data_customcode-6.0.6.dev2/src/datacustomcode/templates/script/examples/streaming_deltas/entrypoint.py +49 -0
- {salesforce_data_customcode-6.0.5.dev1 → salesforce_data_customcode-6.0.6.dev2}/src/datacustomcode/templates/script/payload/entrypoint.py +17 -0
- salesforce_data_customcode-6.0.5.dev1/src/datacustomcode/io/reader/base.py +0 -43
- {salesforce_data_customcode-6.0.5.dev1 → salesforce_data_customcode-6.0.6.dev2}/LICENSE.txt +0 -0
- {salesforce_data_customcode-6.0.5.dev1 → salesforce_data_customcode-6.0.6.dev2}/src/datacustomcode/auth.py +0 -0
- {salesforce_data_customcode-6.0.5.dev1 → salesforce_data_customcode-6.0.6.dev2}/src/datacustomcode/cli.py +0 -0
- {salesforce_data_customcode-6.0.5.dev1 → salesforce_data_customcode-6.0.6.dev2}/src/datacustomcode/cmd.py +0 -0
- {salesforce_data_customcode-6.0.5.dev1 → salesforce_data_customcode-6.0.6.dev2}/src/datacustomcode/common_config.py +0 -0
- {salesforce_data_customcode-6.0.5.dev1 → salesforce_data_customcode-6.0.6.dev2}/src/datacustomcode/constants.py +0 -0
- {salesforce_data_customcode-6.0.5.dev1 → salesforce_data_customcode-6.0.6.dev2}/src/datacustomcode/credentials.py +0 -0
- {salesforce_data_customcode-6.0.5.dev1 → salesforce_data_customcode-6.0.6.dev2}/src/datacustomcode/deploy.py +0 -0
- {salesforce_data_customcode-6.0.5.dev1 → salesforce_data_customcode-6.0.6.dev2}/src/datacustomcode/einstein_platform_client.py +0 -0
- {salesforce_data_customcode-6.0.5.dev1 → salesforce_data_customcode-6.0.6.dev2}/src/datacustomcode/einstein_platform_config.py +0 -0
- {salesforce_data_customcode-6.0.5.dev1 → salesforce_data_customcode-6.0.6.dev2}/src/datacustomcode/einstein_predictions/base.py +0 -0
- {salesforce_data_customcode-6.0.5.dev1 → salesforce_data_customcode-6.0.6.dev2}/src/datacustomcode/einstein_predictions/impl/default.py +0 -0
- {salesforce_data_customcode-6.0.5.dev1 → salesforce_data_customcode-6.0.6.dev2}/src/datacustomcode/einstein_predictions/types.py +0 -0
- {salesforce_data_customcode-6.0.5.dev1 → salesforce_data_customcode-6.0.6.dev2}/src/datacustomcode/file/__init__.py +0 -0
- {salesforce_data_customcode-6.0.5.dev1 → salesforce_data_customcode-6.0.6.dev2}/src/datacustomcode/file/base.py +0 -0
- {salesforce_data_customcode-6.0.5.dev1 → salesforce_data_customcode-6.0.6.dev2}/src/datacustomcode/file/path/__init__.py +0 -0
- {salesforce_data_customcode-6.0.5.dev1 → salesforce_data_customcode-6.0.6.dev2}/src/datacustomcode/file/path/default.py +0 -0
- {salesforce_data_customcode-6.0.5.dev1 → salesforce_data_customcode-6.0.6.dev2}/src/datacustomcode/function/__init__.py +0 -0
- {salesforce_data_customcode-6.0.5.dev1 → salesforce_data_customcode-6.0.6.dev2}/src/datacustomcode/function/base.py +0 -0
- {salesforce_data_customcode-6.0.5.dev1 → salesforce_data_customcode-6.0.6.dev2}/src/datacustomcode/function/feature_types/__init__.py +0 -0
- {salesforce_data_customcode-6.0.5.dev1 → salesforce_data_customcode-6.0.6.dev2}/src/datacustomcode/function/feature_types/chunking.py +0 -0
- {salesforce_data_customcode-6.0.5.dev1 → salesforce_data_customcode-6.0.6.dev2}/src/datacustomcode/function/runtime.py +0 -0
- {salesforce_data_customcode-6.0.5.dev1 → salesforce_data_customcode-6.0.6.dev2}/src/datacustomcode/function_utils.py +0 -0
- {salesforce_data_customcode-6.0.5.dev1 → salesforce_data_customcode-6.0.6.dev2}/src/datacustomcode/io/__init__.py +0 -0
- {salesforce_data_customcode-6.0.5.dev1 → salesforce_data_customcode-6.0.6.dev2}/src/datacustomcode/io/base.py +0 -0
- {salesforce_data_customcode-6.0.5.dev1 → salesforce_data_customcode-6.0.6.dev2}/src/datacustomcode/io/reader/__init__.py +0 -0
- {salesforce_data_customcode-6.0.5.dev1 → salesforce_data_customcode-6.0.6.dev2}/src/datacustomcode/io/reader/query_api.py +0 -0
- {salesforce_data_customcode-6.0.5.dev1 → salesforce_data_customcode-6.0.6.dev2}/src/datacustomcode/io/reader/sf_cli.py +0 -0
- {salesforce_data_customcode-6.0.5.dev1 → salesforce_data_customcode-6.0.6.dev2}/src/datacustomcode/io/reader/utils.py +0 -0
- {salesforce_data_customcode-6.0.5.dev1 → salesforce_data_customcode-6.0.6.dev2}/src/datacustomcode/io/writer/__init__.py +0 -0
- {salesforce_data_customcode-6.0.5.dev1 → salesforce_data_customcode-6.0.6.dev2}/src/datacustomcode/io/writer/csv.py +0 -0
- {salesforce_data_customcode-6.0.5.dev1 → salesforce_data_customcode-6.0.6.dev2}/src/datacustomcode/io/writer/print.py +0 -0
- {salesforce_data_customcode-6.0.5.dev1 → salesforce_data_customcode-6.0.6.dev2}/src/datacustomcode/llm_gateway/__init__.py +0 -0
- {salesforce_data_customcode-6.0.5.dev1 → salesforce_data_customcode-6.0.6.dev2}/src/datacustomcode/llm_gateway/base.py +0 -0
- {salesforce_data_customcode-6.0.5.dev1 → salesforce_data_customcode-6.0.6.dev2}/src/datacustomcode/llm_gateway/default.py +0 -0
- {salesforce_data_customcode-6.0.5.dev1 → salesforce_data_customcode-6.0.6.dev2}/src/datacustomcode/llm_gateway/errors.py +0 -0
- {salesforce_data_customcode-6.0.5.dev1 → salesforce_data_customcode-6.0.6.dev2}/src/datacustomcode/llm_gateway/spark_base.py +0 -0
- {salesforce_data_customcode-6.0.5.dev1 → salesforce_data_customcode-6.0.6.dev2}/src/datacustomcode/llm_gateway/spark_default.py +0 -0
- {salesforce_data_customcode-6.0.5.dev1 → salesforce_data_customcode-6.0.6.dev2}/src/datacustomcode/llm_gateway/types/__init__.py +0 -0
- {salesforce_data_customcode-6.0.5.dev1 → salesforce_data_customcode-6.0.6.dev2}/src/datacustomcode/llm_gateway/types/generate_text_request.py +0 -0
- {salesforce_data_customcode-6.0.5.dev1 → salesforce_data_customcode-6.0.6.dev2}/src/datacustomcode/llm_gateway/types/generate_text_request_builder.py +0 -0
- {salesforce_data_customcode-6.0.5.dev1 → salesforce_data_customcode-6.0.6.dev2}/src/datacustomcode/llm_gateway/types/generate_text_response.py +0 -0
- {salesforce_data_customcode-6.0.5.dev1 → salesforce_data_customcode-6.0.6.dev2}/src/datacustomcode/llm_gateway/types/generate_text_response_builder.py +0 -0
- {salesforce_data_customcode-6.0.5.dev1 → salesforce_data_customcode-6.0.6.dev2}/src/datacustomcode/llm_gateway_config.py +0 -0
- {salesforce_data_customcode-6.0.5.dev1 → salesforce_data_customcode-6.0.6.dev2}/src/datacustomcode/mixin.py +0 -0
- {salesforce_data_customcode-6.0.5.dev1 → salesforce_data_customcode-6.0.6.dev2}/src/datacustomcode/py.typed +0 -0
- {salesforce_data_customcode-6.0.5.dev1 → salesforce_data_customcode-6.0.6.dev2}/src/datacustomcode/scan.py +0 -0
- {salesforce_data_customcode-6.0.5.dev1 → salesforce_data_customcode-6.0.6.dev2}/src/datacustomcode/spark/__init__.py +0 -0
- {salesforce_data_customcode-6.0.5.dev1 → salesforce_data_customcode-6.0.6.dev2}/src/datacustomcode/spark/base.py +0 -0
- {salesforce_data_customcode-6.0.5.dev1 → salesforce_data_customcode-6.0.6.dev2}/src/datacustomcode/spark/default.py +0 -0
- {salesforce_data_customcode-6.0.5.dev1 → salesforce_data_customcode-6.0.6.dev2}/src/datacustomcode/template.py +0 -0
- {salesforce_data_customcode-6.0.5.dev1 → salesforce_data_customcode-6.0.6.dev2}/src/datacustomcode/templates/__init__.py +0 -0
- {salesforce_data_customcode-6.0.5.dev1 → salesforce_data_customcode-6.0.6.dev2}/src/datacustomcode/templates/function/.devcontainer/devcontainer.json +0 -0
- {salesforce_data_customcode-6.0.5.dev1 → salesforce_data_customcode-6.0.6.dev2}/src/datacustomcode/templates/function/Dockerfile.dependencies +0 -0
- {salesforce_data_customcode-6.0.5.dev1 → salesforce_data_customcode-6.0.6.dev2}/src/datacustomcode/templates/function/README.md +0 -0
- {salesforce_data_customcode-6.0.5.dev1 → salesforce_data_customcode-6.0.6.dev2}/src/datacustomcode/templates/function/build_native_dependencies.sh +0 -0
- {salesforce_data_customcode-6.0.5.dev1 → salesforce_data_customcode-6.0.6.dev2}/src/datacustomcode/templates/function/chunking/payload/config.json +0 -0
- {salesforce_data_customcode-6.0.5.dev1 → salesforce_data_customcode-6.0.6.dev2}/src/datacustomcode/templates/function/chunking/payload/entrypoint.py +0 -0
- {salesforce_data_customcode-6.0.5.dev1 → salesforce_data_customcode-6.0.6.dev2}/src/datacustomcode/templates/function/chunking/requirements.txt +0 -0
- {salesforce_data_customcode-6.0.5.dev1 → salesforce_data_customcode-6.0.6.dev2}/src/datacustomcode/templates/function/example/chunking_with_llm/config.json +0 -0
- {salesforce_data_customcode-6.0.5.dev1 → salesforce_data_customcode-6.0.6.dev2}/src/datacustomcode/templates/function/example/chunking_with_llm/entrypoint.py +0 -0
- {salesforce_data_customcode-6.0.5.dev1 → salesforce_data_customcode-6.0.6.dev2}/src/datacustomcode/templates/function/example/chunking_with_llm/files/chunking_prompt.txt +0 -0
- {salesforce_data_customcode-6.0.5.dev1 → salesforce_data_customcode-6.0.6.dev2}/src/datacustomcode/templates/function/example/chunking_with_llm/tests/test.json +0 -0
- {salesforce_data_customcode-6.0.5.dev1 → salesforce_data_customcode-6.0.6.dev2}/src/datacustomcode/templates/function/example/chunking_with_prediction/config.json +0 -0
- {salesforce_data_customcode-6.0.5.dev1 → salesforce_data_customcode-6.0.6.dev2}/src/datacustomcode/templates/function/example/chunking_with_prediction/entrypoint.py +0 -0
- {salesforce_data_customcode-6.0.5.dev1 → salesforce_data_customcode-6.0.6.dev2}/src/datacustomcode/templates/function/example/chunking_with_prediction/tests/test.json +0 -0
- {salesforce_data_customcode-6.0.5.dev1 → salesforce_data_customcode-6.0.6.dev2}/src/datacustomcode/templates/function/payload/config.json +0 -0
- {salesforce_data_customcode-6.0.5.dev1 → salesforce_data_customcode-6.0.6.dev2}/src/datacustomcode/templates/function/payload/entrypoint.py +0 -0
- {salesforce_data_customcode-6.0.5.dev1 → salesforce_data_customcode-6.0.6.dev2}/src/datacustomcode/templates/function/payload/utility.py +0 -0
- {salesforce_data_customcode-6.0.5.dev1 → salesforce_data_customcode-6.0.6.dev2}/src/datacustomcode/templates/function/requirements-dev.txt +0 -0
- {salesforce_data_customcode-6.0.5.dev1 → salesforce_data_customcode-6.0.6.dev2}/src/datacustomcode/templates/function/requirements.txt +0 -0
- {salesforce_data_customcode-6.0.5.dev1 → salesforce_data_customcode-6.0.6.dev2}/src/datacustomcode/templates/script/.devcontainer/devcontainer.json +0 -0
- {salesforce_data_customcode-6.0.5.dev1 → salesforce_data_customcode-6.0.6.dev2}/src/datacustomcode/templates/script/Dockerfile +0 -0
- {salesforce_data_customcode-6.0.5.dev1 → salesforce_data_customcode-6.0.6.dev2}/src/datacustomcode/templates/script/Dockerfile.dependencies +0 -0
- {salesforce_data_customcode-6.0.5.dev1 → salesforce_data_customcode-6.0.6.dev2}/src/datacustomcode/templates/script/README.md +0 -0
- {salesforce_data_customcode-6.0.5.dev1 → salesforce_data_customcode-6.0.6.dev2}/src/datacustomcode/templates/script/account.ipynb +0 -0
- {salesforce_data_customcode-6.0.5.dev1 → salesforce_data_customcode-6.0.6.dev2}/src/datacustomcode/templates/script/build_native_dependencies.sh +0 -0
- {salesforce_data_customcode-6.0.5.dev1 → salesforce_data_customcode-6.0.6.dev2}/src/datacustomcode/templates/script/examples/employee_hierarchy/employee_data.csv +0 -0
- {salesforce_data_customcode-6.0.5.dev1 → salesforce_data_customcode-6.0.6.dev2}/src/datacustomcode/templates/script/examples/employee_hierarchy/entrypoint.py +0 -0
- {salesforce_data_customcode-6.0.5.dev1 → salesforce_data_customcode-6.0.6.dev2}/src/datacustomcode/templates/script/jupyterlab.sh +0 -0
- {salesforce_data_customcode-6.0.5.dev1 → salesforce_data_customcode-6.0.6.dev2}/src/datacustomcode/templates/script/payload/config.json +0 -0
- {salesforce_data_customcode-6.0.5.dev1 → salesforce_data_customcode-6.0.6.dev2}/src/datacustomcode/templates/script/requirements-dev.txt +0 -0
- {salesforce_data_customcode-6.0.5.dev1 → salesforce_data_customcode-6.0.6.dev2}/src/datacustomcode/templates/script/requirements.txt +0 -0
- {salesforce_data_customcode-6.0.5.dev1 → salesforce_data_customcode-6.0.6.dev2}/src/datacustomcode/token_provider.py +0 -0
- {salesforce_data_customcode-6.0.5.dev1 → salesforce_data_customcode-6.0.6.dev2}/src/datacustomcode/version.py +0 -0
|
@@ -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.dev2
|
|
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]
|
|
@@ -170,15 +170,22 @@ Your Python dependencies can be packaged as .py files, .zip archives (containing
|
|
|
170
170
|
|
|
171
171
|
## API
|
|
172
172
|
|
|
173
|
-
Your entry point script will define logic using the `Client` object which
|
|
173
|
+
Your entry point script will define logic using the `Client` object (for batch transforms) or the `StreamingClient` object (for streaming delta transforms), which wrap the data access layers. Both are singletons; a single transform should use one or the other, not both.
|
|
174
174
|
|
|
175
|
-
You should only need the following methods:
|
|
175
|
+
For a batch transform, use `Client`. You should only need the following methods:
|
|
176
176
|
* `find_file_path(file_name)` – Resolve a bundled file (placed under `payload/files/`) to a `pathlib.Path` that exists. Works the same locally and inside Data Cloud — see [Bundled file resolution](#bundled-file-resolution) below for the full lookup order. Raises `FileNotFoundError` if the file isn't found.
|
|
177
177
|
* `read_dlo(name)` – Read from a Data Lake Object by name
|
|
178
178
|
* `read_dmo(name)` – Read from a Data Model Object by name
|
|
179
179
|
* `write_to_dlo(name, spark_dataframe, write_mode)` – Write to a Data Model Object by name with a Spark dataframe
|
|
180
180
|
* `write_to_dmo(name, spark_dataframe, write_mode)` – Write to a Data Lake Object by name with a Spark dataframe
|
|
181
181
|
|
|
182
|
+
For a streaming (delta) transform, use `StreamingClient`, which exposes the streaming counterparts:
|
|
183
|
+
* `read_dlo_deltas()` – Read the streaming change feed (deltas) of a Data Lake Object as a streaming DataFrame.
|
|
184
|
+
* `read_dmo_deltas()` – Read the streaming change feed (deltas) of a Data Model Object as a streaming DataFrame.
|
|
185
|
+
* `write_dlo_deltas(name, spark_dataframe)` – Write a streaming DataFrame of deltas to a Data Lake Object; returns the started `StreamingQuery`
|
|
186
|
+
|
|
187
|
+
`find_file_path`, `llm_gateway_generate_text`, and `einstein_predict` are available on both clients.
|
|
188
|
+
|
|
182
189
|
For example:
|
|
183
190
|
```python
|
|
184
191
|
from datacustomcode import Client
|
|
@@ -194,6 +201,36 @@ client.write_to_dlo('output_DLO')
|
|
|
194
201
|
> [!WARNING]
|
|
195
202
|
> Currently we only support reading from DMOs and writing to DMOs or reading from DLOs and writing to DLOs, but they cannot mix.
|
|
196
203
|
|
|
204
|
+
### Streaming (delta) transforms
|
|
205
|
+
|
|
206
|
+
Streaming BYOC transforms process a Data Lake Object's Change Data Feed continuously instead of reading a bounded snapshot. Use a `StreamingClient` and its `*_deltas` methods in place of the batch `Client` read/write methods:
|
|
207
|
+
|
|
208
|
+
```python
|
|
209
|
+
from pyspark.sql.functions import col, upper
|
|
210
|
+
|
|
211
|
+
from datacustomcode import StreamingClient
|
|
212
|
+
|
|
213
|
+
client = StreamingClient()
|
|
214
|
+
|
|
215
|
+
# read_dlo_deltas returns a *streaming* DataFrame over the change feed.
|
|
216
|
+
# The runtime resolves the single streaming source, so no name is passed.
|
|
217
|
+
deltas = client.read_dlo_deltas()
|
|
218
|
+
|
|
219
|
+
# Ordinary PySpark transform.
|
|
220
|
+
transformed = deltas.withColumn("description__c", upper(col("description__c")))
|
|
221
|
+
|
|
222
|
+
# write_dlo_deltas starts a streaming query and returns the StreamingQuery.
|
|
223
|
+
# The runtime owns the trigger and checkpoint location; you
|
|
224
|
+
# choose only the target table.
|
|
225
|
+
query = client.write_dlo_deltas("Output__dll", transformed)
|
|
226
|
+
query.awaitTermination()
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
Notes:
|
|
230
|
+
|
|
231
|
+
- These methods only run inside the Data Cloud streaming (`DELTA_SYNC`) runtime. Locally (`datacustomcode run`) they raise `NotImplementedError`, since there is no change feed to stream.
|
|
232
|
+
- A complete runnable entry point is provided in [`examples/streaming_deltas/entrypoint.py`](src/datacustomcode/templates/script/examples/streaming_deltas/entrypoint.py).
|
|
233
|
+
|
|
197
234
|
### Bundled file resolution
|
|
198
235
|
|
|
199
236
|
Place bundled files (CSVs, prompt files, etc.) under `payload/files/`. The same `client.find_file_path("data.csv")` call resolves consistently across all three runtimes:
|
|
@@ -345,6 +382,7 @@ Options:
|
|
|
345
382
|
- `--description TEXT`: Description of the transformation job (default: "")
|
|
346
383
|
- `--network TEXT`: docker network (default: "default")
|
|
347
384
|
- `--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)
|
|
385
|
+
- `--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
386
|
- `--function-invoke-opt TEXT`: Currently we support only `UnstructuredChunking` for functions.
|
|
349
387
|
|
|
350
388
|
|
|
@@ -397,6 +435,56 @@ datacustomcode run ./payload/entrypoint.py --sf-cli-org myorg
|
|
|
397
435
|
```
|
|
398
436
|
|
|
399
437
|
|
|
438
|
+
## Testing Einstein Predictions
|
|
439
|
+
|
|
440
|
+
You can use AI models configured in Einstein Studio to score your data while
|
|
441
|
+
transforming it. As with the LLM Gateway, there are two flavors: a one-shot
|
|
442
|
+
scalar call (`client.einstein_predict`) and a per-row column helper
|
|
443
|
+
(`einstein_predict_col`). Below is a sample code example:
|
|
444
|
+
|
|
445
|
+
```
|
|
446
|
+
from datacustomcode.client import Client, einstein_predict_col
|
|
447
|
+
from datacustomcode.einstein_predictions.types import PredictionType
|
|
448
|
+
|
|
449
|
+
|
|
450
|
+
def main():
|
|
451
|
+
client = Client()
|
|
452
|
+
df = client.read_dlo("Input__dll")
|
|
453
|
+
# einstein_predict_col returns a struct
|
|
454
|
+
# {status, response, error_code, error_message} per row, so per-row
|
|
455
|
+
# failures don't abort the Spark job. `response` is the prediction
|
|
456
|
+
# payload as a JSON string. Pick the field you want with [].
|
|
457
|
+
df_scored = df.withColumn(
|
|
458
|
+
"prediction__c",
|
|
459
|
+
einstein_predict_col(
|
|
460
|
+
"my_regression_model", # An AI model in your org
|
|
461
|
+
PredictionType.REGRESSION,
|
|
462
|
+
{"square_feet": col("square_feet__c"), "beds": col("beds__c")},
|
|
463
|
+
)["response"],
|
|
464
|
+
)
|
|
465
|
+
|
|
466
|
+
dlo_name = "Output_dll"
|
|
467
|
+
client.write_to_dlo(dlo_name, df_scored, write_mode=WriteMode.APPEND)
|
|
468
|
+
|
|
469
|
+
# One-shot scalar prediction returns the response payload as a dict
|
|
470
|
+
prediction = client.einstein_predict(
|
|
471
|
+
"my_regression_model",
|
|
472
|
+
PredictionType.REGRESSION,
|
|
473
|
+
{"square_feet": 1800, "beds": 3},
|
|
474
|
+
)
|
|
475
|
+
|
|
476
|
+
if __name__ == "__main__":
|
|
477
|
+
main()
|
|
478
|
+
```
|
|
479
|
+
|
|
480
|
+
Testing this code locally uses the same External Client App setup described in
|
|
481
|
+
[Testing LLM Gateway](#testing-llm-gateway). Once your `myorg` alias is set up,
|
|
482
|
+
run:
|
|
483
|
+
```
|
|
484
|
+
datacustomcode run ./payload/entrypoint.py --sf-cli-org myorg
|
|
485
|
+
```
|
|
486
|
+
|
|
487
|
+
|
|
400
488
|
## Docker usage
|
|
401
489
|
|
|
402
490
|
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 +596,7 @@ sf --version
|
|
|
508
596
|
**Browser-based (recommended for developer orgs and sandboxes):**
|
|
509
597
|
```zsh
|
|
510
598
|
# Production / Developer Edition
|
|
511
|
-
sf org login web --alias myorg
|
|
599
|
+
sf org login web --alias myorg --instance-url <your-instance-url>
|
|
512
600
|
|
|
513
601
|
# Sandbox
|
|
514
602
|
sf org login web --alias mysandbox --instance-url https://test.salesforce.com
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# Data Cloud Custom Code SDK
|
|
1
|
+
# Data Cloud Custom Code SDK
|
|
2
2
|
|
|
3
3
|
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).
|
|
4
4
|
|
|
@@ -11,7 +11,7 @@ Use of this project with Salesforce is subject to the [TERMS OF USE](./TERMS_OF_
|
|
|
11
11
|
- **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)
|
|
12
12
|
- JDK 17
|
|
13
13
|
- Docker support like [Docker Desktop](https://docs.docker.com/desktop/)
|
|
14
|
-
- A salesforce org with some DLOs or DMOs with data and this feature enabled
|
|
14
|
+
- A salesforce org with some DLOs or DMOs with data and this feature enabled
|
|
15
15
|
- **One of the following** for authentication:
|
|
16
16
|
- A Salesforce org already authenticated via the [Salesforce CLI](https://developer.salesforce.com/tools/salesforcecli)
|
|
17
17
|
(simplest — no External Client App needed)
|
|
@@ -91,7 +91,7 @@ After modifying the `entrypoint.py` as needed, using any dependencies you add in
|
|
|
91
91
|
```zsh
|
|
92
92
|
cd my_package
|
|
93
93
|
datacustomcode scan ./payload/entrypoint.py
|
|
94
|
-
datacustomcode deploy --path ./payload --name my_custom_script --cpu-size CPU_L
|
|
94
|
+
datacustomcode deploy --path ./payload --name my_custom_script --cpu-size CPU_L --sf-cli-org myorg
|
|
95
95
|
```
|
|
96
96
|
|
|
97
97
|
> [!TIP]
|
|
@@ -145,15 +145,22 @@ Your Python dependencies can be packaged as .py files, .zip archives (containing
|
|
|
145
145
|
|
|
146
146
|
## API
|
|
147
147
|
|
|
148
|
-
Your entry point script will define logic using the `Client` object which
|
|
148
|
+
Your entry point script will define logic using the `Client` object (for batch transforms) or the `StreamingClient` object (for streaming delta transforms), which wrap the data access layers. Both are singletons; a single transform should use one or the other, not both.
|
|
149
149
|
|
|
150
|
-
You should only need the following methods:
|
|
150
|
+
For a batch transform, use `Client`. You should only need the following methods:
|
|
151
151
|
* `find_file_path(file_name)` – Resolve a bundled file (placed under `payload/files/`) to a `pathlib.Path` that exists. Works the same locally and inside Data Cloud — see [Bundled file resolution](#bundled-file-resolution) below for the full lookup order. Raises `FileNotFoundError` if the file isn't found.
|
|
152
152
|
* `read_dlo(name)` – Read from a Data Lake Object by name
|
|
153
153
|
* `read_dmo(name)` – Read from a Data Model Object by name
|
|
154
154
|
* `write_to_dlo(name, spark_dataframe, write_mode)` – Write to a Data Model Object by name with a Spark dataframe
|
|
155
155
|
* `write_to_dmo(name, spark_dataframe, write_mode)` – Write to a Data Lake Object by name with a Spark dataframe
|
|
156
156
|
|
|
157
|
+
For a streaming (delta) transform, use `StreamingClient`, which exposes the streaming counterparts:
|
|
158
|
+
* `read_dlo_deltas()` – Read the streaming change feed (deltas) of a Data Lake Object as a streaming DataFrame.
|
|
159
|
+
* `read_dmo_deltas()` – Read the streaming change feed (deltas) of a Data Model Object as a streaming DataFrame.
|
|
160
|
+
* `write_dlo_deltas(name, spark_dataframe)` – Write a streaming DataFrame of deltas to a Data Lake Object; returns the started `StreamingQuery`
|
|
161
|
+
|
|
162
|
+
`find_file_path`, `llm_gateway_generate_text`, and `einstein_predict` are available on both clients.
|
|
163
|
+
|
|
157
164
|
For example:
|
|
158
165
|
```python
|
|
159
166
|
from datacustomcode import Client
|
|
@@ -169,6 +176,36 @@ client.write_to_dlo('output_DLO')
|
|
|
169
176
|
> [!WARNING]
|
|
170
177
|
> Currently we only support reading from DMOs and writing to DMOs or reading from DLOs and writing to DLOs, but they cannot mix.
|
|
171
178
|
|
|
179
|
+
### Streaming (delta) transforms
|
|
180
|
+
|
|
181
|
+
Streaming BYOC transforms process a Data Lake Object's Change Data Feed continuously instead of reading a bounded snapshot. Use a `StreamingClient` and its `*_deltas` methods in place of the batch `Client` read/write methods:
|
|
182
|
+
|
|
183
|
+
```python
|
|
184
|
+
from pyspark.sql.functions import col, upper
|
|
185
|
+
|
|
186
|
+
from datacustomcode import StreamingClient
|
|
187
|
+
|
|
188
|
+
client = StreamingClient()
|
|
189
|
+
|
|
190
|
+
# read_dlo_deltas returns a *streaming* DataFrame over the change feed.
|
|
191
|
+
# The runtime resolves the single streaming source, so no name is passed.
|
|
192
|
+
deltas = client.read_dlo_deltas()
|
|
193
|
+
|
|
194
|
+
# Ordinary PySpark transform.
|
|
195
|
+
transformed = deltas.withColumn("description__c", upper(col("description__c")))
|
|
196
|
+
|
|
197
|
+
# write_dlo_deltas starts a streaming query and returns the StreamingQuery.
|
|
198
|
+
# The runtime owns the trigger and checkpoint location; you
|
|
199
|
+
# choose only the target table.
|
|
200
|
+
query = client.write_dlo_deltas("Output__dll", transformed)
|
|
201
|
+
query.awaitTermination()
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
Notes:
|
|
205
|
+
|
|
206
|
+
- These methods only run inside the Data Cloud streaming (`DELTA_SYNC`) runtime. Locally (`datacustomcode run`) they raise `NotImplementedError`, since there is no change feed to stream.
|
|
207
|
+
- A complete runnable entry point is provided in [`examples/streaming_deltas/entrypoint.py`](src/datacustomcode/templates/script/examples/streaming_deltas/entrypoint.py).
|
|
208
|
+
|
|
172
209
|
### Bundled file resolution
|
|
173
210
|
|
|
174
211
|
Place bundled files (CSVs, prompt files, etc.) under `payload/files/`. The same `client.find_file_path("data.csv")` call resolves consistently across all three runtimes:
|
|
@@ -320,6 +357,7 @@ Options:
|
|
|
320
357
|
- `--description TEXT`: Description of the transformation job (default: "")
|
|
321
358
|
- `--network TEXT`: docker network (default: "default")
|
|
322
359
|
- `--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)
|
|
360
|
+
- `--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.
|
|
323
361
|
- `--function-invoke-opt TEXT`: Currently we support only `UnstructuredChunking` for functions.
|
|
324
362
|
|
|
325
363
|
|
|
@@ -372,6 +410,56 @@ datacustomcode run ./payload/entrypoint.py --sf-cli-org myorg
|
|
|
372
410
|
```
|
|
373
411
|
|
|
374
412
|
|
|
413
|
+
## Testing Einstein Predictions
|
|
414
|
+
|
|
415
|
+
You can use AI models configured in Einstein Studio to score your data while
|
|
416
|
+
transforming it. As with the LLM Gateway, there are two flavors: a one-shot
|
|
417
|
+
scalar call (`client.einstein_predict`) and a per-row column helper
|
|
418
|
+
(`einstein_predict_col`). Below is a sample code example:
|
|
419
|
+
|
|
420
|
+
```
|
|
421
|
+
from datacustomcode.client import Client, einstein_predict_col
|
|
422
|
+
from datacustomcode.einstein_predictions.types import PredictionType
|
|
423
|
+
|
|
424
|
+
|
|
425
|
+
def main():
|
|
426
|
+
client = Client()
|
|
427
|
+
df = client.read_dlo("Input__dll")
|
|
428
|
+
# einstein_predict_col returns a struct
|
|
429
|
+
# {status, response, error_code, error_message} per row, so per-row
|
|
430
|
+
# failures don't abort the Spark job. `response` is the prediction
|
|
431
|
+
# payload as a JSON string. Pick the field you want with [].
|
|
432
|
+
df_scored = df.withColumn(
|
|
433
|
+
"prediction__c",
|
|
434
|
+
einstein_predict_col(
|
|
435
|
+
"my_regression_model", # An AI model in your org
|
|
436
|
+
PredictionType.REGRESSION,
|
|
437
|
+
{"square_feet": col("square_feet__c"), "beds": col("beds__c")},
|
|
438
|
+
)["response"],
|
|
439
|
+
)
|
|
440
|
+
|
|
441
|
+
dlo_name = "Output_dll"
|
|
442
|
+
client.write_to_dlo(dlo_name, df_scored, write_mode=WriteMode.APPEND)
|
|
443
|
+
|
|
444
|
+
# One-shot scalar prediction returns the response payload as a dict
|
|
445
|
+
prediction = client.einstein_predict(
|
|
446
|
+
"my_regression_model",
|
|
447
|
+
PredictionType.REGRESSION,
|
|
448
|
+
{"square_feet": 1800, "beds": 3},
|
|
449
|
+
)
|
|
450
|
+
|
|
451
|
+
if __name__ == "__main__":
|
|
452
|
+
main()
|
|
453
|
+
```
|
|
454
|
+
|
|
455
|
+
Testing this code locally uses the same External Client App setup described in
|
|
456
|
+
[Testing LLM Gateway](#testing-llm-gateway). Once your `myorg` alias is set up,
|
|
457
|
+
run:
|
|
458
|
+
```
|
|
459
|
+
datacustomcode run ./payload/entrypoint.py --sf-cli-org myorg
|
|
460
|
+
```
|
|
461
|
+
|
|
462
|
+
|
|
375
463
|
## Docker usage
|
|
376
464
|
|
|
377
465
|
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.
|
|
@@ -483,7 +571,7 @@ sf --version
|
|
|
483
571
|
**Browser-based (recommended for developer orgs and sandboxes):**
|
|
484
572
|
```zsh
|
|
485
573
|
# Production / Developer Edition
|
|
486
|
-
sf org login web --alias myorg
|
|
574
|
+
sf org login web --alias myorg --instance-url <your-instance-url>
|
|
487
575
|
|
|
488
576
|
# Sandbox
|
|
489
577
|
sf org login web --alias mysandbox --instance-url https://test.salesforce.com
|
{salesforce_data_customcode-6.0.5.dev1 → salesforce_data_customcode-6.0.6.dev2}/pyproject.toml
RENAMED
|
@@ -5,7 +5,7 @@ requires = ["poetry-core>=2.0.0", "poetry-dynamic-versioning>=1.0.0,<2.0.0"]
|
|
|
5
5
|
[project]
|
|
6
6
|
authors = []
|
|
7
7
|
classifiers = [
|
|
8
|
-
"Development Status ::
|
|
8
|
+
"Development Status :: 5 - Production/Stable",
|
|
9
9
|
"Operating System :: Unix",
|
|
10
10
|
"Programming Language :: Python",
|
|
11
11
|
"Programming Language :: Python :: 3",
|
|
@@ -18,7 +18,7 @@ license = "Apache-2.0"
|
|
|
18
18
|
name = "salesforce-data-customcode"
|
|
19
19
|
readme = "README.md"
|
|
20
20
|
requires-python = ">=3.10,<3.12"
|
|
21
|
-
version = "6.0.
|
|
21
|
+
version = "6.0.6.dev2"
|
|
22
22
|
|
|
23
23
|
[tool.black]
|
|
24
24
|
exclude = '''
|
|
@@ -17,10 +17,14 @@ __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
|
+
"StreamingClient",
|
|
27
|
+
"einstein_predict_col",
|
|
24
28
|
"llm_gateway_generate_text_col",
|
|
25
29
|
]
|
|
26
30
|
|
|
@@ -31,6 +35,10 @@ def __getattr__(name: str):
|
|
|
31
35
|
from datacustomcode.client import Client
|
|
32
36
|
|
|
33
37
|
return Client
|
|
38
|
+
elif name == "StreamingClient":
|
|
39
|
+
from datacustomcode.client import StreamingClient
|
|
40
|
+
|
|
41
|
+
return StreamingClient
|
|
34
42
|
elif name == "AuthType":
|
|
35
43
|
from datacustomcode.credentials import AuthType
|
|
36
44
|
|
|
@@ -59,4 +67,16 @@ def __getattr__(name: str):
|
|
|
59
67
|
from datacustomcode.client import llm_gateway_generate_text_col
|
|
60
68
|
|
|
61
69
|
return llm_gateway_generate_text_col
|
|
70
|
+
elif name == "SparkEinsteinPredictions":
|
|
71
|
+
from datacustomcode.einstein_predictions import SparkEinsteinPredictions
|
|
72
|
+
|
|
73
|
+
return SparkEinsteinPredictions
|
|
74
|
+
elif name == "DefaultSparkEinsteinPredictions":
|
|
75
|
+
from datacustomcode.einstein_predictions import DefaultSparkEinsteinPredictions
|
|
76
|
+
|
|
77
|
+
return DefaultSparkEinsteinPredictions
|
|
78
|
+
elif name == "einstein_predict_col":
|
|
79
|
+
from datacustomcode.client import einstein_predict_col
|
|
80
|
+
|
|
81
|
+
return einstein_predict_col
|
|
62
82
|
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
|