salesforce-data-customcode 2.0.0__tar.gz → 3.0.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (75) hide show
  1. {salesforce_data_customcode-2.0.0 → salesforce_data_customcode-3.0.0}/PKG-INFO +2 -2
  2. {salesforce_data_customcode-2.0.0 → salesforce_data_customcode-3.0.0}/pyproject.toml +4 -2
  3. {salesforce_data_customcode-2.0.0 → salesforce_data_customcode-3.0.0}/src/datacustomcode/client.py +6 -18
  4. {salesforce_data_customcode-2.0.0 → salesforce_data_customcode-3.0.0}/src/datacustomcode/config.py +3 -3
  5. salesforce_data_customcode-3.0.0/src/datacustomcode/function/__init__.py +20 -0
  6. salesforce_data_customcode-3.0.0/src/datacustomcode/function/base.py +18 -0
  7. salesforce_data_customcode-3.0.0/src/datacustomcode/function/features_types/chunking.py +89 -0
  8. salesforce_data_customcode-3.0.0/src/datacustomcode/function/runtime.py +77 -0
  9. {salesforce_data_customcode-2.0.0 → salesforce_data_customcode-3.0.0}/src/datacustomcode/io/reader/query_api.py +2 -2
  10. salesforce_data_customcode-3.0.0/src/datacustomcode/llm_gateway/base.py +34 -0
  11. salesforce_data_customcode-3.0.0/src/datacustomcode/llm_gateway/default.py +33 -0
  12. salesforce_data_customcode-3.0.0/src/datacustomcode/llm_gateway/types/generate_text_request.py +46 -0
  13. salesforce_data_customcode-3.0.0/src/datacustomcode/llm_gateway/types/generate_text_request_builder.py +82 -0
  14. salesforce_data_customcode-3.0.0/src/datacustomcode/llm_gateway/types/generate_text_response.py +58 -0
  15. salesforce_data_customcode-3.0.0/src/datacustomcode/llm_gateway/types/generate_text_response_builder.py +37 -0
  16. salesforce_data_customcode-3.0.0/src/datacustomcode/proxy/__init__.py +14 -0
  17. salesforce_data_customcode-3.0.0/src/datacustomcode/proxy/client/__init__.py +14 -0
  18. {salesforce_data_customcode-2.0.0 → salesforce_data_customcode-3.0.0}/src/datacustomcode/templates/function/payload/entrypoint.py +16 -2
  19. {salesforce_data_customcode-2.0.0 → salesforce_data_customcode-3.0.0}/LICENSE.txt +0 -0
  20. {salesforce_data_customcode-2.0.0 → salesforce_data_customcode-3.0.0}/README.md +0 -0
  21. {salesforce_data_customcode-2.0.0 → salesforce_data_customcode-3.0.0}/src/datacustomcode/__init__.py +0 -0
  22. {salesforce_data_customcode-2.0.0 → salesforce_data_customcode-3.0.0}/src/datacustomcode/auth.py +0 -0
  23. {salesforce_data_customcode-2.0.0 → salesforce_data_customcode-3.0.0}/src/datacustomcode/cli.py +0 -0
  24. {salesforce_data_customcode-2.0.0 → salesforce_data_customcode-3.0.0}/src/datacustomcode/cmd.py +0 -0
  25. {salesforce_data_customcode-2.0.0 → salesforce_data_customcode-3.0.0}/src/datacustomcode/config.yaml +0 -0
  26. {salesforce_data_customcode-2.0.0 → salesforce_data_customcode-3.0.0}/src/datacustomcode/credentials.py +0 -0
  27. {salesforce_data_customcode-2.0.0 → salesforce_data_customcode-3.0.0}/src/datacustomcode/deploy.py +0 -0
  28. {salesforce_data_customcode-2.0.0 → salesforce_data_customcode-3.0.0}/src/datacustomcode/file/__init__.py +0 -0
  29. {salesforce_data_customcode-2.0.0 → salesforce_data_customcode-3.0.0}/src/datacustomcode/file/base.py +0 -0
  30. {salesforce_data_customcode-2.0.0 → salesforce_data_customcode-3.0.0}/src/datacustomcode/file/path/__init__.py +0 -0
  31. {salesforce_data_customcode-2.0.0 → salesforce_data_customcode-3.0.0}/src/datacustomcode/file/path/default.py +0 -0
  32. {salesforce_data_customcode-2.0.0 → salesforce_data_customcode-3.0.0}/src/datacustomcode/io/__init__.py +0 -0
  33. {salesforce_data_customcode-2.0.0 → salesforce_data_customcode-3.0.0}/src/datacustomcode/io/base.py +0 -0
  34. {salesforce_data_customcode-2.0.0 → salesforce_data_customcode-3.0.0}/src/datacustomcode/io/reader/__init__.py +0 -0
  35. {salesforce_data_customcode-2.0.0 → salesforce_data_customcode-3.0.0}/src/datacustomcode/io/reader/base.py +0 -0
  36. {salesforce_data_customcode-2.0.0 → salesforce_data_customcode-3.0.0}/src/datacustomcode/io/reader/sf_cli.py +0 -0
  37. {salesforce_data_customcode-2.0.0 → salesforce_data_customcode-3.0.0}/src/datacustomcode/io/reader/utils.py +0 -0
  38. {salesforce_data_customcode-2.0.0 → salesforce_data_customcode-3.0.0}/src/datacustomcode/io/writer/__init__.py +0 -0
  39. {salesforce_data_customcode-2.0.0 → salesforce_data_customcode-3.0.0}/src/datacustomcode/io/writer/base.py +0 -0
  40. {salesforce_data_customcode-2.0.0 → salesforce_data_customcode-3.0.0}/src/datacustomcode/io/writer/csv.py +0 -0
  41. {salesforce_data_customcode-2.0.0 → salesforce_data_customcode-3.0.0}/src/datacustomcode/io/writer/print.py +0 -0
  42. {salesforce_data_customcode-2.0.0/src/datacustomcode/proxy → salesforce_data_customcode-3.0.0/src/datacustomcode/llm_gateway}/__init__.py +0 -0
  43. {salesforce_data_customcode-2.0.0/src/datacustomcode/proxy/client → salesforce_data_customcode-3.0.0/src/datacustomcode/llm_gateway/types}/__init__.py +0 -0
  44. {salesforce_data_customcode-2.0.0 → salesforce_data_customcode-3.0.0}/src/datacustomcode/mixin.py +0 -0
  45. {salesforce_data_customcode-2.0.0 → salesforce_data_customcode-3.0.0}/src/datacustomcode/proxy/base.py +0 -0
  46. {salesforce_data_customcode-2.0.0 → salesforce_data_customcode-3.0.0}/src/datacustomcode/proxy/client/LocalProxyClientProvider.py +0 -0
  47. {salesforce_data_customcode-2.0.0 → salesforce_data_customcode-3.0.0}/src/datacustomcode/proxy/client/base.py +0 -0
  48. {salesforce_data_customcode-2.0.0 → salesforce_data_customcode-3.0.0}/src/datacustomcode/py.typed +0 -0
  49. {salesforce_data_customcode-2.0.0 → salesforce_data_customcode-3.0.0}/src/datacustomcode/run.py +0 -0
  50. {salesforce_data_customcode-2.0.0 → salesforce_data_customcode-3.0.0}/src/datacustomcode/scan.py +0 -0
  51. {salesforce_data_customcode-2.0.0 → salesforce_data_customcode-3.0.0}/src/datacustomcode/spark/__init__.py +0 -0
  52. {salesforce_data_customcode-2.0.0 → salesforce_data_customcode-3.0.0}/src/datacustomcode/spark/base.py +0 -0
  53. {salesforce_data_customcode-2.0.0 → salesforce_data_customcode-3.0.0}/src/datacustomcode/spark/default.py +0 -0
  54. {salesforce_data_customcode-2.0.0 → salesforce_data_customcode-3.0.0}/src/datacustomcode/template.py +0 -0
  55. {salesforce_data_customcode-2.0.0 → salesforce_data_customcode-3.0.0}/src/datacustomcode/templates/function/.devcontainer/devcontainer.json +0 -0
  56. {salesforce_data_customcode-2.0.0 → salesforce_data_customcode-3.0.0}/src/datacustomcode/templates/function/Dockerfile.dependencies +0 -0
  57. {salesforce_data_customcode-2.0.0 → salesforce_data_customcode-3.0.0}/src/datacustomcode/templates/function/README.md +0 -0
  58. {salesforce_data_customcode-2.0.0 → salesforce_data_customcode-3.0.0}/src/datacustomcode/templates/function/build_native_dependencies.sh +0 -0
  59. {salesforce_data_customcode-2.0.0 → salesforce_data_customcode-3.0.0}/src/datacustomcode/templates/function/payload/config.json +0 -0
  60. {salesforce_data_customcode-2.0.0 → salesforce_data_customcode-3.0.0}/src/datacustomcode/templates/function/requirements-dev.txt +0 -0
  61. {salesforce_data_customcode-2.0.0 → salesforce_data_customcode-3.0.0}/src/datacustomcode/templates/function/requirements.txt +0 -0
  62. {salesforce_data_customcode-2.0.0 → salesforce_data_customcode-3.0.0}/src/datacustomcode/templates/script/.devcontainer/devcontainer.json +0 -0
  63. {salesforce_data_customcode-2.0.0 → salesforce_data_customcode-3.0.0}/src/datacustomcode/templates/script/Dockerfile +0 -0
  64. {salesforce_data_customcode-2.0.0 → salesforce_data_customcode-3.0.0}/src/datacustomcode/templates/script/Dockerfile.dependencies +0 -0
  65. {salesforce_data_customcode-2.0.0 → salesforce_data_customcode-3.0.0}/src/datacustomcode/templates/script/README.md +0 -0
  66. {salesforce_data_customcode-2.0.0 → salesforce_data_customcode-3.0.0}/src/datacustomcode/templates/script/account.ipynb +0 -0
  67. {salesforce_data_customcode-2.0.0 → salesforce_data_customcode-3.0.0}/src/datacustomcode/templates/script/build_native_dependencies.sh +0 -0
  68. {salesforce_data_customcode-2.0.0 → salesforce_data_customcode-3.0.0}/src/datacustomcode/templates/script/examples/employee_hierarchy/employee_data.csv +0 -0
  69. {salesforce_data_customcode-2.0.0 → salesforce_data_customcode-3.0.0}/src/datacustomcode/templates/script/examples/employee_hierarchy/entrypoint.py +0 -0
  70. {salesforce_data_customcode-2.0.0 → salesforce_data_customcode-3.0.0}/src/datacustomcode/templates/script/jupyterlab.sh +0 -0
  71. {salesforce_data_customcode-2.0.0 → salesforce_data_customcode-3.0.0}/src/datacustomcode/templates/script/payload/config.json +0 -0
  72. {salesforce_data_customcode-2.0.0 → salesforce_data_customcode-3.0.0}/src/datacustomcode/templates/script/payload/entrypoint.py +0 -0
  73. {salesforce_data_customcode-2.0.0 → salesforce_data_customcode-3.0.0}/src/datacustomcode/templates/script/requirements-dev.txt +0 -0
  74. {salesforce_data_customcode-2.0.0 → salesforce_data_customcode-3.0.0}/src/datacustomcode/templates/script/requirements.txt +0 -0
  75. {salesforce_data_customcode-2.0.0 → salesforce_data_customcode-3.0.0}/src/datacustomcode/version.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: salesforce-data-customcode
3
- Version: 2.0.0
3
+ Version: 3.0.0
4
4
  Summary: Data Cloud Custom Code SDK
5
5
  License-Expression: Apache-2.0
6
6
  License-File: LICENSE.txt
@@ -15,7 +15,7 @@ Requires-Dist: click (>=8.1.8,<9.0.0)
15
15
  Requires-Dist: loguru (>=0.7.3,<0.8.0)
16
16
  Requires-Dist: numpy
17
17
  Requires-Dist: pandas
18
- Requires-Dist: pydantic (>=1.8.2,<3.0.0)
18
+ Requires-Dist: pydantic (==2.13.1)
19
19
  Requires-Dist: pyspark (==3.5.1)
20
20
  Requires-Dist: pyyaml (>=6.0,<7.0)
21
21
  Requires-Dist: salesforce-cdp-connector (>=1.0.19)
@@ -18,7 +18,7 @@ license = "Apache-2.0"
18
18
  name = "salesforce-data-customcode"
19
19
  readme = "README.md"
20
20
  requires-python = ">=3.10,<3.12"
21
- version = "2.0.0"
21
+ version = "3.0.0"
22
22
 
23
23
  [tool.black]
24
24
  exclude = '''
@@ -73,7 +73,9 @@ use_parentheses = true
73
73
 
74
74
  [tool.mypy]
75
75
  check_untyped_defs = false
76
+ explicit_package_bases = true
76
77
  ignore_missing_imports = true
78
+ mypy_path = "src"
77
79
  no_implicit_optional = true
78
80
  plugins = [
79
81
  'pydantic.mypy'
@@ -99,7 +101,7 @@ click = "^8.1.8"
99
101
  loguru = "^0.7.3"
100
102
  numpy = "*"
101
103
  pandas = "*"
102
- pydantic = "^1.8.2 || ^2.0.0"
104
+ pydantic = "2.13.1"
103
105
  pyspark = "3.5.1"
104
106
  python = ">=3.10,<3.12"
105
107
  pyyaml = "^6.0"
@@ -119,8 +119,6 @@ class Client:
119
119
  spark_provider: Optional["BaseSparkSessionProvider"] = None,
120
120
  code_type: str = "script",
121
121
  ) -> Client:
122
- if "function" in code_type:
123
- return cls._new_function_client()
124
122
 
125
123
  if cls._instance is None:
126
124
  cls._instance = super().__new__(cls)
@@ -175,16 +173,6 @@ class Client:
175
173
  raise ValueError("Cannot set reader or writer after client is initialized")
176
174
  return cls._instance
177
175
 
178
- @classmethod
179
- def _new_function_client(cls) -> Client:
180
- cls._instance = super().__new__(cls)
181
- cls._instance._proxy = (
182
- config.proxy_config.to_object() # type: ignore
183
- if config.proxy_config is not None
184
- else None
185
- )
186
- return cls._instance
187
-
188
176
  def read_dlo(self, name: str) -> PySparkDataFrame:
189
177
  """Read a DLO from Data Cloud.
190
178
 
@@ -195,7 +183,7 @@ class Client:
195
183
  A PySpark DataFrame containing the DLO data.
196
184
  """
197
185
  self._record_dlo_access(name)
198
- return self._reader.read_dlo(name)
186
+ return self._reader.read_dlo(name) # type: ignore[no-any-return]
199
187
 
200
188
  def read_dmo(self, name: str) -> PySparkDataFrame:
201
189
  """Read a DMO from Data Cloud.
@@ -207,7 +195,7 @@ class Client:
207
195
  A PySpark DataFrame containing the DMO data.
208
196
  """
209
197
  self._record_dmo_access(name)
210
- return self._reader.read_dmo(name)
198
+ return self._reader.read_dmo(name) # type: ignore[no-any-return]
211
199
 
212
200
  def write_to_dlo(
213
201
  self, name: str, dataframe: PySparkDataFrame, write_mode: WriteMode, **kwargs
@@ -220,7 +208,7 @@ class Client:
220
208
  write_mode: The write mode to use for writing to the DLO.
221
209
  """
222
210
  self._validate_data_layer_history_does_not_contain(DataCloudObjectType.DMO)
223
- return self._writer.write_to_dlo(name, dataframe, write_mode, **kwargs)
211
+ return self._writer.write_to_dlo(name, dataframe, write_mode, **kwargs) # type: ignore[no-any-return]
224
212
 
225
213
  def write_to_dmo(
226
214
  self, name: str, dataframe: PySparkDataFrame, write_mode: WriteMode, **kwargs
@@ -233,17 +221,17 @@ class Client:
233
221
  write_mode: The write mode to use for writing to the DMO.
234
222
  """
235
223
  self._validate_data_layer_history_does_not_contain(DataCloudObjectType.DLO)
236
- return self._writer.write_to_dmo(name, dataframe, write_mode, **kwargs)
224
+ return self._writer.write_to_dmo(name, dataframe, write_mode, **kwargs) # type: ignore[no-any-return]
237
225
 
238
226
  def call_llm_gateway(self, LLM_MODEL_ID: str, prompt: str, maxTokens: int) -> str:
239
227
  if self._proxy is None:
240
228
  raise ValueError("No proxy configured; set proxy or proxy_config")
241
- return self._proxy.call_llm_gateway(LLM_MODEL_ID, prompt, maxTokens)
229
+ return self._proxy.call_llm_gateway(LLM_MODEL_ID, prompt, maxTokens) # type: ignore[no-any-return]
242
230
 
243
231
  def find_file_path(self, file_name: str) -> Path:
244
232
  """Return a file path"""
245
233
 
246
- return self._file.find_file_path(file_name)
234
+ return self._file.find_file_path(file_name) # type: ignore[no-any-return]
247
235
 
248
236
  def _validate_data_layer_history_does_not_contain(
249
237
  self, data_cloud_object_type: DataCloudObjectType
@@ -36,10 +36,10 @@ import yaml
36
36
  # This lets all readers and writers to be findable via config
37
37
  from datacustomcode.io import * # noqa: F403
38
38
  from datacustomcode.io.base import BaseDataAccessLayer
39
- from datacustomcode.io.reader.base import BaseDataCloudReader # noqa: TCH001
40
- from datacustomcode.io.writer.base import BaseDataCloudWriter # noqa: TCH001
39
+ from datacustomcode.io.reader.base import BaseDataCloudReader # noqa: TCH002
40
+ from datacustomcode.io.writer.base import BaseDataCloudWriter # noqa: TCH002
41
41
  from datacustomcode.proxy.base import BaseProxyAccessLayer
42
- from datacustomcode.proxy.client.base import BaseProxyClient # noqa: TCH001
42
+ from datacustomcode.proxy.client.base import BaseProxyClient # noqa: TCH002
43
43
  from datacustomcode.spark.base import BaseSparkSessionProvider
44
44
 
45
45
  DEFAULT_CONFIG_NAME = "config.yaml"
@@ -0,0 +1,20 @@
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
+
16
+ """Function runtime for Data Cloud Custom Code."""
17
+
18
+ from datacustomcode.function.runtime import Runtime
19
+
20
+ __all__ = ["Runtime"]
@@ -0,0 +1,18 @@
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
+
16
+
17
+ class BaseRuntime:
18
+ """Base class for datacustomcode run time"""
@@ -0,0 +1,89 @@
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
+
16
+ """
17
+ Pydantic models for byoc-function-proto (uds_chunking.proto)
18
+ Auto-generated - validation rules from buf.validate
19
+ """
20
+
21
+ from typing import (
22
+ Any,
23
+ Dict,
24
+ List,
25
+ Literal,
26
+ )
27
+
28
+ from pydantic import BaseModel, Field
29
+
30
+
31
+ class DocElement(BaseModel):
32
+ """Document element to be chunked"""
33
+
34
+ text: str = Field(..., description="Text content to be chunked")
35
+ metadata: Dict[str, Any] = Field(
36
+ default_factory=dict, description="Source document metadata"
37
+ )
38
+
39
+
40
+ class ChunkOutput(BaseModel):
41
+ """Output chunk from the chunking process"""
42
+
43
+ chunk_id: str = Field(..., description="UUID for this chunk")
44
+ chunk_type: str = Field(..., description="Type: 'text'")
45
+ text: str = Field(..., description="Chunk text content")
46
+ seq_no: int = Field(..., description="Sequential chunk number (1-based)")
47
+ metadata: Dict[str, str] = Field(
48
+ default_factory=dict, description="Metadata from source (DMO fields)"
49
+ )
50
+ tag_metadata: Dict[str, Any] = Field(
51
+ default_factory=dict, description="Additional tags"
52
+ )
53
+ citations: Dict[str, Any] = Field(
54
+ default_factory=dict, description="Citation information"
55
+ )
56
+
57
+
58
+ class StatusResponse(BaseModel):
59
+ """Status response for operation"""
60
+
61
+ status_type: str = Field(..., description="'success' or 'error'")
62
+ status_message: str = Field(..., description="Human-readable status")
63
+
64
+
65
+ class UdsChunkingV1BatchRequest(BaseModel):
66
+ """Batch request for UDS chunking"""
67
+
68
+ version: Literal["v1"] = Field(
69
+ default="v1", description="API version, must be 'v1'"
70
+ )
71
+ input: List[DocElement] = Field(
72
+ ..., min_length=1, description="List of documents (min 1)"
73
+ )
74
+ max_characters: int = Field(..., description="Max chars per chunk (default: 100)")
75
+ additional_params: Dict[str, Any] = Field(
76
+ default_factory=dict, description="Future extension point"
77
+ )
78
+
79
+
80
+ class UdsChunkingV1BatchResponse(BaseModel):
81
+ """Batch response for UDS chunking"""
82
+
83
+ version: Literal["v1"] = Field(
84
+ default="v1", description="API version, must be 'v1'"
85
+ )
86
+ output: List[ChunkOutput] = Field(
87
+ default_factory=list, description="Flat list of chunks from all docs"
88
+ )
89
+ status: StatusResponse = Field(..., description="Overall operation status")
@@ -0,0 +1,77 @@
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
+
16
+
17
+ import threading
18
+ from typing import Optional
19
+
20
+ from datacustomcode.file.path.default import DefaultFindFilePath
21
+ from datacustomcode.function.base import BaseRuntime
22
+ from datacustomcode.llm_gateway.default import DefaultLLMGateway
23
+
24
+
25
+ class Runtime(BaseRuntime):
26
+ """Client for Function code type.
27
+
28
+ NOTE: Do not instantiate this class directly.
29
+ It will be provided to your function by the SDK:
30
+
31
+ def function(request: dict, runtime: RunTime) -> dict:
32
+ response = {...}
33
+ return response
34
+
35
+ """
36
+
37
+ _instance: Optional["Runtime"] = None
38
+ _lock = threading.Lock()
39
+
40
+ def __new__(cls):
41
+ """Create singleton instance (thread-safe)."""
42
+ with cls._lock:
43
+ if cls._instance is not None:
44
+ raise RuntimeError(
45
+ "Runtime can only be instantiated once by the SDK.\n\n"
46
+ "Do not instantiate it yourself. Accept it as a parameter:\n\n"
47
+ " from datacustomcode.runtime.function.RunTime import Function\n"
48
+ " \n"
49
+ " def function(request: dict, runtime: Runtime) -> dict:\n"
50
+ " response = {...}\n"
51
+ " return response"
52
+ )
53
+ cls._instance = super().__new__(cls)
54
+ return cls._instance
55
+
56
+ def __init__(self) -> None:
57
+ # Prevent re-initialization
58
+ if hasattr(self, "_initialized"):
59
+ return
60
+
61
+ self._initialized = True
62
+
63
+ super().__init__()
64
+
65
+ # Initialize resources
66
+ self._llm_gateway = DefaultLLMGateway()
67
+ self._file = DefaultFindFilePath()
68
+
69
+ @property
70
+ def llm_gateway(self) -> DefaultLLMGateway:
71
+ """Access LLM operations."""
72
+ return self._llm_gateway
73
+
74
+ @property
75
+ def file(self) -> DefaultFindFilePath:
76
+ """Access file operations."""
77
+ return self._file
@@ -197,7 +197,7 @@ class QueryAPIDataCloudReader(BaseDataCloudReader):
197
197
  self, "_sf_cli_reader", None
198
198
  )
199
199
  if sf_cli_reader is not None:
200
- return sf_cli_reader.read_dlo(name, schema)
200
+ return sf_cli_reader.read_dlo(name, schema) # type: ignore[no-any-return]
201
201
 
202
202
  query = self._build_query(name)
203
203
 
@@ -230,7 +230,7 @@ class QueryAPIDataCloudReader(BaseDataCloudReader):
230
230
  self, "_sf_cli_reader", None
231
231
  )
232
232
  if sf_cli_reader is not None:
233
- return sf_cli_reader.read_dmo(name, schema)
233
+ return sf_cli_reader.read_dmo(name, schema) # type: ignore[no-any-return]
234
234
 
235
235
  query = self._build_query(name)
236
236
 
@@ -0,0 +1,34 @@
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
+ from typing import TYPE_CHECKING
19
+
20
+ if TYPE_CHECKING:
21
+ from datacustomcode.llm_gateway.types.generate_text_request import (
22
+ GenerateTextRequest,
23
+ )
24
+ from datacustomcode.llm_gateway.types.generate_text_response import (
25
+ GenerateTextResponse,
26
+ )
27
+
28
+
29
+ class LLMGateway:
30
+ def __init__(self) -> None:
31
+ pass
32
+
33
+ @abstractmethod
34
+ def generate_text(self, request: GenerateTextRequest) -> GenerateTextResponse: ...
@@ -0,0 +1,33 @@
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
+
16
+ from datacustomcode.llm_gateway.base import LLMGateway
17
+ from datacustomcode.llm_gateway.types.generate_text_request import GenerateTextRequest
18
+ from datacustomcode.llm_gateway.types.generate_text_response import GenerateTextResponse
19
+ from datacustomcode.llm_gateway.types.generate_text_response_builder import (
20
+ GenerateTextResponseBuilder,
21
+ )
22
+
23
+
24
+ class DefaultLLMGateway(LLMGateway):
25
+ def generate_text(self, request: GenerateTextRequest) -> GenerateTextResponse:
26
+
27
+ response_data = {
28
+ "version": "v1",
29
+ "status_code": 200,
30
+ "data": {"generation": {"generatedText": "Hello World"}},
31
+ }
32
+
33
+ return GenerateTextResponseBuilder.build(response_data)
@@ -0,0 +1,46 @@
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
+
16
+ from typing import (
17
+ Any,
18
+ Dict,
19
+ Literal,
20
+ Optional,
21
+ )
22
+
23
+ from pydantic import (
24
+ BaseModel,
25
+ ConfigDict,
26
+ Field,
27
+ )
28
+ from pydantic.alias_generators import to_camel
29
+
30
+
31
+ class GenerateTextRequest(BaseModel):
32
+
33
+ model_config = ConfigDict(
34
+ alias_generator=to_camel,
35
+ populate_by_name=True, # Allows both snake_case and camelCase input
36
+ )
37
+
38
+ version: Literal["v1"] = Field(
39
+ default="v1", description="API version, must be 'v1'"
40
+ )
41
+ model_name: str = Field(..., min_length=1, description="Name of the model to use")
42
+ prompt: str = Field(..., description="Input prompt")
43
+ localization: Optional[Dict[str, Any]] = Field(
44
+ default=None, description="Localization settings"
45
+ )
46
+ tags: Optional[Dict[str, Any]] = Field(default=None, description="Additional tags")
@@ -0,0 +1,82 @@
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
+
16
+ from typing import (
17
+ Any,
18
+ Dict,
19
+ Optional,
20
+ )
21
+
22
+ from datacustomcode.llm_gateway.types.generate_text_request import GenerateTextRequest
23
+
24
+
25
+ class GenerateTextRequestBuilder:
26
+ def __init__(self) -> None:
27
+ self._prompt = ""
28
+ self._model_name = ""
29
+ self._localization: Optional[Dict[str, Any]] = None
30
+ self._tags: Optional[Dict[str, Any]] = None
31
+
32
+ def set_prompt(self, prompt: str) -> "GenerateTextRequestBuilder":
33
+ self._prompt = prompt
34
+ return self
35
+
36
+ def set_model(self, model_name: str) -> "GenerateTextRequestBuilder":
37
+ self._model_name = model_name
38
+ return self
39
+
40
+ def set_localization(
41
+ self,
42
+ localization: Optional[Dict[str, Any]] = None,
43
+ locale: Optional[str] = None,
44
+ ) -> "GenerateTextRequestBuilder":
45
+ """
46
+ Set localization either from a dict or a simple locale string.
47
+
48
+ Args:
49
+ localization: Full localization dict (if provided, locale is ignored)
50
+ locale: Simple locale string for defaultLocale only
51
+
52
+ Returns:
53
+ self for method chaining
54
+ """
55
+
56
+ if localization is not None:
57
+ self._localization = localization
58
+ elif locale is not None:
59
+ self._localization = {
60
+ "defaultLocale": locale,
61
+ "inputLocales": [{"locale": locale, "probability": 1.0}],
62
+ "expectedLocales": [locale],
63
+ }
64
+ else:
65
+ raise ValueError("Must provide either localization or locale")
66
+
67
+ return self
68
+
69
+ def set_tags(self, tags: Dict[str, Any]) -> "GenerateTextRequestBuilder":
70
+ self._tags = tags
71
+ return self
72
+
73
+ def build(self) -> GenerateTextRequest:
74
+
75
+ request = GenerateTextRequest(
76
+ prompt=self._prompt,
77
+ model_name=self._model_name,
78
+ localization=self._localization,
79
+ tags=self._tags,
80
+ )
81
+
82
+ return request
@@ -0,0 +1,58 @@
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
+
16
+ from typing import (
17
+ Any,
18
+ Dict,
19
+ Optional,
20
+ )
21
+
22
+ from pydantic import BaseModel, Field
23
+
24
+
25
+ class GenerateTextResponse(BaseModel):
26
+ """Response from LLM text generation"""
27
+
28
+ version: str = Field(default="v1", description="API version")
29
+ status_code: int = Field(..., description="HTTP status code", ge=0)
30
+ data: Optional[Dict[str, Any]] = Field(default=None, description="Response data")
31
+
32
+ @property
33
+ def is_success(self) -> bool:
34
+ """Check if request succeeded."""
35
+ return self.status_code == 200
36
+
37
+ @property
38
+ def is_error(self) -> bool:
39
+ """Check if request failed."""
40
+ return not self.is_success
41
+
42
+ @property
43
+ def text(self) -> str:
44
+ """Generated text (convenience property)."""
45
+ if self.is_success and self.data:
46
+ generation = self.data.get("generation", {})
47
+ if isinstance(generation, dict):
48
+ text = generation.get("generatedText", "")
49
+ return str(text) if text else ""
50
+ return ""
51
+
52
+ @property
53
+ def error_code(self) -> str:
54
+ """Generated text (convenience property)."""
55
+ if self.is_error and self.data:
56
+ error_code = self.data.get("errorCode", str(self.status_code))
57
+ return str(error_code)
58
+ return ""
@@ -0,0 +1,37 @@
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
+
16
+ from typing import Any, Dict
17
+
18
+ from datacustomcode.llm_gateway.types.generate_text_response import GenerateTextResponse
19
+
20
+
21
+ class GenerateTextResponseBuilder:
22
+ def __init__(self):
23
+ self._version = "v1" # Hardcoded default for your SDK
24
+ self._status_code = None
25
+ self._data = None
26
+
27
+ def set_status_code(self, status_code: int):
28
+ self._status_code = status_code
29
+ return self
30
+
31
+ def set_data(self, data: dict):
32
+ self._data = data
33
+ return self
34
+
35
+ @staticmethod
36
+ def build(response_dict: Dict[str, Any]) -> GenerateTextResponse:
37
+ return GenerateTextResponse.model_validate(response_dict)
@@ -0,0 +1,14 @@
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.
@@ -0,0 +1,14 @@
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.
@@ -2,6 +2,11 @@ import logging
2
2
  from typing import List
3
3
  from uuid import uuid4
4
4
 
5
+ from datacustomcode.function import Runtime
6
+ from datacustomcode.llm_gateway.types.generate_text_request_builder import (
7
+ GenerateTextRequestBuilder,
8
+ )
9
+
5
10
  logger = logging.getLogger(__name__)
6
11
 
7
12
 
@@ -33,7 +38,7 @@ def chunk_text(text: str, chunk_size: int = 1000) -> List[str]:
33
38
  return chunks
34
39
 
35
40
 
36
- def function(request: dict) -> dict:
41
+ def function(request: dict, runtime: Runtime) -> dict:
37
42
  logger.info("Inside Function")
38
43
  logger.info(request)
39
44
 
@@ -41,6 +46,15 @@ def function(request: dict) -> dict:
41
46
  output_chunks = []
42
47
  current_seq_no = 1 # Start sequence number from 1
43
48
 
49
+ builder = GenerateTextRequestBuilder()
50
+ llm_request = builder.set_prompt("Hello").set_model("modelName").build()
51
+ llm_response = runtime.llm_gateway.generate_text(llm_request)
52
+
53
+ if llm_response.is_success:
54
+ print(llm_response.text)
55
+ else:
56
+ print(llm_response.error_code)
57
+
44
58
  for item in items:
45
59
  # Item is DocElement as dict
46
60
  logger.info(f"Processing item: {item}")
@@ -107,7 +121,7 @@ if __name__ == "__main__":
107
121
  }
108
122
 
109
123
  # Run the function
110
- result = function(test_request)
124
+ result = function(test_request, Runtime())
111
125
 
112
126
  # Print the results in a more readable format
113
127
  print("\nChunking Results:")