salesforce-data-customcode 6.0.2__py3-none-any.whl → 6.0.3.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.
Files changed (28) hide show
  1. datacustomcode/__init__.py +15 -0
  2. datacustomcode/cli.py +3 -6
  3. datacustomcode/client.py +92 -1
  4. datacustomcode/config.yaml +3 -0
  5. datacustomcode/einstein_platform_config.py +8 -4
  6. datacustomcode/einstein_predictions_config.py +1 -1
  7. datacustomcode/function/feature_types/chunking.py +1 -0
  8. datacustomcode/io/writer/base.py +1 -11
  9. datacustomcode/llm_gateway/__init__.py +6 -0
  10. datacustomcode/{proxy/base.py → llm_gateway/errors.py} +17 -5
  11. datacustomcode/llm_gateway/spark_base.py +53 -0
  12. datacustomcode/llm_gateway/spark_default.py +125 -0
  13. datacustomcode/llm_gateway_config.py +43 -2
  14. datacustomcode/templates/function/.devcontainer/devcontainer.json +1 -1
  15. datacustomcode/templates/function/example/chunking_with_llm/entrypoint.py +3 -0
  16. datacustomcode/templates/function/example/chunking_with_prediction/entrypoint.py +3 -0
  17. datacustomcode/templates/function/payload/entrypoint.py +11 -12
  18. datacustomcode/templates/script/.devcontainer/devcontainer.json +1 -1
  19. datacustomcode/templates/script/payload/entrypoint.py +26 -0
  20. datacustomcode/token_provider.py +36 -70
  21. {salesforce_data_customcode-6.0.2.dist-info → salesforce_data_customcode-6.0.3.dev1.dist-info}/METADATA +47 -1
  22. {salesforce_data_customcode-6.0.2.dist-info → salesforce_data_customcode-6.0.3.dev1.dist-info}/RECORD +25 -26
  23. datacustomcode/proxy/__init__.py +0 -14
  24. datacustomcode/proxy/client/__init__.py +0 -14
  25. datacustomcode/proxy/client/base.py +0 -32
  26. {salesforce_data_customcode-6.0.2.dist-info → salesforce_data_customcode-6.0.3.dev1.dist-info}/WHEEL +0 -0
  27. {salesforce_data_customcode-6.0.2.dist-info → salesforce_data_customcode-6.0.3.dev1.dist-info}/entry_points.txt +0 -0
  28. {salesforce_data_customcode-6.0.2.dist-info → salesforce_data_customcode-6.0.3.dev1.dist-info}/licenses/LICENSE.txt +0 -0
@@ -17,8 +17,11 @@ __all__ = [
17
17
  "AuthType",
18
18
  "Client",
19
19
  "Credentials",
20
+ "DefaultSparkLLMGateway",
20
21
  "PrintDataCloudWriter",
21
22
  "QueryAPIDataCloudReader",
23
+ "SparkLLMGateway",
24
+ "llm_gateway_generate_text_col",
22
25
  ]
23
26
 
24
27
 
@@ -44,4 +47,16 @@ def __getattr__(name: str):
44
47
  from datacustomcode.io.reader.query_api import QueryAPIDataCloudReader
45
48
 
46
49
  return QueryAPIDataCloudReader
50
+ elif name == "SparkLLMGateway":
51
+ from datacustomcode.llm_gateway import SparkLLMGateway
52
+
53
+ return SparkLLMGateway
54
+ elif name == "DefaultSparkLLMGateway":
55
+ from datacustomcode.llm_gateway import DefaultSparkLLMGateway
56
+
57
+ return DefaultSparkLLMGateway
58
+ elif name == "llm_gateway_generate_text_col":
59
+ from datacustomcode.client import llm_gateway_generate_text_col
60
+
61
+ return llm_gateway_generate_text_col
47
62
  raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
datacustomcode/cli.py CHANGED
@@ -198,7 +198,6 @@ def zip(path: str, network: str):
198
198
  default=None,
199
199
  help="SF CLI org alias or username. Fetches credentials via `sf org display`.",
200
200
  )
201
- @click.option("--use-in-feature", default=None, hidden=True, deprecated=True)
202
201
  def deploy(
203
202
  path: str,
204
203
  name: str,
@@ -208,7 +207,6 @@ def deploy(
208
207
  profile: str,
209
208
  network: str,
210
209
  sf_cli_org: Optional[str],
211
- use_in_feature: Optional[str],
212
210
  ):
213
211
  from datacustomcode.constants import USE_IN_FEATURE_MAPPING_FOR_CONNECT_API
214
212
  from datacustomcode.deploy import (
@@ -251,14 +249,13 @@ def deploy(
251
249
  logger.info(f"Inferred use_in_feature: {use_in_feature}")
252
250
  else:
253
251
  click.secho(
254
- "Error: Function signature does not match a supported type. "
255
- "Use SearchIndexChunkingV1Request and "
256
- "SearchIndexChunkingV1Response in function signature.",
252
+ "Error: Could not infer function invoke options. "
253
+ "Please provide --use-in-feature",
257
254
  fg="red",
258
255
  )
259
256
  raise click.Abort()
260
257
 
261
- # Map feature names to Connect API names
258
+ # Map user-provided feature names to API names
262
259
  mapped_feature = USE_IN_FEATURE_MAPPING_FOR_CONNECT_API.get(
263
260
  use_in_feature, use_in_feature
264
261
  )
datacustomcode/client.py CHANGED
@@ -18,24 +18,75 @@ from enum import Enum
18
18
  from typing import (
19
19
  TYPE_CHECKING,
20
20
  ClassVar,
21
+ Dict,
21
22
  Optional,
23
+ Union,
22
24
  )
23
25
 
24
26
  from datacustomcode.config import config
25
27
  from datacustomcode.file.path.default import DefaultFindFilePath
26
28
  from datacustomcode.io.reader.base import BaseDataCloudReader
29
+ from datacustomcode.llm_gateway_config import spark_llm_gateway_config
27
30
  from datacustomcode.spark.default import DefaultSparkSessionProvider
28
31
 
29
32
  if TYPE_CHECKING:
30
33
  from pathlib import Path
31
34
 
32
- from pyspark.sql import DataFrame as PySparkDataFrame
35
+ from pyspark.sql import Column, DataFrame as PySparkDataFrame
33
36
 
34
37
  from datacustomcode.io.reader.base import BaseDataCloudReader
35
38
  from datacustomcode.io.writer.base import BaseDataCloudWriter, WriteMode
39
+ from datacustomcode.llm_gateway.spark_base import SparkLLMGateway
36
40
  from datacustomcode.spark.base import BaseSparkSessionProvider
37
41
 
38
42
 
43
+ def _build_spark_llm_gateway() -> "SparkLLMGateway":
44
+ """Instantiate the SDK-configured :class:`SparkLLMGateway`.
45
+
46
+ Raises:
47
+ RuntimeError: If no ``spark_llm_gateway_config`` has been loaded.
48
+ """
49
+ cfg = spark_llm_gateway_config.spark_llm_gateway_config
50
+ if cfg is None:
51
+ raise RuntimeError(
52
+ "spark_llm_gateway_config is not configured. Add a "
53
+ "'spark_llm_gateway_config' section to config.yaml."
54
+ )
55
+ return cfg.to_object()
56
+
57
+
58
+ def llm_gateway_generate_text_col(
59
+ template: str,
60
+ values: Union[Dict[str, "Column"], "Column"],
61
+ model_id: Optional[str] = None,
62
+ ) -> "Column":
63
+ """Build a Spark Column that runs the LLM Gateway per row.
64
+
65
+ Example:
66
+
67
+ >>> df.withColumn(
68
+ ... "greeting__c",
69
+ ... llm_gateway_generate_text_col(
70
+ ... "In one sentence, greet {name} from {city}.",
71
+ ... {"name": col("name__c"), "city": col("homecity__c")},
72
+ ... model_id="sfdc_ai__DefaultGPT4Omni",
73
+ ... ),
74
+ ... )
75
+
76
+ Args:
77
+ template: The prompt template, with ``{field}`` placeholders matching
78
+ keys in ``values``. Substitution uses ``str.format``.
79
+ values: Either a mapping from placeholder name to Spark ``Column``, or
80
+ a single ``Column`` whose value is already a struct.
81
+ model_id: LLM model id. Defaults to ``sfdc_ai__DefaultGPT4Omni``.
82
+
83
+ Returns:
84
+ A Spark ``Column`` that, when evaluated, produces the generated text.
85
+ """
86
+ gateway = Client()._get_spark_llm_gateway()
87
+ return gateway.llm_gateway_generate_text_col(template, values, model_id=model_id)
88
+
89
+
39
90
  class DataCloudObjectType(Enum):
40
91
  DLO = "dlo"
41
92
  DMO = "dmo"
@@ -94,18 +145,21 @@ class Client:
94
145
  finder: Find a file path
95
146
  reader: A custom reader to use for reading Data Cloud objects.
96
147
  writer: A custom writer to use for writing Data Cloud objects.
148
+ spark_llm_gateway: Optional custom :class:`SparkLLMGateway`.
97
149
 
98
150
  Example:
99
151
  >>> client = Client()
100
152
  >>> file_path = client.find_file_path("data.csv")
101
153
  >>> dlo = client.read_dlo("my_dlo")
102
154
  >>> client.write_to_dmo("my_dmo", dlo)
155
+ >>> answer = client.llm_gateway_generate_text("Generate a greeting message")
103
156
  """
104
157
 
105
158
  _instance: ClassVar[Optional[Client]] = None
106
159
  _reader: BaseDataCloudReader
107
160
  _writer: BaseDataCloudWriter
108
161
  _file: DefaultFindFilePath
162
+ _spark_llm_gateway: Optional[SparkLLMGateway]
109
163
  _data_layer_history: dict[DataCloudObjectType, set[str]]
110
164
  _code_type: str
111
165
 
@@ -114,11 +168,13 @@ class Client:
114
168
  reader: Optional[BaseDataCloudReader] = None,
115
169
  writer: Optional[BaseDataCloudWriter] = None,
116
170
  spark_provider: Optional[BaseSparkSessionProvider] = None,
171
+ spark_llm_gateway: Optional[SparkLLMGateway] = None,
117
172
  code_type: str = "script",
118
173
  ) -> Client:
119
174
 
120
175
  if cls._instance is None:
121
176
  cls._instance = super().__new__(cls)
177
+ cls._instance._spark_llm_gateway = spark_llm_gateway
122
178
  # Initialize Readers and Writers from config
123
179
  # and/or provided reader and writer
124
180
  if reader is None or writer is None:
@@ -225,6 +281,41 @@ class Client:
225
281
 
226
282
  return self._file.find_file_path(file_name) # type: ignore[no-any-return]
227
283
 
284
+ def llm_gateway_generate_text(
285
+ self,
286
+ prompt: str,
287
+ model_id: Optional[str] = None,
288
+ ) -> str:
289
+ """Issue a one-shot LLM Gateway call. This is the scalar counterpart to
290
+ :func:`llm_gateway_generate_text_col`: it runs **once** — not per row.
291
+ Use the column helper method instead when you want to fan a prompt out across
292
+ every row of a DataFrame.
293
+
294
+ Example:
295
+
296
+ >>> response = Client().llm_gateway_generate_text(
297
+ ... "Generate a greeting message"
298
+ ... )
299
+
300
+ Args:
301
+ prompt: The literal prompt to send. Plain text — no
302
+ ``{field}`` substitution is performed on this string.
303
+ model_id: LLM model id to target. Defaults to
304
+ ``sfdc_ai__DefaultGPT4Omni`` when ``None``.
305
+
306
+ Returns:
307
+ The generated text as a plain Python ``str``; empty when the
308
+ gateway response carries no generated text.
309
+ """
310
+ return self._get_spark_llm_gateway().llm_gateway_generate_text(
311
+ prompt, model_id=model_id
312
+ )
313
+
314
+ def _get_spark_llm_gateway(self) -> SparkLLMGateway:
315
+ if self._spark_llm_gateway is None:
316
+ self._spark_llm_gateway = _build_spark_llm_gateway()
317
+ return self._spark_llm_gateway
318
+
228
319
  def _validate_data_layer_history_does_not_contain(
229
320
  self, data_cloud_object_type: DataCloudObjectType
230
321
  ) -> None:
@@ -28,3 +28,6 @@ llm_gateway_config:
28
28
  type_config_name: DefaultLLMGateway
29
29
  options:
30
30
  credentials_profile: default
31
+
32
+ spark_llm_gateway_config:
33
+ type_config_name: DefaultSparkLLMGateway
@@ -15,20 +15,23 @@
15
15
 
16
16
  from typing import (
17
17
  ClassVar,
18
+ Generic,
18
19
  Optional,
19
20
  Type,
20
- cast,
21
+ TypeVar,
21
22
  )
22
23
 
23
24
  from datacustomcode.common_config import BaseObjectConfig
24
25
 
26
+ _T = TypeVar("_T")
25
27
 
26
- class CredentialsObjectConfig(BaseObjectConfig):
28
+
29
+ class CredentialsObjectConfig(BaseObjectConfig, Generic[_T]):
27
30
  type_to_create: ClassVar[Type]
28
31
  credentials_profile: Optional[str] = None
29
32
  sf_cli_org: Optional[str] = None
30
33
 
31
- def to_object(self):
34
+ def to_object(self) -> _T:
32
35
  """Create an object instance, automatically including credentials in options"""
33
36
 
34
37
  options = self.options.copy()
@@ -38,4 +41,5 @@ class CredentialsObjectConfig(BaseObjectConfig):
38
41
  options["sf_cli_org"] = self.sf_cli_org
39
42
 
40
43
  type_ = self.type_to_create.subclass_from_config_name(self.type_config_name)
41
- return cast(type_, type_(**options))
44
+ instance: _T = type_(**options)
45
+ return instance
@@ -28,7 +28,7 @@ from datacustomcode.einstein_predictions.base import EinsteinPredictions
28
28
  _E = TypeVar("_E", bound=EinsteinPredictions)
29
29
 
30
30
 
31
- class EinsteinPredictionsObjectConfig(CredentialsObjectConfig, Generic[_E]):
31
+ class EinsteinPredictionsObjectConfig(CredentialsObjectConfig[_E], Generic[_E]):
32
32
  type_to_create: ClassVar[Type[EinsteinPredictions]] = EinsteinPredictions # type: ignore[type-abstract]
33
33
 
34
34
 
@@ -16,6 +16,7 @@
16
16
  """
17
17
  Pydantic models for Search Index Chunking V1
18
18
  """
19
+
19
20
  from enum import Enum
20
21
  from typing import (
21
22
  Dict,
@@ -27,21 +27,11 @@ if TYPE_CHECKING:
27
27
  class WriteMode(str, Enum):
28
28
  APPEND = "append"
29
29
  OVERWRITE = "overwrite"
30
- OVERWRITE_PARTITIONS = "overwrite_partitions" # Deprecated: raises error if used
30
+ OVERWRITE_PARTITIONS = "overwrite_partitions"
31
31
  MERGE = "merge"
32
32
  MERGE_UPSERT_DELETE = "merge_upsert_delete"
33
33
 
34
34
 
35
- class MergeRecordType(str, Enum):
36
- """Values for the _merge_record_type column used by MERGE_UPSERT_DELETE."""
37
-
38
- UPSERT = "UPSERT"
39
- DELETE = "DELETE"
40
-
41
-
42
- MERGE_RECORD_TYPE_COLUMN = "_merge_record_type"
43
-
44
-
45
35
  class BaseDataCloudWriter(BaseDataAccessLayer):
46
36
  """Base class for Data Cloud writers."""
47
37
 
@@ -15,8 +15,14 @@
15
15
 
16
16
  from datacustomcode.llm_gateway.base import LLMGateway
17
17
  from datacustomcode.llm_gateway.default import DefaultLLMGateway
18
+ from datacustomcode.llm_gateway.errors import LLMGatewayCallError
19
+ from datacustomcode.llm_gateway.spark_base import SparkLLMGateway
20
+ from datacustomcode.llm_gateway.spark_default import DefaultSparkLLMGateway
18
21
 
19
22
  __all__ = [
20
23
  "DefaultLLMGateway",
24
+ "DefaultSparkLLMGateway",
21
25
  "LLMGateway",
26
+ "LLMGatewayCallError",
27
+ "SparkLLMGateway",
22
28
  ]
@@ -12,13 +12,25 @@
12
12
  # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
13
  # See the License for the specific language governing permissions and
14
14
  # limitations under the License.
15
+ """Exceptions raised by LLM Gateway implementations."""
16
+
15
17
  from __future__ import annotations
16
18
 
17
- from abc import ABC
19
+ from typing import Optional
18
20
 
19
- from datacustomcode.mixin import UserExtendableNamedConfigMixin
20
21
 
22
+ class LLMGatewayCallError(RuntimeError):
23
+ """Raised when an LLM Gateway call returns an error."""
21
24
 
22
- class BaseProxyAccessLayer(ABC, UserExtendableNamedConfigMixin):
23
- def __init__(self):
24
- pass
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,53 @@
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
+ Union,
24
+ )
25
+
26
+ from datacustomcode.mixin import UserExtendableNamedConfigMixin
27
+
28
+ if TYPE_CHECKING:
29
+ from pyspark.sql import Column
30
+
31
+
32
+ class SparkLLMGateway(ABC, UserExtendableNamedConfigMixin):
33
+ CONFIG_NAME: str
34
+
35
+ def __init__(self, **kwargs: Any) -> None:
36
+ pass
37
+
38
+ @abstractmethod
39
+ def llm_gateway_generate_text(
40
+ self,
41
+ prompt: str,
42
+ model_id: Optional[str] = None,
43
+ ) -> str:
44
+ """Issue a one-shot LLM Gateway call and return the generated text."""
45
+
46
+ @abstractmethod
47
+ def llm_gateway_generate_text_col(
48
+ self,
49
+ template: str,
50
+ values: Union[Dict[str, "Column"], "Column"],
51
+ model_id: Optional[str] = None,
52
+ ) -> "Column":
53
+ """Build a Spark ``Column`` that invokes the LLM Gateway per row."""
@@ -0,0 +1,125 @@
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 typing import (
18
+ TYPE_CHECKING,
19
+ Any,
20
+ Dict,
21
+ Optional,
22
+ Union,
23
+ )
24
+
25
+ from datacustomcode.llm_gateway.spark_base import SparkLLMGateway
26
+
27
+ if TYPE_CHECKING:
28
+ from pyspark.sql import Column
29
+
30
+ from datacustomcode.llm_gateway.base import LLMGateway
31
+
32
+
33
+ _DEFAULT_LLM_MODEL_ID = "sfdc_ai__DefaultGPT4Omni"
34
+
35
+
36
+ class DefaultSparkLLMGateway(SparkLLMGateway):
37
+
38
+ CONFIG_NAME = "DefaultSparkLLMGateway"
39
+
40
+ def __init__(
41
+ self,
42
+ llm_gateway: Optional["LLMGateway"] = None,
43
+ **kwargs: Any,
44
+ ) -> None:
45
+ super().__init__(**kwargs)
46
+ if llm_gateway is None:
47
+ llm_gateway = _build_underlying_gateway()
48
+ self._llm_gateway: "LLMGateway" = llm_gateway
49
+
50
+ def llm_gateway_generate_text(
51
+ self,
52
+ prompt: str,
53
+ model_id: Optional[str] = None,
54
+ ) -> str:
55
+ return _invoke_llm_gateway(self._llm_gateway, prompt, model_id)
56
+
57
+ def llm_gateway_generate_text_col(
58
+ self,
59
+ template: str,
60
+ values: Union[Dict[str, "Column"], "Column"],
61
+ model_id: Optional[str] = None,
62
+ ) -> "Column":
63
+
64
+ from pyspark.sql.functions import struct, udf
65
+ from pyspark.sql.types import StringType
66
+
67
+ if isinstance(values, dict):
68
+ values_col = struct(*[v.alias(k) for k, v in values.items()])
69
+ else:
70
+ values_col = values
71
+
72
+ gateway = self._llm_gateway
73
+
74
+ def _generate(values_row: Any) -> str:
75
+ if values_row is None:
76
+ return ""
77
+ subs = (
78
+ values_row.asDict()
79
+ if hasattr(values_row, "asDict")
80
+ else dict(values_row)
81
+ )
82
+ prompt = template.format(**subs)
83
+ return _invoke_llm_gateway(gateway, prompt, model_id)
84
+
85
+ return udf(_generate, StringType())(values_col)
86
+
87
+
88
+ def _build_underlying_gateway() -> "LLMGateway":
89
+ from datacustomcode.llm_gateway_config import llm_gateway_config
90
+
91
+ cfg = llm_gateway_config.llm_gateway_config
92
+ if cfg is None:
93
+ raise RuntimeError(
94
+ "llm_gateway_config is not configured. Add an 'llm_gateway_config' "
95
+ "section to config.yaml."
96
+ )
97
+ return cfg.to_object()
98
+
99
+
100
+ def _invoke_llm_gateway(
101
+ gateway: "LLMGateway",
102
+ prompt: str,
103
+ model_id: Optional[str],
104
+ ) -> str:
105
+ from datacustomcode.llm_gateway.errors import LLMGatewayCallError
106
+ from datacustomcode.llm_gateway.types.generate_text_request_builder import (
107
+ GenerateTextRequestBuilder,
108
+ )
109
+
110
+ builder = (
111
+ GenerateTextRequestBuilder()
112
+ .set_prompt(prompt)
113
+ .set_model(model_id or _DEFAULT_LLM_MODEL_ID)
114
+ )
115
+ response = gateway.generate_text(builder.build())
116
+ if response.is_error:
117
+ raise LLMGatewayCallError(
118
+ f"LLM Gateway call failed: status_code={response.status_code}, "
119
+ f"error_code={response.error_code!r}, "
120
+ f"message={response.data!r}",
121
+ status=response.status_code,
122
+ error_code=response.error_code or None,
123
+ error_message=str(response.data) if response.data else None,
124
+ )
125
+ return response.text
@@ -21,14 +21,20 @@ from typing import (
21
21
  Union,
22
22
  )
23
23
 
24
- from datacustomcode.common_config import BaseConfig, default_config_file
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.llm_gateway.base import LLMGateway
31
+ from datacustomcode.llm_gateway.spark_base import SparkLLMGateway
27
32
 
28
33
  _E = TypeVar("_E", bound=LLMGateway)
34
+ _S = TypeVar("_S", bound=SparkLLMGateway)
29
35
 
30
36
 
31
- class LLMGatewayObjectConfig(CredentialsObjectConfig, Generic[_E]):
37
+ class LLMGatewayObjectConfig(CredentialsObjectConfig[_E], Generic[_E]):
32
38
  type_to_create: ClassVar[Type[LLMGateway]] = LLMGateway # type: ignore[type-abstract]
33
39
 
34
40
 
@@ -52,6 +58,41 @@ class LLMGatewayConfig(BaseConfig):
52
58
  return self
53
59
 
54
60
 
61
+ class SparkLLMGatewayObjectConfig(BaseObjectConfig, Generic[_S]):
62
+ type_to_create: ClassVar[Type[SparkLLMGateway]] = SparkLLMGateway # type: ignore[type-abstract]
63
+
64
+ def to_object(self) -> SparkLLMGateway:
65
+ type_ = self.type_to_create.subclass_from_config_name(self.type_config_name)
66
+ return type_(**self.options)
67
+
68
+
69
+ class SparkLLMGatewayConfig(BaseConfig):
70
+ spark_llm_gateway_config: Union[
71
+ SparkLLMGatewayObjectConfig[SparkLLMGateway], None
72
+ ] = None
73
+
74
+ def update(self, other: "SparkLLMGatewayConfig") -> "SparkLLMGatewayConfig":
75
+ def merge(
76
+ config_a: Union[SparkLLMGatewayObjectConfig, None],
77
+ config_b: Union[SparkLLMGatewayObjectConfig, None],
78
+ ) -> Union[SparkLLMGatewayObjectConfig, None]:
79
+ if config_a is not None and config_a.force:
80
+ return config_a
81
+ if config_b:
82
+ return config_b
83
+ return config_a
84
+
85
+ self.spark_llm_gateway_config = merge(
86
+ self.spark_llm_gateway_config, other.spark_llm_gateway_config
87
+ )
88
+ return self
89
+
90
+
55
91
  # Global LLM Gateway config instance
56
92
  llm_gateway_config = LLMGatewayConfig()
57
93
  llm_gateway_config.load(default_config_file())
94
+
95
+
96
+ # Global Spark LLM Gateway config instance
97
+ spark_llm_gateway_config = SparkLLMGatewayConfig()
98
+ spark_llm_gateway_config.load(default_config_file())
@@ -5,6 +5,6 @@
5
5
  "dockerfile": "../Dockerfile"
6
6
  },
7
7
  "features": {
8
- "ghcr.io/devcontainers/features/git:1": {}
8
+ "ghcr.io/devcontainers/features/git:1": {},
9
9
  }
10
10
  }
@@ -7,6 +7,9 @@ This function demonstrates the new signature-based invocation with Pydantic mode
7
7
  - Requires Runtime parameter (for agentic capabilities)
8
8
  - Type-safe with direct field access (no wrappers)
9
9
  - Automatic validation and conversion
10
+
11
+ You can use your AI models configured in Salesforce to generate texts.
12
+ See README.md for how to test locally before deploying to Data Cloud.
10
13
  """
11
14
 
12
15
  import logging
@@ -12,6 +12,9 @@ Model: YH_Regression_Python_Predicted_SalePrice_CM_12l_ATC937af934
12
12
  Type: Regression
13
13
  Input: Year_Built__c (numeric)
14
14
  Output: Predicted_SalePrice
15
+
16
+ You can use your AI models configured in Salesforce to make predictions.
17
+ See README.md for how to test locally before deploying to Data Cloud.
15
18
  """
16
19
 
17
20
  import logging
@@ -65,13 +65,9 @@ def make_einstein_prediction(runtime: Runtime) -> None:
65
65
  )
66
66
 
67
67
 
68
- def generate_text(runtime: Runtime):
68
+ def generate_text(runtime: Runtime, prompt: str, model: str = "sfdc_ai__DefaultGPT52"):
69
69
  builder = GenerateTextRequestBuilder()
70
- llm_request = (
71
- builder.set_prompt("Generate 2 dog names")
72
- .set_model("sfdc_ai__DefaultGPT52")
73
- .build()
74
- )
70
+ llm_request = builder.set_prompt(prompt).set_model(model).build()
75
71
  llm_response = runtime.llm_gateway.generate_text(llm_request)
76
72
  logger.info(
77
73
  f"LLM Gateway generate text results - success: [{llm_response.is_success}] "
@@ -88,13 +84,16 @@ def function(request: dict, runtime: Runtime) -> dict:
88
84
  current_seq_no = 1 # Start sequence number from 1
89
85
 
90
86
  """
91
- You can use your AI models configured in Salesforce
92
- to generate texts or predict an outcome.
93
- First configure an external client app before using these AI APIs
94
- https://developer.salesforce.com/docs/ai/agentforce/guide/agent-api-get-started.html#create-a-salesforce-app"
87
+ You can use your AI models configured in Salesforce to generate texts
88
+ or predict an outcome. See README.md for how to test locally before
89
+ deploying to Data Cloud.
90
+
91
+ Example:
92
+
93
+ >>> generated_text = generate_text(runtime, "Generate a greeting message")
94
+ ... prediction = make_einstein_prediction(runtime)
95
+
95
96
  """
96
- # generate_text(runtime)
97
- # make_einstein_prediction(runtime)
98
97
 
99
98
  for item in items:
100
99
  # Item is DocElement as dict
@@ -5,6 +5,6 @@
5
5
  "dockerfile": "../Dockerfile"
6
6
  },
7
7
  "features": {
8
- "ghcr.io/devcontainers/features/git:1": {}
8
+ "ghcr.io/devcontainers/features/git:1": {},
9
9
  }
10
10
  }
@@ -12,6 +12,32 @@ def main():
12
12
  # Perform transformations on the DataFrame
13
13
  df_upper1 = df.withColumn("description__c", upper(col("description__c")))
14
14
 
15
+ """
16
+ You can use your AI models configured in Salesforce to generate column
17
+ values. See README.md for how to test locally before deploying to Data Cloud.
18
+
19
+ Example:
20
+
21
+ >>> from datacustomcode.client import llm_gateway_generate_text_col
22
+ df_generated = df.withColumn(
23
+ ... "greeting__c",
24
+ ... llm_gateway_generate_text_col(
25
+ ... "In one sentence, greet {name} from {city}.",
26
+ ... {"name": col("name__c"), "city": col("homecity__c")},
27
+ ... model_id="sfdc_ai__DefaultGPT4Omni",
28
+ ... ),
29
+ ... )
30
+
31
+ You can also invoke the LLM with a literal plain text prompt — no
32
+ ``{field}`` substitution is performed on this string.
33
+
34
+ Example:
35
+
36
+ >>> generated_text = client.llm_gateway_generate_text(
37
+ ... prompt, model_id
38
+ ... )
39
+ """
40
+
15
41
  # Drop specific columns related to relationships
16
42
  df_upper1 = df_upper1.drop("sfdcorganizationid__c")
17
43
  df_upper1 = df_upper1.drop("kq_id__c")
@@ -101,83 +101,49 @@ class SFCLITokenProvider(TokenProvider):
101
101
 
102
102
  from datacustomcode.deploy import AccessTokenResponse
103
103
 
104
- def _run_sf_command(args: list[str], description: str) -> dict:
105
- try:
106
- result = subprocess.run(
107
- args,
108
- capture_output=True,
109
- text=True,
110
- check=True,
111
- timeout=30,
112
- )
113
- except FileNotFoundError as exc:
114
- raise RuntimeError(
115
- "The 'sf' command was not found. "
116
- "Install Salesforce CLI: "
117
- "https://developer.salesforce.com/tools/salesforcecli"
118
- ) from exc
119
- except subprocess.TimeoutExpired as exc:
120
- raise RuntimeError(
121
- f"'{description}' timed out for org '{self.sf_cli_org}'"
122
- ) from exc
123
- except subprocess.CalledProcessError as exc:
124
- raise RuntimeError(
125
- f"'{description}' failed for org '{self.sf_cli_org}': "
126
- f"{exc.stderr}"
127
- ) from exc
104
+ try:
105
+ result = subprocess.run(
106
+ ["sf", "org", "display", "--target-org", self.sf_cli_org, "--json"],
107
+ capture_output=True,
108
+ text=True,
109
+ check=True,
110
+ timeout=30,
111
+ )
112
+ except FileNotFoundError as exc:
113
+ raise RuntimeError(
114
+ "The 'sf' command was not found. "
115
+ "Install Salesforce CLI: https://developer.salesforce.com/tools/salesforcecli"
116
+ ) from exc
117
+ except subprocess.TimeoutExpired as exc:
118
+ raise RuntimeError(
119
+ f"'sf org display' timed out for org '{self.sf_cli_org}'"
120
+ ) from exc
121
+ except subprocess.CalledProcessError as exc:
122
+ raise RuntimeError(
123
+ f"'sf org display' failed for org '{self.sf_cli_org}': {exc.stderr}"
124
+ ) from exc
128
125
 
129
- try:
130
- data = json.loads(result.stdout)
131
- except json.JSONDecodeError as exc:
132
- raise RuntimeError(
133
- f"Failed to parse JSON from '{description}': {result.stdout}"
134
- ) from exc
126
+ try:
127
+ data = json.loads(result.stdout)
128
+ except json.JSONDecodeError as exc:
129
+ raise RuntimeError(
130
+ f"Failed to parse JSON from 'sf org display': {result.stdout}"
131
+ ) from exc
135
132
 
136
- if data.get("status") != 0:
137
- raise RuntimeError(
138
- f"SF CLI error for org '{self.sf_cli_org}': "
139
- f"{data.get('message', 'unknown error')}"
140
- )
141
- return dict(data)
142
-
143
- # Get org info from sf org display
144
- display_data = _run_sf_command(
145
- ["sf", "org", "display", "--target-org", self.sf_cli_org, "--json"],
146
- "sf org display",
147
- )
148
- result_data = display_data.get("result", {})
149
- instance_url = result_data.get("instanceUrl")
150
- if not instance_url:
133
+ if data.get("status") != 0:
151
134
  raise RuntimeError(
152
- f"'sf org display' did not return an instance URL "
153
- f"for org '{self.sf_cli_org}'"
135
+ f"SF CLI error for org '{self.sf_cli_org}': "
136
+ f"{data.get('message', 'unknown error')}"
154
137
  )
155
138
 
156
- # Try show-access-token first (SF CLI >= 2.136.6); fall back to the
157
- # token from sf org display (older CLIs don't redact it).
158
- access_token = None
159
- try:
160
- token_data = _run_sf_command(
161
- [
162
- "sf",
163
- "org",
164
- "auth",
165
- "show-access-token",
166
- "--target-org",
167
- self.sf_cli_org,
168
- "--json",
169
- ],
170
- "sf org auth show-access-token",
171
- )
172
- access_token = token_data.get("result", {}).get("accessToken")
173
- except RuntimeError:
174
- # Command not available on older SF CLI versions
175
- access_token = result_data.get("accessToken")
139
+ result_data = data.get("result", {})
140
+ access_token = result_data.get("accessToken")
141
+ instance_url = result_data.get("instanceUrl")
176
142
 
177
- if not access_token:
143
+ if not access_token or not instance_url:
178
144
  raise RuntimeError(
179
- f"Could not obtain an access token for org '{self.sf_cli_org}'. "
180
- f"Upgrade SF CLI to 2.136.6+ or ensure the org is authenticated."
145
+ f"'sf org display' did not return an access token or instance URL "
146
+ f"for org '{self.sf_cli_org}'"
181
147
  )
182
148
 
183
149
  return AccessTokenResponse(access_token=access_token, instance_url=instance_url)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: salesforce-data-customcode
3
- Version: 6.0.2
3
+ Version: 6.0.3.dev1
4
4
  Summary: Data Cloud Custom Code SDK
5
5
  License-Expression: Apache-2.0
6
6
  License-File: LICENSE.txt
@@ -330,6 +330,52 @@ Options:
330
330
  - `--function-invoke-opt TEXT`: Currently we support only `UnstructuredChunking` for functions.
331
331
 
332
332
 
333
+ ## Testing LLM Gateway
334
+
335
+ You can use AI models configured in Salesforce to generate responses while transforming your data. Below is a sample code example:
336
+
337
+ ```
338
+ from datacustomcode.client import Client, llm_gateway_generate_text_col
339
+
340
+
341
+ def main():
342
+ client = Client()
343
+ df = client.read_dlo("Input__dll")
344
+ df_generated = df.withColumn(
345
+ "greeting__c",
346
+ llm_gateway_generate_text_col(
347
+ "In one sentence, greet {name} from {city}.",
348
+ {"name": col("name__c"), "city": col("homecity__c")},
349
+ model_id="sfdc_ai__DefaultGPT4Omni", # An AI model in your org
350
+ ),
351
+ )
352
+
353
+ dlo_name = "Output_dll"
354
+ client.write_to_dlo(dlo_name, df_upper1, write_mode=WriteMode.APPEND)
355
+
356
+ greeting = client.llm_gateway_generate_text("In one sentence, generate a greeting message", "sfdc_ai__DefaultGPT52")
357
+
358
+ if __name__ == "__main__":
359
+ main()
360
+ ```
361
+
362
+ In order to test this code on your local machine before deploying it to Data Cloud, you must first set up an External Client App that allows access to the Agent API. Follow this guide to create the ECA https://developer.salesforce.com/docs/ai/agentforce/guide/agent-api-get-started.html#create-a-salesforce-app. You must use `http://localhost:1717/OauthRedirect` as the callback URL.
363
+
364
+ Once the ECA is set up, log in to your org using this ECA
365
+ ```
366
+ sf org login web \
367
+ --alias myorg \
368
+ --instance-url https://{MY_DOMAIN_URL} \
369
+ --client-id {CONSUMER_KEY} \
370
+ --scopes "sfap_api api"
371
+ ```
372
+
373
+ then you can test your code using `myorg` alias
374
+ ```
375
+ datacustomcode run ./payload/entrypoint.py --sf-cli-org myorg
376
+ ```
377
+
378
+
333
379
  ## Docker usage
334
380
 
335
381
  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.
@@ -1,21 +1,21 @@
1
- datacustomcode/__init__.py,sha256=Fj2pIYQqLV_kQrMywrYPj0NsMtHjx4tkvwyqSt-m0E0,1530
1
+ datacustomcode/__init__.py,sha256=qx-X3U9lU_791ZbAxhGqQeyDw6eqTi9-E9AbYnwJfxU,2071
2
2
  datacustomcode/auth.py,sha256=fpSjhIBdv9trC8yq2vuljAix_Euu-4Ah7HDCGhYjOxI,8309
3
- datacustomcode/cli.py,sha256=i7hPoQqJskTmx3Odz2wVgkxBnf_PFaIawI12FXhaRUE,13264
4
- datacustomcode/client.py,sha256=TO9TkIsJjCEMlQMnD-NBGF26fk2K9jpOZjOT_XWKJ0g,9340
3
+ datacustomcode/cli.py,sha256=ObqhOasZSZ8ZNFP6pywV3FpsUOz45_KaZdMNT4BHOKs,13069
4
+ datacustomcode/client.py,sha256=NzPtJkHszRh0GqKUkobPyDUCDauUIaeI-zrac9VR7l8,12813
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=iMs3HPMLHwnesJ9KRD0-CpJqtHRehQvq2w2od9uE0aU,775
8
+ datacustomcode/config.yaml,sha256=Wd8Zd2f1__hRv7166H5IBbU5YoOEOdQNHgAb4ONbFnA,845
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=9yaekNew5ha6bd6rIFERknFROomOeIfikrKXN6jCoq4,18811
12
12
  datacustomcode/einstein_platform_client.py,sha256=ON2B_m--vdbCVVMVcLc81T-BRZ5-UuxVCer8E6OEQPA,4083
13
- datacustomcode/einstein_platform_config.py,sha256=EYnAKz0z1hNN9A8GHtaYm-Ek4GqUi68iYrJ1TSR-J9w,1416
13
+ datacustomcode/einstein_platform_config.py,sha256=6lb_FRbEzdt5a8-2cR15f13ZKw674uvRJ4Xq0o_pQXE,1490
14
14
  datacustomcode/einstein_predictions/__init__.py,sha256=o-iask36phwGA7hGi_sxJpwbITO6h_STDe0J92qD2_M,859
15
15
  datacustomcode/einstein_predictions/base.py,sha256=OBV445dpZRtCF5cpdHH49w_6RU_jZY1A-e7VtHTQCFU,1061
16
16
  datacustomcode/einstein_predictions/impl/default.py,sha256=fEFhrosozhG0yT87w4Gu8wbe1NgHxBqdAduwWHdWGN4,3011
17
17
  datacustomcode/einstein_predictions/types.py,sha256=W7H3sFzm_NdBtnia7O576w0OvM7jwzfGVQmFiZTO8Og,6098
18
- datacustomcode/einstein_predictions_config.py,sha256=dwY3Q5MFBHm60lL27m-Srt5BpcURc_PuKpaW-ALOqw4,2131
18
+ datacustomcode/einstein_predictions_config.py,sha256=oxKgp2u7t5SdHVsasBBxsvd3KAfXuZ9uQXTrtpcCUlU,2135
19
19
  datacustomcode/file/__init__.py,sha256=gamfOD1VnAtEslRBpqh-yKiVjkG_wYWYdSatGIfsN-w,621
20
20
  datacustomcode/file/base.py,sha256=2IUlQufPvpHccgyoeZlKhcktO4pS4NKdha03Whq33Zk,745
21
21
  datacustomcode/file/path/__init__.py,sha256=gamfOD1VnAtEslRBpqh-yKiVjkG_wYWYdSatGIfsN-w,621
@@ -23,7 +23,7 @@ datacustomcode/file/path/default.py,sha256=jyDv61JUlD5H6lUPAp55_B2toMryKItA8Hu2k
23
23
  datacustomcode/function/__init__.py,sha256=oag8FevDxbx1vR0tFhtxPd5SvmUEYDj8esRo4v2qu3Q,749
24
24
  datacustomcode/function/base.py,sha256=H_i80GcsFScvHJJ6bYLry5phTpsEU1KubpR8D3qnsjo,691
25
25
  datacustomcode/function/feature_types/__init__.py,sha256=gamfOD1VnAtEslRBpqh-yKiVjkG_wYWYdSatGIfsN-w,621
26
- datacustomcode/function/feature_types/chunking.py,sha256=ftP90Wp5w7V5WYDIpgD-eIyCRQr7VJUlfnrlvwrZZ8w,7179
26
+ datacustomcode/function/feature_types/chunking.py,sha256=_tCCHj7slD59-aLLEZUFRPOX37q_MEKkL4thi9CNwMw,7180
27
27
  datacustomcode/function/runtime.py,sha256=jUduf-pve-0JAv0Lg0HPyLCZ9_Hgcxe25dI7t-DgX9o,3696
28
28
  datacustomcode/function_utils.py,sha256=y6vaoSSaJ9CeHhjLCNyqzJT4vx6yCePWRxNLz38ddjQ,12920
29
29
  datacustomcode/io/__init__.py,sha256=gamfOD1VnAtEslRBpqh-yKiVjkG_wYWYdSatGIfsN-w,621
@@ -34,23 +34,22 @@ datacustomcode/io/reader/query_api.py,sha256=eVrohrcnTnhSMsGfPRHu5XltjFtGG0hyHt1
34
34
  datacustomcode/io/reader/sf_cli.py,sha256=x5QacVqRZaZSyph1_wwxo67s8r29wsOcUsOaiF9cDWE,6324
35
35
  datacustomcode/io/reader/utils.py,sha256=HlHhPZoHfmWA3mF8kTnd3m4Nd2exaz4NsOJJfQY7Pew,1656
36
36
  datacustomcode/io/writer/__init__.py,sha256=gamfOD1VnAtEslRBpqh-yKiVjkG_wYWYdSatGIfsN-w,621
37
- datacustomcode/io/writer/base.py,sha256=6e2Yszb6BiuN1zCV2rjvZ5CQ0YikQdUgdYAkutoy9pE,1787
37
+ datacustomcode/io/writer/base.py,sha256=RHafJ-Xmh_TnYcN4QfSh0VdIZeIevKmz9sf05adMgxA,1540
38
38
  datacustomcode/io/writer/csv.py,sha256=asty5teBpNQ1fMGHZ7wA3suLhq0sk0lVQPN5x7TOSRk,1555
39
39
  datacustomcode/io/writer/print.py,sha256=0g2sP_1Wb95UIyEELWJt3dqM18Iv_CWhDW3mDxcIhns,5105
40
- datacustomcode/llm_gateway/__init__.py,sha256=xT7J9qZByD80WWk-835JcJSvQx8_NiX6qsBPhq7mU6k,800
40
+ datacustomcode/llm_gateway/__init__.py,sha256=rcoGpSkv36tZuAOcTxKkhNpM0qV03qgmer_ej07Vmfc,1088
41
41
  datacustomcode/llm_gateway/base.py,sha256=CTUhZiZ0JFlmWj6kQpTR4_-mUvqkBKl_Fkdvv9VwxY8,1262
42
42
  datacustomcode/llm_gateway/default.py,sha256=QiwrDaUvrh00I-fYYhmogTtqC5r0fmm-JrXxNQLvTc0,1854
43
+ datacustomcode/llm_gateway/errors.py,sha256=ABjztpJwAYMBlmFX808CHHtu4nD6zA4ECelzNJQkJg0,1197
44
+ datacustomcode/llm_gateway/spark_base.py,sha256=apM3FYDPNz3JTgEdlf-7jeryzTYmSzjJsCIt95ZEpvg,1553
45
+ datacustomcode/llm_gateway/spark_default.py,sha256=ctBzkP1vDxgrmoIC6PvfmwxmlPVotkcADK3-M5qyFKI,3794
43
46
  datacustomcode/llm_gateway/types/__init__.py,sha256=gamfOD1VnAtEslRBpqh-yKiVjkG_wYWYdSatGIfsN-w,621
44
47
  datacustomcode/llm_gateway/types/generate_text_request.py,sha256=ChxOCzp7St1EYgwl4tWiwfrPyVci4zPyBhnEjWc1Vfs,1448
45
48
  datacustomcode/llm_gateway/types/generate_text_request_builder.py,sha256=R4hCgatMPSfWYqaOmeC1IMQj753iDgu7wU992c4tkmk,2565
46
49
  datacustomcode/llm_gateway/types/generate_text_response.py,sha256=NSvj6nvzR4cZ0cVs2-PE9Ct3ZY-xL4MQYknJMd_xdDI,1912
47
50
  datacustomcode/llm_gateway/types/generate_text_response_builder.py,sha256=6MV6JZuTyHUId3Slw9k7m0KBgjj-5S9HI4qtd94Jl2A,1272
48
- datacustomcode/llm_gateway_config.py,sha256=oBfBlLipM3y8uBtFrynzy7HYXt_R9zMdb8gECXNpR9E,1919
51
+ datacustomcode/llm_gateway_config.py,sha256=ECm9CFqOFOaLHwenvVFAjiRSzG9Eg9fY4pb8nHbQ34c,3312
49
52
  datacustomcode/mixin.py,sha256=ZtROqO1W1_D7MV8lw4cw6zzcchJLoZS0aAiXrolYR4k,4914
50
- datacustomcode/proxy/__init__.py,sha256=gamfOD1VnAtEslRBpqh-yKiVjkG_wYWYdSatGIfsN-w,621
51
- datacustomcode/proxy/base.py,sha256=Gw8of0cBgcOSpjnR7PX8pqWnsjHpt1AgKIK7u6k6DBU,846
52
- datacustomcode/proxy/client/__init__.py,sha256=gamfOD1VnAtEslRBpqh-yKiVjkG_wYWYdSatGIfsN-w,621
53
- datacustomcode/proxy/client/base.py,sha256=dWFcc5_5vSBxZmo7k5cwKnJl1_gcjvOZYI2EjBUXuaA,1073
54
53
  datacustomcode/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
55
54
  datacustomcode/run.py,sha256=78IpkuEih3GCO6sQPZLFfiKnrCI-eMtEMLsj-1vUvSE,7362
56
55
  datacustomcode/scan.py,sha256=Zb9mE733H_xaqyDI-LZjjnIcctMC9j1AaD_kcr9qTh4,14325
@@ -59,7 +58,7 @@ datacustomcode/spark/base.py,sha256=tlGqM4LxuLoDa7OxJF5nVeb7phO5uvD1DHBQeH7NMMs,
59
58
  datacustomcode/spark/default.py,sha256=aMB8CaTPYwHbQE_7XqBLQUZPGRdDJcRL72qTVh0aG7M,1433
60
59
  datacustomcode/template.py,sha256=FNNi8YPfd-JB-hUt1DDWSFK5AqwKnTnRSy_KpqYluaU,3282
61
60
  datacustomcode/templates/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
62
- datacustomcode/templates/function/.devcontainer/devcontainer.json,sha256=21RNTadhC-rynON2-VOtr5U174Hxnr_MA4RQ4592rsg,166
61
+ datacustomcode/templates/function/.devcontainer/devcontainer.json,sha256=lWMFk-SdTKBlQt_VJrMn8gZPmvT4tfcjRpCNhLdElZc,167
63
62
  datacustomcode/templates/function/Dockerfile.dependencies,sha256=AfHRddm5l3ujv4vdrf0d-SMB-qxPCOa0XQm6ptP2Euw,174
64
63
  datacustomcode/templates/function/README.md,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
65
64
  datacustomcode/templates/function/build_native_dependencies.sh,sha256=dk9vhzlObdsFAO5BtxP7ioOGDBM8JVwJHp_W0712Nlk,222
@@ -67,17 +66,17 @@ datacustomcode/templates/function/chunking/payload/config.json,sha256=RBNvo1WzZ4
67
66
  datacustomcode/templates/function/chunking/payload/entrypoint.py,sha256=tzpQJjODulwzLilOoKdOo8GjpdIlCDByjnawpHIQYQA,4517
68
67
  datacustomcode/templates/function/chunking/requirements.txt,sha256=Ih0KsVdiTdo7KuIsqFB_AJMRT2r0ao_xDEb3hiDmf48,46
69
68
  datacustomcode/templates/function/example/chunking_with_llm/config.json,sha256=EvjuOVssRg2SjcUb5F50RRMGDqag_f69bv8AOflaK1g,38
70
- datacustomcode/templates/function/example/chunking_with_llm/entrypoint.py,sha256=BgxWWQeWUVtLDDH21HXHX9HZ5atO6A2_fokw8LsSeyI,3295
69
+ datacustomcode/templates/function/example/chunking_with_llm/entrypoint.py,sha256=6AMVf_busCCDALktG8vkCtjnzJheMgM-OOLML5iLKOg,3437
71
70
  datacustomcode/templates/function/example/chunking_with_llm/files/chunking_prompt.txt,sha256=w_gSYNXwZmbMTMjZ_YYwm1IpMsVpI3hyC3ew60zqs_0,446
72
71
  datacustomcode/templates/function/example/chunking_with_llm/tests/test.json,sha256=ZcH6QGEWu03dc922Za3vdx6hJY08nfGPie9kkeup-sc,9457
73
72
  datacustomcode/templates/function/example/chunking_with_prediction/config.json,sha256=EvjuOVssRg2SjcUb5F50RRMGDqag_f69bv8AOflaK1g,38
74
- datacustomcode/templates/function/example/chunking_with_prediction/entrypoint.py,sha256=4Rtv9HAPzDNmxbDt0OBoS4Y5SmCDGnQDpy1i800fXPY,7578
73
+ datacustomcode/templates/function/example/chunking_with_prediction/entrypoint.py,sha256=w25_WucA-tUN3O6j-Od_WsW4HDH1Dfp2uJYNfVs_0M4,7722
75
74
  datacustomcode/templates/function/example/chunking_with_prediction/tests/test.json,sha256=2EE0c0n-s6eOayb_JrBqeKkXzyxieOyHK3_Mbm--Nmo,1073
76
75
  datacustomcode/templates/function/payload/config.json,sha256=RBNvo1WzZ4oRRq0W9-hknpT7T8If536DEMBg9hyq_4o,2
77
- datacustomcode/templates/function/payload/entrypoint.py,sha256=DrH73aAkU_ua6IHBRYr9SkMtX5pEFvNJYew8zXQL5TQ,5705
76
+ datacustomcode/templates/function/payload/entrypoint.py,sha256=uWb9mT7GaOvVWBK8P7vHS1GNbNaH--rSLV8J1v0eTHY,5667
78
77
  datacustomcode/templates/function/requirements-dev.txt,sha256=qT2-m2FOgiYPTvqaNy0yRzIAXqHgR5b8stccYXsXVi8,88
79
78
  datacustomcode/templates/function/requirements.txt,sha256=qJbqzs5z0Hrx590U3T7dHq_m8UQEx2TdhgrJSz-QHIQ,40
80
- datacustomcode/templates/script/.devcontainer/devcontainer.json,sha256=21RNTadhC-rynON2-VOtr5U174Hxnr_MA4RQ4592rsg,166
79
+ datacustomcode/templates/script/.devcontainer/devcontainer.json,sha256=lWMFk-SdTKBlQt_VJrMn8gZPmvT4tfcjRpCNhLdElZc,167
81
80
  datacustomcode/templates/script/Dockerfile,sha256=l8HEy5X7BsW9Gi7rBYM6G2czwk-1832RRIos7xyFzpI,463
82
81
  datacustomcode/templates/script/Dockerfile.dependencies,sha256=vBbTfgcBkVQVCnrJZE_QEx89ddTQrLZGeyVTZXU8TI8,206
83
82
  datacustomcode/templates/script/README.md,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -87,13 +86,13 @@ datacustomcode/templates/script/examples/employee_hierarchy/employee_data.csv,sh
87
86
  datacustomcode/templates/script/examples/employee_hierarchy/entrypoint.py,sha256=Mfm3iQtEHTQRW6cmZTwRKdTh3IeGWR-teXrd6x-YLSY,2223
88
87
  datacustomcode/templates/script/jupyterlab.sh,sha256=IHR3YQ8d_busuyvesByhJgzCKULIFPI-Hqogs0NPhCs,2432
89
88
  datacustomcode/templates/script/payload/config.json,sha256=0d2mEMt4NHIeOUTsgiPMuRKdOLIQxmh-Wq-IfEqU-gc,28
90
- datacustomcode/templates/script/payload/entrypoint.py,sha256=qpFA-jkhaYYF2CFNQ7_7MZVefU7SxnoMQIQYi7ox2dY,691
89
+ datacustomcode/templates/script/payload/entrypoint.py,sha256=Ox2guNka3r0GW2jzKsmH7AP70j1-1Qqr_jhtH5dkPOM,1590
91
90
  datacustomcode/templates/script/requirements-dev.txt,sha256=OWwuy1awesqOZOS3Zizmdd6BDnSePpGFN5bq5IrALvc,176
92
91
  datacustomcode/templates/script/requirements.txt,sha256=qJbqzs5z0Hrx590U3T7dHq_m8UQEx2TdhgrJSz-QHIQ,40
93
- datacustomcode/token_provider.py,sha256=kGPxdxGZEr6VFknW7jFb9P6wOvRaRo-sfdBN2cUjEZY,6792
92
+ datacustomcode/token_provider.py,sha256=KPj_ca6mcBmhXHaPmZjAI4it8QvOWdwKJL8yfqxfYwU,5446
94
93
  datacustomcode/version.py,sha256=9LlbVrzwBvut1L308QbwNBgxdQk5CrghH39JARVSxms,989
95
- salesforce_data_customcode-6.0.2.dist-info/METADATA,sha256=5VE2LhqzlGE8-BNLoPZM5qqtYnteWP776xC2XK7GGFE,20162
96
- salesforce_data_customcode-6.0.2.dist-info/WHEEL,sha256=eY7nduwzv-ldUxpzbRlxwvC693Hg6PX8bWDjEHjZ_dk,88
97
- salesforce_data_customcode-6.0.2.dist-info/entry_points.txt,sha256=WpQ94UB7UuRCYGOLtJV3vAgm1tl7iVf43VYRWIuNu5Y,74
98
- salesforce_data_customcode-6.0.2.dist-info/licenses/LICENSE.txt,sha256=iOi8EmQpfkFhMENi7VYtkl2EqS14pEOccoXHiW2dyPU,11443
99
- salesforce_data_customcode-6.0.2.dist-info/RECORD,,
94
+ salesforce_data_customcode-6.0.3.dev1.dist-info/METADATA,sha256=wmHyC6LrpV7uYwKuMBQEwfrztUX9gLb7v7YRwsYYGAg,21724
95
+ salesforce_data_customcode-6.0.3.dev1.dist-info/WHEEL,sha256=eY7nduwzv-ldUxpzbRlxwvC693Hg6PX8bWDjEHjZ_dk,88
96
+ salesforce_data_customcode-6.0.3.dev1.dist-info/entry_points.txt,sha256=WpQ94UB7UuRCYGOLtJV3vAgm1tl7iVf43VYRWIuNu5Y,74
97
+ salesforce_data_customcode-6.0.3.dev1.dist-info/licenses/LICENSE.txt,sha256=iOi8EmQpfkFhMENi7VYtkl2EqS14pEOccoXHiW2dyPU,11443
98
+ salesforce_data_customcode-6.0.3.dev1.dist-info/RECORD,,
@@ -1,14 +0,0 @@
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.
@@ -1,14 +0,0 @@
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.
@@ -1,32 +0,0 @@
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 abstractmethod
18
-
19
- from datacustomcode.proxy.base import BaseProxyAccessLayer
20
-
21
-
22
- class BaseProxyClient(BaseProxyAccessLayer):
23
- def __init__(self):
24
- pass
25
-
26
- @abstractmethod
27
- def call_llm_gateway(self, llmModelId: str, prompt: str, maxTokens: int) -> str: ...
28
-
29
- @abstractmethod
30
- def llm_gateway_generate_text(
31
- self, template, values, llmModelId: str, maxTokens: int
32
- ): ...