salesforce-data-customcode 6.0.1__py3-none-any.whl → 6.0.2.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 (31) hide show
  1. datacustomcode/__init__.py +15 -0
  2. datacustomcode/cli.py +10 -14
  3. datacustomcode/client.py +102 -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 +4 -0
  10. datacustomcode/llm_gateway/default.py +2 -0
  11. datacustomcode/llm_gateway/spark_base.py +55 -0
  12. datacustomcode/llm_gateway/spark_default.py +119 -0
  13. datacustomcode/llm_gateway/types/generate_text_request.py +7 -0
  14. datacustomcode/llm_gateway/types/generate_text_request_builder.py +6 -0
  15. datacustomcode/llm_gateway_config.py +43 -2
  16. datacustomcode/templates/function/.devcontainer/devcontainer.json +1 -1
  17. datacustomcode/templates/function/example/chunking_with_llm/entrypoint.py +3 -0
  18. datacustomcode/templates/function/example/chunking_with_prediction/entrypoint.py +3 -0
  19. datacustomcode/templates/function/payload/entrypoint.py +11 -12
  20. datacustomcode/templates/script/.devcontainer/devcontainer.json +1 -1
  21. datacustomcode/templates/script/payload/entrypoint.py +27 -0
  22. datacustomcode/token_provider.py +36 -63
  23. {salesforce_data_customcode-6.0.1.dist-info → salesforce_data_customcode-6.0.2.dev1.dist-info}/METADATA +48 -1
  24. {salesforce_data_customcode-6.0.1.dist-info → salesforce_data_customcode-6.0.2.dev1.dist-info}/RECORD +27 -29
  25. datacustomcode/proxy/__init__.py +0 -14
  26. datacustomcode/proxy/base.py +0 -24
  27. datacustomcode/proxy/client/__init__.py +0 -14
  28. datacustomcode/proxy/client/base.py +0 -32
  29. {salesforce_data_customcode-6.0.1.dist-info → salesforce_data_customcode-6.0.2.dev1.dist-info}/WHEEL +0 -0
  30. {salesforce_data_customcode-6.0.1.dist-info → salesforce_data_customcode-6.0.2.dev1.dist-info}/entry_points.txt +0 -0
  31. {salesforce_data_customcode-6.0.1.dist-info → salesforce_data_customcode-6.0.2.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,11 +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(
202
- "--use-in-feature",
203
- default="SearchIndexChunking",
204
- help="Feature where this function will be used.",
205
- )
206
201
  def deploy(
207
202
  path: str,
208
203
  name: str,
@@ -212,7 +207,6 @@ def deploy(
212
207
  profile: str,
213
208
  network: str,
214
209
  sf_cli_org: Optional[str],
215
- use_in_feature: Optional[str],
216
210
  ):
217
211
  from datacustomcode.constants import USE_IN_FEATURE_MAPPING_FOR_CONNECT_API
218
212
  from datacustomcode.deploy import (
@@ -248,18 +242,20 @@ def deploy(
248
242
  )
249
243
 
250
244
  if package_type == "function":
251
- # Try to infer use_in_feature from function signature; fall back to
252
- # the explicit flag value (defaults to SearchIndexChunking)
245
+ # Infer use_in_feature from function signature
253
246
  entrypoint_path = os.path.join(path, ENTRYPOINT_FILE)
254
- inferred = infer_use_in_feature(entrypoint_path)
255
- if inferred:
256
- use_in_feature = inferred
247
+ use_in_feature = infer_use_in_feature(entrypoint_path)
248
+ if use_in_feature:
257
249
  logger.info(f"Inferred use_in_feature: {use_in_feature}")
258
250
  else:
259
- logger.info(f"Using use_in_feature: {use_in_feature}")
251
+ click.secho(
252
+ "Error: Could not infer function invoke options. "
253
+ "Please provide --use-in-feature",
254
+ fg="red",
255
+ )
256
+ raise click.Abort()
260
257
 
261
- assert use_in_feature is not None
262
- # Map feature names to Connect API names
258
+ # Map user-provided feature names to API names
263
259
  mapped_feature = USE_IN_FEATURE_MAPPING_FOR_CONNECT_API.get(
264
260
  use_in_feature, use_in_feature
265
261
  )
datacustomcode/client.py CHANGED
@@ -18,24 +18,80 @@ 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
+ max_tokens: Optional[int] = None,
63
+ ) -> "Column":
64
+ """Build a Spark Column that runs the LLM Gateway per row.
65
+
66
+ Example:
67
+
68
+ >>> df.withColumn(
69
+ ... "greeting__c",
70
+ ... llm_gateway_generate_text_col(
71
+ ... "In one sentence, greet {name} from {city}.",
72
+ ... {"name": col("name__c"), "city": col("homecity__c")},
73
+ ... model_id="sfdc_ai__DefaultGPT4Omni",
74
+ ... max_tokens=100,
75
+ ... ),
76
+ ... )
77
+
78
+ Args:
79
+ template: The prompt template, with ``{field}`` placeholders matching
80
+ keys in ``values``. Substitution uses ``str.format``.
81
+ values: Either a mapping from placeholder name to Spark ``Column``, or
82
+ a single ``Column`` whose value is already a struct.
83
+ model_id: LLM model id. Defaults to ``sfdc_ai__DefaultGPT4Omni``.
84
+ max_tokens: Maximum tokens to generate. Defaults to 200.
85
+
86
+ Returns:
87
+ A Spark ``Column`` that, when evaluated, produces the generated text.
88
+ """
89
+ gateway = Client()._get_spark_llm_gateway()
90
+ return gateway.llm_gateway_generate_text_col(
91
+ template, values, model_id=model_id, max_tokens=max_tokens
92
+ )
93
+
94
+
39
95
  class DataCloudObjectType(Enum):
40
96
  DLO = "dlo"
41
97
  DMO = "dmo"
@@ -94,18 +150,23 @@ class Client:
94
150
  finder: Find a file path
95
151
  reader: A custom reader to use for reading Data Cloud objects.
96
152
  writer: A custom writer to use for writing Data Cloud objects.
153
+ spark_llm_gateway: Optional custom :class:`SparkLLMGateway`. When
154
+ omitted, the gateway is lazily resolved from
155
+ ``spark_llm_gateway_config``.
97
156
 
98
157
  Example:
99
158
  >>> client = Client()
100
159
  >>> file_path = client.find_file_path("data.csv")
101
160
  >>> dlo = client.read_dlo("my_dlo")
102
161
  >>> client.write_to_dmo("my_dmo", dlo)
162
+ >>> answer = client.llm_gateway_generate_text("Generate a greeting message")
103
163
  """
104
164
 
105
165
  _instance: ClassVar[Optional[Client]] = None
106
166
  _reader: BaseDataCloudReader
107
167
  _writer: BaseDataCloudWriter
108
168
  _file: DefaultFindFilePath
169
+ _spark_llm_gateway: Optional[SparkLLMGateway]
109
170
  _data_layer_history: dict[DataCloudObjectType, set[str]]
110
171
  _code_type: str
111
172
 
@@ -114,11 +175,13 @@ class Client:
114
175
  reader: Optional[BaseDataCloudReader] = None,
115
176
  writer: Optional[BaseDataCloudWriter] = None,
116
177
  spark_provider: Optional[BaseSparkSessionProvider] = None,
178
+ spark_llm_gateway: Optional[SparkLLMGateway] = None,
117
179
  code_type: str = "script",
118
180
  ) -> Client:
119
181
 
120
182
  if cls._instance is None:
121
183
  cls._instance = super().__new__(cls)
184
+ cls._instance._spark_llm_gateway = spark_llm_gateway
122
185
  # Initialize Readers and Writers from config
123
186
  # and/or provided reader and writer
124
187
  if reader is None or writer is None:
@@ -225,6 +288,44 @@ class Client:
225
288
 
226
289
  return self._file.find_file_path(file_name) # type: ignore[no-any-return]
227
290
 
291
+ def llm_gateway_generate_text(
292
+ self,
293
+ prompt: str,
294
+ model_id: Optional[str] = None,
295
+ max_tokens: Optional[int] = None,
296
+ ) -> str:
297
+ """Issue a one-shot LLM Gateway call. This is the scalar counterpart to
298
+ :func:`llm_gateway_generate_text_col`: it runs **once** — not per row.
299
+ Use the column helper method instead when you want to fan a prompt out across
300
+ every row of a DataFrame.
301
+
302
+ Example:
303
+
304
+ >>> response = Client().llm_gateway_generate_text(
305
+ ... "Generate a greeting message"
306
+ ... )
307
+
308
+ Args:
309
+ prompt: The literal prompt to send. Plain text — no
310
+ ``{field}`` substitution is performed on this string.
311
+ model_id: LLM model id to target. Defaults to
312
+ ``sfdc_ai__DefaultGPT4Omni`` when ``None``.
313
+ max_tokens: Hard upper bound on the number of tokens the model
314
+ may generate. Defaults to 200 when ``None``.
315
+
316
+ Returns:
317
+ The generated text as a plain Python ``str``; empty when the
318
+ gateway response carries no generated text.
319
+ """
320
+ return self._get_spark_llm_gateway().llm_gateway_generate_text(
321
+ prompt, model_id=model_id, max_tokens=max_tokens
322
+ )
323
+
324
+ def _get_spark_llm_gateway(self) -> SparkLLMGateway:
325
+ if self._spark_llm_gateway is None:
326
+ self._spark_llm_gateway = _build_spark_llm_gateway()
327
+ return self._spark_llm_gateway
328
+
228
329
  def _validate_data_layer_history_does_not_contain(
229
330
  self, data_cloud_object_type: DataCloudObjectType
230
331
  ) -> 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,12 @@
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.spark_base import SparkLLMGateway
19
+ from datacustomcode.llm_gateway.spark_default import DefaultSparkLLMGateway
18
20
 
19
21
  __all__ = [
20
22
  "DefaultLLMGateway",
23
+ "DefaultSparkLLMGateway",
21
24
  "LLMGateway",
25
+ "SparkLLMGateway",
22
26
  ]
@@ -34,6 +34,8 @@ class DefaultLLMGateway(EinsteinPlatformClient, LLMGateway):
34
34
 
35
35
  payload: Dict[str, Any] = {"prompt": request.prompt}
36
36
 
37
+ if request.max_tokens is not None:
38
+ payload["max_tokens"] = request.max_tokens
37
39
  if request.localization:
38
40
  payload["localization"] = request.localization
39
41
  if request.tags:
@@ -0,0 +1,55 @@
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
+ max_tokens: Optional[int] = None,
44
+ ) -> str:
45
+ """Issue a one-shot LLM Gateway call and return the generated text."""
46
+
47
+ @abstractmethod
48
+ def llm_gateway_generate_text_col(
49
+ self,
50
+ template: str,
51
+ values: Union[Dict[str, "Column"], "Column"],
52
+ model_id: Optional[str] = None,
53
+ max_tokens: Optional[int] = None,
54
+ ) -> "Column":
55
+ """Build a Spark ``Column`` that invokes the LLM Gateway per row."""
@@ -0,0 +1,119 @@
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
+ _DEFAULT_LLM_MAX_TOKENS = 200
35
+
36
+
37
+ class DefaultSparkLLMGateway(SparkLLMGateway):
38
+
39
+ CONFIG_NAME = "DefaultSparkLLMGateway"
40
+
41
+ def __init__(
42
+ self,
43
+ llm_gateway: Optional["LLMGateway"] = None,
44
+ **kwargs: Any,
45
+ ) -> None:
46
+ super().__init__(**kwargs)
47
+ if llm_gateway is None:
48
+ llm_gateway = _build_underlying_gateway()
49
+ self._llm_gateway: "LLMGateway" = llm_gateway
50
+
51
+ def llm_gateway_generate_text(
52
+ self,
53
+ prompt: str,
54
+ model_id: Optional[str] = None,
55
+ max_tokens: Optional[int] = None,
56
+ ) -> str:
57
+ return _invoke_llm_gateway(self._llm_gateway, prompt, model_id, max_tokens)
58
+
59
+ def llm_gateway_generate_text_col(
60
+ self,
61
+ template: str,
62
+ values: Union[Dict[str, "Column"], "Column"],
63
+ model_id: Optional[str] = None,
64
+ max_tokens: Optional[int] = None,
65
+ ) -> "Column":
66
+
67
+ from pyspark.sql.functions import struct, udf
68
+ from pyspark.sql.types import StringType
69
+
70
+ if isinstance(values, dict):
71
+ values_col = struct(*[v.alias(k) for k, v in values.items()])
72
+ else:
73
+ values_col = values
74
+
75
+ gateway = self._llm_gateway
76
+
77
+ def _generate(values_row: Any) -> str:
78
+ if values_row is None:
79
+ return ""
80
+ subs = (
81
+ values_row.asDict()
82
+ if hasattr(values_row, "asDict")
83
+ else dict(values_row)
84
+ )
85
+ prompt = template.format(**subs)
86
+ return _invoke_llm_gateway(gateway, prompt, model_id, max_tokens)
87
+
88
+ return udf(_generate, StringType())(values_col)
89
+
90
+
91
+ def _build_underlying_gateway() -> "LLMGateway":
92
+ from datacustomcode.llm_gateway_config import llm_gateway_config
93
+
94
+ cfg = llm_gateway_config.llm_gateway_config
95
+ if cfg is None:
96
+ raise RuntimeError(
97
+ "llm_gateway_config is not configured. Add an 'llm_gateway_config' "
98
+ "section to config.yaml."
99
+ )
100
+ return cfg.to_object()
101
+
102
+
103
+ def _invoke_llm_gateway(
104
+ gateway: "LLMGateway",
105
+ prompt: str,
106
+ model_id: Optional[str],
107
+ max_tokens: Optional[int],
108
+ ) -> str:
109
+ from datacustomcode.llm_gateway.types.generate_text_request_builder import (
110
+ GenerateTextRequestBuilder,
111
+ )
112
+
113
+ builder = (
114
+ GenerateTextRequestBuilder()
115
+ .set_prompt(prompt)
116
+ .set_model(model_id or _DEFAULT_LLM_MODEL_ID)
117
+ .set_max_tokens(max_tokens or _DEFAULT_LLM_MAX_TOKENS)
118
+ )
119
+ return gateway.generate_text(builder.build()).text
@@ -40,6 +40,13 @@ class GenerateTextRequest(BaseModel):
40
40
  )
41
41
  model_name: str = Field(..., min_length=1, description="Name of the model to use")
42
42
  prompt: str = Field(..., description="Input prompt")
43
+ max_tokens: Optional[int] = Field(
44
+ default=None,
45
+ ge=1,
46
+ description=(
47
+ "Maximum number of tokens to generate. If None, server default applies."
48
+ ),
49
+ )
43
50
  localization: Optional[Dict[str, Any]] = Field(
44
51
  default=None, description="Localization settings"
45
52
  )
@@ -26,6 +26,7 @@ class GenerateTextRequestBuilder:
26
26
  def __init__(self) -> None:
27
27
  self._prompt = ""
28
28
  self._model_name = ""
29
+ self._max_tokens: Optional[int] = None
29
30
  self._localization: Optional[Dict[str, Any]] = None
30
31
  self._tags: Optional[Dict[str, Any]] = None
31
32
 
@@ -37,6 +38,10 @@ class GenerateTextRequestBuilder:
37
38
  self._model_name = model_name
38
39
  return self
39
40
 
41
+ def set_max_tokens(self, max_tokens: int) -> "GenerateTextRequestBuilder":
42
+ self._max_tokens = max_tokens
43
+ return self
44
+
40
45
  def set_localization(
41
46
  self,
42
47
  localization: Optional[Dict[str, Any]] = None,
@@ -75,6 +80,7 @@ class GenerateTextRequestBuilder:
75
80
  request = GenerateTextRequest(
76
81
  prompt=self._prompt,
77
82
  model_name=self._model_name,
83
+ max_tokens=self._max_tokens,
78
84
  localization=self._localization,
79
85
  tags=self._tags,
80
86
  )
@@ -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,33 @@ 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
+ ... max_tokens=100,
29
+ ... ),
30
+ ... )
31
+
32
+ You can also invoke the LLM with a literal plain text prompt — no
33
+ ``{field}`` substitution is performed on this string.
34
+
35
+ Example:
36
+
37
+ >>> generated_text = client.llm_gateway_generate_text(
38
+ ... prompt, model_id, max_tokens
39
+ ... )
40
+ """
41
+
15
42
  # Drop specific columns related to relationships
16
43
  df_upper1 = df_upper1.drop("sfdcorganizationid__c")
17
44
  df_upper1 = df_upper1.drop("kq_id__c")
@@ -101,75 +101,48 @@ 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 instanceUrl 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
- instance_url = display_data.get("result", {}).get("instanceUrl")
149
- if not instance_url:
133
+ if data.get("status") != 0:
150
134
  raise RuntimeError(
151
- f"'sf org display' did not return an instance URL "
152
- 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')}"
153
137
  )
154
138
 
155
- # Get access token via show-access-token (newer SF CLI versions
156
- # redact the token in sf org display)
157
- token_data = _run_sf_command(
158
- [
159
- "sf",
160
- "org",
161
- "auth",
162
- "show-access-token",
163
- "--target-org",
164
- self.sf_cli_org,
165
- "--json",
166
- ],
167
- "sf org auth show-access-token",
168
- )
169
- access_token = token_data.get("result", {}).get("accessToken")
170
- if not access_token:
139
+ result_data = data.get("result", {})
140
+ access_token = result_data.get("accessToken")
141
+ instance_url = result_data.get("instanceUrl")
142
+
143
+ if not access_token or not instance_url:
171
144
  raise RuntimeError(
172
- f"'sf org auth show-access-token' did not return an access token "
145
+ f"'sf org display' did not return an access token or instance URL "
173
146
  f"for org '{self.sf_cli_org}'"
174
147
  )
175
148
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: salesforce-data-customcode
3
- Version: 6.0.1
3
+ Version: 6.0.2.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,53 @@ 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
+ max_tokens=100,
351
+ ),
352
+ )
353
+
354
+ dlo_name = "Output_dll"
355
+ client.write_to_dlo(dlo_name, df_upper1, write_mode=WriteMode.APPEND)
356
+
357
+ greeting = client.llm_gateway_generate_text("In one sentence, generate a greeting message", "sfdc_ai__DefaultGPT52")
358
+
359
+ if __name__ == "__main__":
360
+ main()
361
+ ```
362
+
363
+ 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.
364
+
365
+ Once the ECA is set up, log in to your org using this ECA
366
+ ```
367
+ sf org login web \
368
+ --alias myorg \
369
+ --instance-url https://{MY_DOMAIN_URL} \
370
+ --client-id {CONSUMER_KEY} \
371
+ --scopes "sfap_api api"
372
+ ```
373
+
374
+ then you can test your code using `myorg` alias
375
+ ```
376
+ datacustomcode run ./payload/entrypoint.py --sf-cli-org myorg
377
+ ```
378
+
379
+
333
380
  ## Docker usage
334
381
 
335
382
  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=tAtSSUMoVQtME_y1fxpNq4Lw1vfQsP1a6UxlEPiV9Sw,13237
4
- datacustomcode/client.py,sha256=TO9TkIsJjCEMlQMnD-NBGF26fk2K9jpOZjOT_XWKJ0g,9340
3
+ datacustomcode/cli.py,sha256=ObqhOasZSZ8ZNFP6pywV3FpsUOz45_KaZdMNT4BHOKs,13069
4
+ datacustomcode/client.py,sha256=prAufc2_KAKdy0XX-nYhuD22a93GqQCsQuql9ru-8dY,13294
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,21 @@ 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=ZYEMvQlnWzdSdBwYrhBQGmlfBBqsfmhAy4KViyCBFVA,995
41
41
  datacustomcode/llm_gateway/base.py,sha256=CTUhZiZ0JFlmWj6kQpTR4_-mUvqkBKl_Fkdvv9VwxY8,1262
42
- datacustomcode/llm_gateway/default.py,sha256=QiwrDaUvrh00I-fYYhmogTtqC5r0fmm-JrXxNQLvTc0,1854
42
+ datacustomcode/llm_gateway/default.py,sha256=HVUc5NihNAQMVgRAJ8w2CBDX4mRLl8FRkpCJWv9FAOU,1952
43
+ datacustomcode/llm_gateway/spark_base.py,sha256=h9fBrhBSSthCMBi6DjryxMKp_WSLdoMDg6a3FgO4oKI,1637
44
+ datacustomcode/llm_gateway/spark_default.py,sha256=2-u9Oy936kj35wv8_qBSc-xtqnC7WNkgprhcuroP7U8,3524
43
45
  datacustomcode/llm_gateway/types/__init__.py,sha256=gamfOD1VnAtEslRBpqh-yKiVjkG_wYWYdSatGIfsN-w,621
44
- datacustomcode/llm_gateway/types/generate_text_request.py,sha256=ChxOCzp7St1EYgwl4tWiwfrPyVci4zPyBhnEjWc1Vfs,1448
45
- datacustomcode/llm_gateway/types/generate_text_request_builder.py,sha256=R4hCgatMPSfWYqaOmeC1IMQj753iDgu7wU992c4tkmk,2565
46
+ datacustomcode/llm_gateway/types/generate_text_request.py,sha256=pHFp9r12xT_GlFm873kQEfrHCdoJI5sJGrCRVqqN3vg,1647
47
+ datacustomcode/llm_gateway/types/generate_text_request_builder.py,sha256=P2lBwyjGQvqf8G9PR4pNeN02AeYri1XCOvlBeRqAJNw,2791
46
48
  datacustomcode/llm_gateway/types/generate_text_response.py,sha256=NSvj6nvzR4cZ0cVs2-PE9Ct3ZY-xL4MQYknJMd_xdDI,1912
47
49
  datacustomcode/llm_gateway/types/generate_text_response_builder.py,sha256=6MV6JZuTyHUId3Slw9k7m0KBgjj-5S9HI4qtd94Jl2A,1272
48
- datacustomcode/llm_gateway_config.py,sha256=oBfBlLipM3y8uBtFrynzy7HYXt_R9zMdb8gECXNpR9E,1919
50
+ datacustomcode/llm_gateway_config.py,sha256=ECm9CFqOFOaLHwenvVFAjiRSzG9Eg9fY4pb8nHbQ34c,3312
49
51
  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
52
  datacustomcode/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
55
53
  datacustomcode/run.py,sha256=78IpkuEih3GCO6sQPZLFfiKnrCI-eMtEMLsj-1vUvSE,7362
56
54
  datacustomcode/scan.py,sha256=Zb9mE733H_xaqyDI-LZjjnIcctMC9j1AaD_kcr9qTh4,14325
@@ -59,7 +57,7 @@ datacustomcode/spark/base.py,sha256=tlGqM4LxuLoDa7OxJF5nVeb7phO5uvD1DHBQeH7NMMs,
59
57
  datacustomcode/spark/default.py,sha256=aMB8CaTPYwHbQE_7XqBLQUZPGRdDJcRL72qTVh0aG7M,1433
60
58
  datacustomcode/template.py,sha256=FNNi8YPfd-JB-hUt1DDWSFK5AqwKnTnRSy_KpqYluaU,3282
61
59
  datacustomcode/templates/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
62
- datacustomcode/templates/function/.devcontainer/devcontainer.json,sha256=21RNTadhC-rynON2-VOtr5U174Hxnr_MA4RQ4592rsg,166
60
+ datacustomcode/templates/function/.devcontainer/devcontainer.json,sha256=lWMFk-SdTKBlQt_VJrMn8gZPmvT4tfcjRpCNhLdElZc,167
63
61
  datacustomcode/templates/function/Dockerfile.dependencies,sha256=AfHRddm5l3ujv4vdrf0d-SMB-qxPCOa0XQm6ptP2Euw,174
64
62
  datacustomcode/templates/function/README.md,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
65
63
  datacustomcode/templates/function/build_native_dependencies.sh,sha256=dk9vhzlObdsFAO5BtxP7ioOGDBM8JVwJHp_W0712Nlk,222
@@ -67,17 +65,17 @@ datacustomcode/templates/function/chunking/payload/config.json,sha256=RBNvo1WzZ4
67
65
  datacustomcode/templates/function/chunking/payload/entrypoint.py,sha256=tzpQJjODulwzLilOoKdOo8GjpdIlCDByjnawpHIQYQA,4517
68
66
  datacustomcode/templates/function/chunking/requirements.txt,sha256=Ih0KsVdiTdo7KuIsqFB_AJMRT2r0ao_xDEb3hiDmf48,46
69
67
  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
68
+ datacustomcode/templates/function/example/chunking_with_llm/entrypoint.py,sha256=6AMVf_busCCDALktG8vkCtjnzJheMgM-OOLML5iLKOg,3437
71
69
  datacustomcode/templates/function/example/chunking_with_llm/files/chunking_prompt.txt,sha256=w_gSYNXwZmbMTMjZ_YYwm1IpMsVpI3hyC3ew60zqs_0,446
72
70
  datacustomcode/templates/function/example/chunking_with_llm/tests/test.json,sha256=ZcH6QGEWu03dc922Za3vdx6hJY08nfGPie9kkeup-sc,9457
73
71
  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
72
+ datacustomcode/templates/function/example/chunking_with_prediction/entrypoint.py,sha256=w25_WucA-tUN3O6j-Od_WsW4HDH1Dfp2uJYNfVs_0M4,7722
75
73
  datacustomcode/templates/function/example/chunking_with_prediction/tests/test.json,sha256=2EE0c0n-s6eOayb_JrBqeKkXzyxieOyHK3_Mbm--Nmo,1073
76
74
  datacustomcode/templates/function/payload/config.json,sha256=RBNvo1WzZ4oRRq0W9-hknpT7T8If536DEMBg9hyq_4o,2
77
- datacustomcode/templates/function/payload/entrypoint.py,sha256=DrH73aAkU_ua6IHBRYr9SkMtX5pEFvNJYew8zXQL5TQ,5705
75
+ datacustomcode/templates/function/payload/entrypoint.py,sha256=uWb9mT7GaOvVWBK8P7vHS1GNbNaH--rSLV8J1v0eTHY,5667
78
76
  datacustomcode/templates/function/requirements-dev.txt,sha256=qT2-m2FOgiYPTvqaNy0yRzIAXqHgR5b8stccYXsXVi8,88
79
77
  datacustomcode/templates/function/requirements.txt,sha256=qJbqzs5z0Hrx590U3T7dHq_m8UQEx2TdhgrJSz-QHIQ,40
80
- datacustomcode/templates/script/.devcontainer/devcontainer.json,sha256=21RNTadhC-rynON2-VOtr5U174Hxnr_MA4RQ4592rsg,166
78
+ datacustomcode/templates/script/.devcontainer/devcontainer.json,sha256=lWMFk-SdTKBlQt_VJrMn8gZPmvT4tfcjRpCNhLdElZc,167
81
79
  datacustomcode/templates/script/Dockerfile,sha256=l8HEy5X7BsW9Gi7rBYM6G2czwk-1832RRIos7xyFzpI,463
82
80
  datacustomcode/templates/script/Dockerfile.dependencies,sha256=vBbTfgcBkVQVCnrJZE_QEx89ddTQrLZGeyVTZXU8TI8,206
83
81
  datacustomcode/templates/script/README.md,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -87,13 +85,13 @@ datacustomcode/templates/script/examples/employee_hierarchy/employee_data.csv,sh
87
85
  datacustomcode/templates/script/examples/employee_hierarchy/entrypoint.py,sha256=Mfm3iQtEHTQRW6cmZTwRKdTh3IeGWR-teXrd6x-YLSY,2223
88
86
  datacustomcode/templates/script/jupyterlab.sh,sha256=IHR3YQ8d_busuyvesByhJgzCKULIFPI-Hqogs0NPhCs,2432
89
87
  datacustomcode/templates/script/payload/config.json,sha256=0d2mEMt4NHIeOUTsgiPMuRKdOLIQxmh-Wq-IfEqU-gc,28
90
- datacustomcode/templates/script/payload/entrypoint.py,sha256=qpFA-jkhaYYF2CFNQ7_7MZVefU7SxnoMQIQYi7ox2dY,691
88
+ datacustomcode/templates/script/payload/entrypoint.py,sha256=HFrTe7--w5X78Sh4DVkoGGR6-zeqxemqp80RM20Rr4k,1642
91
89
  datacustomcode/templates/script/requirements-dev.txt,sha256=OWwuy1awesqOZOS3Zizmdd6BDnSePpGFN5bq5IrALvc,176
92
90
  datacustomcode/templates/script/requirements.txt,sha256=qJbqzs5z0Hrx590U3T7dHq_m8UQEx2TdhgrJSz-QHIQ,40
93
- datacustomcode/token_provider.py,sha256=a16r5xYeCOPR3M3JKvEV7JVIG7DOzDswObRBI33MQOk,6461
91
+ datacustomcode/token_provider.py,sha256=KPj_ca6mcBmhXHaPmZjAI4it8QvOWdwKJL8yfqxfYwU,5446
94
92
  datacustomcode/version.py,sha256=9LlbVrzwBvut1L308QbwNBgxdQk5CrghH39JARVSxms,989
95
- salesforce_data_customcode-6.0.1.dist-info/METADATA,sha256=PVoEUlTf9pjQlJq8Yi3Mt7migPd0K16ORl80T2gsMbc,20162
96
- salesforce_data_customcode-6.0.1.dist-info/WHEEL,sha256=eY7nduwzv-ldUxpzbRlxwvC693Hg6PX8bWDjEHjZ_dk,88
97
- salesforce_data_customcode-6.0.1.dist-info/entry_points.txt,sha256=WpQ94UB7UuRCYGOLtJV3vAgm1tl7iVf43VYRWIuNu5Y,74
98
- salesforce_data_customcode-6.0.1.dist-info/licenses/LICENSE.txt,sha256=iOi8EmQpfkFhMENi7VYtkl2EqS14pEOccoXHiW2dyPU,11443
99
- salesforce_data_customcode-6.0.1.dist-info/RECORD,,
93
+ salesforce_data_customcode-6.0.2.dev1.dist-info/METADATA,sha256=8lK5D1Zf3TelM0vlib8QBQLtdmixh79g4ce2Owuh8QQ,21748
94
+ salesforce_data_customcode-6.0.2.dev1.dist-info/WHEEL,sha256=eY7nduwzv-ldUxpzbRlxwvC693Hg6PX8bWDjEHjZ_dk,88
95
+ salesforce_data_customcode-6.0.2.dev1.dist-info/entry_points.txt,sha256=WpQ94UB7UuRCYGOLtJV3vAgm1tl7iVf43VYRWIuNu5Y,74
96
+ salesforce_data_customcode-6.0.2.dev1.dist-info/licenses/LICENSE.txt,sha256=iOi8EmQpfkFhMENi7VYtkl2EqS14pEOccoXHiW2dyPU,11443
97
+ salesforce_data_customcode-6.0.2.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,24 +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 ABC
18
-
19
- from datacustomcode.mixin import UserExtendableNamedConfigMixin
20
-
21
-
22
- class BaseProxyAccessLayer(ABC, UserExtendableNamedConfigMixin):
23
- def __init__(self):
24
- pass
@@ -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
- ): ...