salesforce-data-customcode 6.0.3.dev1__tar.gz → 6.0.3.dev2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (97) hide show
  1. {salesforce_data_customcode-6.0.3.dev1 → salesforce_data_customcode-6.0.3.dev2}/PKG-INFO +5 -2
  2. {salesforce_data_customcode-6.0.3.dev1 → salesforce_data_customcode-6.0.3.dev2}/README.md +4 -1
  3. {salesforce_data_customcode-6.0.3.dev1 → salesforce_data_customcode-6.0.3.dev2}/pyproject.toml +1 -1
  4. {salesforce_data_customcode-6.0.3.dev1 → salesforce_data_customcode-6.0.3.dev2}/src/datacustomcode/client.py +20 -8
  5. {salesforce_data_customcode-6.0.3.dev1 → salesforce_data_customcode-6.0.3.dev2}/src/datacustomcode/llm_gateway/spark_base.py +10 -1
  6. {salesforce_data_customcode-6.0.3.dev1 → salesforce_data_customcode-6.0.3.dev2}/src/datacustomcode/llm_gateway/spark_default.py +70 -11
  7. {salesforce_data_customcode-6.0.3.dev1 → salesforce_data_customcode-6.0.3.dev2}/src/datacustomcode/templates/script/payload/entrypoint.py +4 -2
  8. {salesforce_data_customcode-6.0.3.dev1 → salesforce_data_customcode-6.0.3.dev2}/LICENSE.txt +0 -0
  9. {salesforce_data_customcode-6.0.3.dev1 → salesforce_data_customcode-6.0.3.dev2}/src/datacustomcode/__init__.py +0 -0
  10. {salesforce_data_customcode-6.0.3.dev1 → salesforce_data_customcode-6.0.3.dev2}/src/datacustomcode/auth.py +0 -0
  11. {salesforce_data_customcode-6.0.3.dev1 → salesforce_data_customcode-6.0.3.dev2}/src/datacustomcode/cli.py +0 -0
  12. {salesforce_data_customcode-6.0.3.dev1 → salesforce_data_customcode-6.0.3.dev2}/src/datacustomcode/cmd.py +0 -0
  13. {salesforce_data_customcode-6.0.3.dev1 → salesforce_data_customcode-6.0.3.dev2}/src/datacustomcode/common_config.py +0 -0
  14. {salesforce_data_customcode-6.0.3.dev1 → salesforce_data_customcode-6.0.3.dev2}/src/datacustomcode/config.py +0 -0
  15. {salesforce_data_customcode-6.0.3.dev1 → salesforce_data_customcode-6.0.3.dev2}/src/datacustomcode/config.yaml +0 -0
  16. {salesforce_data_customcode-6.0.3.dev1 → salesforce_data_customcode-6.0.3.dev2}/src/datacustomcode/constants.py +0 -0
  17. {salesforce_data_customcode-6.0.3.dev1 → salesforce_data_customcode-6.0.3.dev2}/src/datacustomcode/credentials.py +0 -0
  18. {salesforce_data_customcode-6.0.3.dev1 → salesforce_data_customcode-6.0.3.dev2}/src/datacustomcode/deploy.py +0 -0
  19. {salesforce_data_customcode-6.0.3.dev1 → salesforce_data_customcode-6.0.3.dev2}/src/datacustomcode/einstein_platform_client.py +0 -0
  20. {salesforce_data_customcode-6.0.3.dev1 → salesforce_data_customcode-6.0.3.dev2}/src/datacustomcode/einstein_platform_config.py +0 -0
  21. {salesforce_data_customcode-6.0.3.dev1 → salesforce_data_customcode-6.0.3.dev2}/src/datacustomcode/einstein_predictions/__init__.py +0 -0
  22. {salesforce_data_customcode-6.0.3.dev1 → salesforce_data_customcode-6.0.3.dev2}/src/datacustomcode/einstein_predictions/base.py +0 -0
  23. {salesforce_data_customcode-6.0.3.dev1 → salesforce_data_customcode-6.0.3.dev2}/src/datacustomcode/einstein_predictions/impl/default.py +0 -0
  24. {salesforce_data_customcode-6.0.3.dev1 → salesforce_data_customcode-6.0.3.dev2}/src/datacustomcode/einstein_predictions/types.py +0 -0
  25. {salesforce_data_customcode-6.0.3.dev1 → salesforce_data_customcode-6.0.3.dev2}/src/datacustomcode/einstein_predictions_config.py +0 -0
  26. {salesforce_data_customcode-6.0.3.dev1 → salesforce_data_customcode-6.0.3.dev2}/src/datacustomcode/file/__init__.py +0 -0
  27. {salesforce_data_customcode-6.0.3.dev1 → salesforce_data_customcode-6.0.3.dev2}/src/datacustomcode/file/base.py +0 -0
  28. {salesforce_data_customcode-6.0.3.dev1 → salesforce_data_customcode-6.0.3.dev2}/src/datacustomcode/file/path/__init__.py +0 -0
  29. {salesforce_data_customcode-6.0.3.dev1 → salesforce_data_customcode-6.0.3.dev2}/src/datacustomcode/file/path/default.py +0 -0
  30. {salesforce_data_customcode-6.0.3.dev1 → salesforce_data_customcode-6.0.3.dev2}/src/datacustomcode/function/__init__.py +0 -0
  31. {salesforce_data_customcode-6.0.3.dev1 → salesforce_data_customcode-6.0.3.dev2}/src/datacustomcode/function/base.py +0 -0
  32. {salesforce_data_customcode-6.0.3.dev1 → salesforce_data_customcode-6.0.3.dev2}/src/datacustomcode/function/feature_types/__init__.py +0 -0
  33. {salesforce_data_customcode-6.0.3.dev1 → salesforce_data_customcode-6.0.3.dev2}/src/datacustomcode/function/feature_types/chunking.py +0 -0
  34. {salesforce_data_customcode-6.0.3.dev1 → salesforce_data_customcode-6.0.3.dev2}/src/datacustomcode/function/runtime.py +0 -0
  35. {salesforce_data_customcode-6.0.3.dev1 → salesforce_data_customcode-6.0.3.dev2}/src/datacustomcode/function_utils.py +0 -0
  36. {salesforce_data_customcode-6.0.3.dev1 → salesforce_data_customcode-6.0.3.dev2}/src/datacustomcode/io/__init__.py +0 -0
  37. {salesforce_data_customcode-6.0.3.dev1 → salesforce_data_customcode-6.0.3.dev2}/src/datacustomcode/io/base.py +0 -0
  38. {salesforce_data_customcode-6.0.3.dev1 → salesforce_data_customcode-6.0.3.dev2}/src/datacustomcode/io/reader/__init__.py +0 -0
  39. {salesforce_data_customcode-6.0.3.dev1 → salesforce_data_customcode-6.0.3.dev2}/src/datacustomcode/io/reader/base.py +0 -0
  40. {salesforce_data_customcode-6.0.3.dev1 → salesforce_data_customcode-6.0.3.dev2}/src/datacustomcode/io/reader/query_api.py +0 -0
  41. {salesforce_data_customcode-6.0.3.dev1 → salesforce_data_customcode-6.0.3.dev2}/src/datacustomcode/io/reader/sf_cli.py +0 -0
  42. {salesforce_data_customcode-6.0.3.dev1 → salesforce_data_customcode-6.0.3.dev2}/src/datacustomcode/io/reader/utils.py +0 -0
  43. {salesforce_data_customcode-6.0.3.dev1 → salesforce_data_customcode-6.0.3.dev2}/src/datacustomcode/io/writer/__init__.py +0 -0
  44. {salesforce_data_customcode-6.0.3.dev1 → salesforce_data_customcode-6.0.3.dev2}/src/datacustomcode/io/writer/base.py +0 -0
  45. {salesforce_data_customcode-6.0.3.dev1 → salesforce_data_customcode-6.0.3.dev2}/src/datacustomcode/io/writer/csv.py +0 -0
  46. {salesforce_data_customcode-6.0.3.dev1 → salesforce_data_customcode-6.0.3.dev2}/src/datacustomcode/io/writer/print.py +0 -0
  47. {salesforce_data_customcode-6.0.3.dev1 → salesforce_data_customcode-6.0.3.dev2}/src/datacustomcode/llm_gateway/__init__.py +0 -0
  48. {salesforce_data_customcode-6.0.3.dev1 → salesforce_data_customcode-6.0.3.dev2}/src/datacustomcode/llm_gateway/base.py +0 -0
  49. {salesforce_data_customcode-6.0.3.dev1 → salesforce_data_customcode-6.0.3.dev2}/src/datacustomcode/llm_gateway/default.py +0 -0
  50. {salesforce_data_customcode-6.0.3.dev1 → salesforce_data_customcode-6.0.3.dev2}/src/datacustomcode/llm_gateway/errors.py +0 -0
  51. {salesforce_data_customcode-6.0.3.dev1 → salesforce_data_customcode-6.0.3.dev2}/src/datacustomcode/llm_gateway/types/__init__.py +0 -0
  52. {salesforce_data_customcode-6.0.3.dev1 → salesforce_data_customcode-6.0.3.dev2}/src/datacustomcode/llm_gateway/types/generate_text_request.py +0 -0
  53. {salesforce_data_customcode-6.0.3.dev1 → salesforce_data_customcode-6.0.3.dev2}/src/datacustomcode/llm_gateway/types/generate_text_request_builder.py +0 -0
  54. {salesforce_data_customcode-6.0.3.dev1 → salesforce_data_customcode-6.0.3.dev2}/src/datacustomcode/llm_gateway/types/generate_text_response.py +0 -0
  55. {salesforce_data_customcode-6.0.3.dev1 → salesforce_data_customcode-6.0.3.dev2}/src/datacustomcode/llm_gateway/types/generate_text_response_builder.py +0 -0
  56. {salesforce_data_customcode-6.0.3.dev1 → salesforce_data_customcode-6.0.3.dev2}/src/datacustomcode/llm_gateway_config.py +0 -0
  57. {salesforce_data_customcode-6.0.3.dev1 → salesforce_data_customcode-6.0.3.dev2}/src/datacustomcode/mixin.py +0 -0
  58. {salesforce_data_customcode-6.0.3.dev1 → salesforce_data_customcode-6.0.3.dev2}/src/datacustomcode/py.typed +0 -0
  59. {salesforce_data_customcode-6.0.3.dev1 → salesforce_data_customcode-6.0.3.dev2}/src/datacustomcode/run.py +0 -0
  60. {salesforce_data_customcode-6.0.3.dev1 → salesforce_data_customcode-6.0.3.dev2}/src/datacustomcode/scan.py +0 -0
  61. {salesforce_data_customcode-6.0.3.dev1 → salesforce_data_customcode-6.0.3.dev2}/src/datacustomcode/spark/__init__.py +0 -0
  62. {salesforce_data_customcode-6.0.3.dev1 → salesforce_data_customcode-6.0.3.dev2}/src/datacustomcode/spark/base.py +0 -0
  63. {salesforce_data_customcode-6.0.3.dev1 → salesforce_data_customcode-6.0.3.dev2}/src/datacustomcode/spark/default.py +0 -0
  64. {salesforce_data_customcode-6.0.3.dev1 → salesforce_data_customcode-6.0.3.dev2}/src/datacustomcode/template.py +0 -0
  65. {salesforce_data_customcode-6.0.3.dev1 → salesforce_data_customcode-6.0.3.dev2}/src/datacustomcode/templates/__init__.py +0 -0
  66. {salesforce_data_customcode-6.0.3.dev1 → salesforce_data_customcode-6.0.3.dev2}/src/datacustomcode/templates/function/.devcontainer/devcontainer.json +0 -0
  67. {salesforce_data_customcode-6.0.3.dev1 → salesforce_data_customcode-6.0.3.dev2}/src/datacustomcode/templates/function/Dockerfile.dependencies +0 -0
  68. {salesforce_data_customcode-6.0.3.dev1 → salesforce_data_customcode-6.0.3.dev2}/src/datacustomcode/templates/function/README.md +0 -0
  69. {salesforce_data_customcode-6.0.3.dev1 → salesforce_data_customcode-6.0.3.dev2}/src/datacustomcode/templates/function/build_native_dependencies.sh +0 -0
  70. {salesforce_data_customcode-6.0.3.dev1 → salesforce_data_customcode-6.0.3.dev2}/src/datacustomcode/templates/function/chunking/payload/config.json +0 -0
  71. {salesforce_data_customcode-6.0.3.dev1 → salesforce_data_customcode-6.0.3.dev2}/src/datacustomcode/templates/function/chunking/payload/entrypoint.py +0 -0
  72. {salesforce_data_customcode-6.0.3.dev1 → salesforce_data_customcode-6.0.3.dev2}/src/datacustomcode/templates/function/chunking/requirements.txt +0 -0
  73. {salesforce_data_customcode-6.0.3.dev1 → salesforce_data_customcode-6.0.3.dev2}/src/datacustomcode/templates/function/example/chunking_with_llm/config.json +0 -0
  74. {salesforce_data_customcode-6.0.3.dev1 → salesforce_data_customcode-6.0.3.dev2}/src/datacustomcode/templates/function/example/chunking_with_llm/entrypoint.py +0 -0
  75. {salesforce_data_customcode-6.0.3.dev1 → salesforce_data_customcode-6.0.3.dev2}/src/datacustomcode/templates/function/example/chunking_with_llm/files/chunking_prompt.txt +0 -0
  76. {salesforce_data_customcode-6.0.3.dev1 → salesforce_data_customcode-6.0.3.dev2}/src/datacustomcode/templates/function/example/chunking_with_llm/tests/test.json +0 -0
  77. {salesforce_data_customcode-6.0.3.dev1 → salesforce_data_customcode-6.0.3.dev2}/src/datacustomcode/templates/function/example/chunking_with_prediction/config.json +0 -0
  78. {salesforce_data_customcode-6.0.3.dev1 → salesforce_data_customcode-6.0.3.dev2}/src/datacustomcode/templates/function/example/chunking_with_prediction/entrypoint.py +0 -0
  79. {salesforce_data_customcode-6.0.3.dev1 → salesforce_data_customcode-6.0.3.dev2}/src/datacustomcode/templates/function/example/chunking_with_prediction/tests/test.json +0 -0
  80. {salesforce_data_customcode-6.0.3.dev1 → salesforce_data_customcode-6.0.3.dev2}/src/datacustomcode/templates/function/payload/config.json +0 -0
  81. {salesforce_data_customcode-6.0.3.dev1 → salesforce_data_customcode-6.0.3.dev2}/src/datacustomcode/templates/function/payload/entrypoint.py +0 -0
  82. {salesforce_data_customcode-6.0.3.dev1 → salesforce_data_customcode-6.0.3.dev2}/src/datacustomcode/templates/function/requirements-dev.txt +0 -0
  83. {salesforce_data_customcode-6.0.3.dev1 → salesforce_data_customcode-6.0.3.dev2}/src/datacustomcode/templates/function/requirements.txt +0 -0
  84. {salesforce_data_customcode-6.0.3.dev1 → salesforce_data_customcode-6.0.3.dev2}/src/datacustomcode/templates/script/.devcontainer/devcontainer.json +0 -0
  85. {salesforce_data_customcode-6.0.3.dev1 → salesforce_data_customcode-6.0.3.dev2}/src/datacustomcode/templates/script/Dockerfile +0 -0
  86. {salesforce_data_customcode-6.0.3.dev1 → salesforce_data_customcode-6.0.3.dev2}/src/datacustomcode/templates/script/Dockerfile.dependencies +0 -0
  87. {salesforce_data_customcode-6.0.3.dev1 → salesforce_data_customcode-6.0.3.dev2}/src/datacustomcode/templates/script/README.md +0 -0
  88. {salesforce_data_customcode-6.0.3.dev1 → salesforce_data_customcode-6.0.3.dev2}/src/datacustomcode/templates/script/account.ipynb +0 -0
  89. {salesforce_data_customcode-6.0.3.dev1 → salesforce_data_customcode-6.0.3.dev2}/src/datacustomcode/templates/script/build_native_dependencies.sh +0 -0
  90. {salesforce_data_customcode-6.0.3.dev1 → salesforce_data_customcode-6.0.3.dev2}/src/datacustomcode/templates/script/examples/employee_hierarchy/employee_data.csv +0 -0
  91. {salesforce_data_customcode-6.0.3.dev1 → salesforce_data_customcode-6.0.3.dev2}/src/datacustomcode/templates/script/examples/employee_hierarchy/entrypoint.py +0 -0
  92. {salesforce_data_customcode-6.0.3.dev1 → salesforce_data_customcode-6.0.3.dev2}/src/datacustomcode/templates/script/jupyterlab.sh +0 -0
  93. {salesforce_data_customcode-6.0.3.dev1 → salesforce_data_customcode-6.0.3.dev2}/src/datacustomcode/templates/script/payload/config.json +0 -0
  94. {salesforce_data_customcode-6.0.3.dev1 → salesforce_data_customcode-6.0.3.dev2}/src/datacustomcode/templates/script/requirements-dev.txt +0 -0
  95. {salesforce_data_customcode-6.0.3.dev1 → salesforce_data_customcode-6.0.3.dev2}/src/datacustomcode/templates/script/requirements.txt +0 -0
  96. {salesforce_data_customcode-6.0.3.dev1 → salesforce_data_customcode-6.0.3.dev2}/src/datacustomcode/token_provider.py +0 -0
  97. {salesforce_data_customcode-6.0.3.dev1 → salesforce_data_customcode-6.0.3.dev2}/src/datacustomcode/version.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: salesforce-data-customcode
3
- Version: 6.0.3.dev1
3
+ Version: 6.0.3.dev2
4
4
  Summary: Data Cloud Custom Code SDK
5
5
  License-Expression: Apache-2.0
6
6
  License-File: LICENSE.txt
@@ -341,13 +341,16 @@ from datacustomcode.client import Client, llm_gateway_generate_text_col
341
341
  def main():
342
342
  client = Client()
343
343
  df = client.read_dlo("Input__dll")
344
+ # llm_gateway_generate_text_col returns a struct
345
+ # {status, response, error_code, error_message} per row, so per-row
346
+ # failures don't abort the Spark job. Pick the field you want with [].
344
347
  df_generated = df.withColumn(
345
348
  "greeting__c",
346
349
  llm_gateway_generate_text_col(
347
350
  "In one sentence, greet {name} from {city}.",
348
351
  {"name": col("name__c"), "city": col("homecity__c")},
349
352
  model_id="sfdc_ai__DefaultGPT4Omni", # An AI model in your org
350
- ),
353
+ )["response"],
351
354
  )
352
355
 
353
356
  dlo_name = "Output_dll"
@@ -316,13 +316,16 @@ from datacustomcode.client import Client, llm_gateway_generate_text_col
316
316
  def main():
317
317
  client = Client()
318
318
  df = client.read_dlo("Input__dll")
319
+ # llm_gateway_generate_text_col returns a struct
320
+ # {status, response, error_code, error_message} per row, so per-row
321
+ # failures don't abort the Spark job. Pick the field you want with [].
319
322
  df_generated = df.withColumn(
320
323
  "greeting__c",
321
324
  llm_gateway_generate_text_col(
322
325
  "In one sentence, greet {name} from {city}.",
323
326
  {"name": col("name__c"), "city": col("homecity__c")},
324
327
  model_id="sfdc_ai__DefaultGPT4Omni", # An AI model in your org
325
- ),
328
+ )["response"],
326
329
  )
327
330
 
328
331
  dlo_name = "Output_dll"
@@ -18,7 +18,7 @@ license = "Apache-2.0"
18
18
  name = "salesforce-data-customcode"
19
19
  readme = "README.md"
20
20
  requires-python = ">=3.10,<3.12"
21
- version = "6.0.3.dev1"
21
+ version = "6.0.3.dev2"
22
22
 
23
23
  [tool.black]
24
24
  exclude = '''
@@ -62,15 +62,23 @@ def llm_gateway_generate_text_col(
62
62
  ) -> "Column":
63
63
  """Build a Spark Column that runs the LLM Gateway per row.
64
64
 
65
+ The returned Column yields a struct ``{status, response, error_code,
66
+ error_message}`` for each row. Use ``[...]`` (or ``getField``) to pick the
67
+ field you want, e.g. ``llm_gateway_generate_text_col(...)["response"]``.
68
+ Per-row failures populate ``status`` / ``error_code`` / ``error_message``
69
+ so a single bad row does not abort the whole Spark job.
70
+
65
71
  Example:
66
72
 
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
- ... ),
73
+ >>> result = llm_gateway_generate_text_col(
74
+ ... "In one sentence, greet {name} from {city}.",
75
+ ... {"name": col("name__c"), "city": col("homecity__c")},
76
+ ... model_id="sfdc_ai__DefaultGPT4Omni",
77
+ ... )
78
+ >>> df.withColumn("greeting__c", result["response"])
79
+ >>> # …or keep the struct around and inspect failures:
80
+ >>> df.withColumn("llm", result).select(
81
+ ... "llm.status", "llm.response", "llm.error_message"
74
82
  ... )
75
83
 
76
84
  Args:
@@ -81,7 +89,11 @@ def llm_gateway_generate_text_col(
81
89
  model_id: LLM model id. Defaults to ``sfdc_ai__DefaultGPT4Omni``.
82
90
 
83
91
  Returns:
84
- A Spark ``Column`` that, when evaluated, produces the generated text.
92
+ A Spark ``Column`` of ``StructType`` with fields ``status``,
93
+ ``response``, ``error_code``, and ``error_message`` (all nullable
94
+ strings). On success, ``status == "SUCCESS"`` and ``response`` holds
95
+ the generated text; on failure, ``status == "ERROR"`` and the
96
+ ``error_*`` fields carry diagnostic detail.
85
97
  """
86
98
  gateway = Client()._get_spark_llm_gateway()
87
99
  return gateway.llm_gateway_generate_text_col(template, values, model_id=model_id)
@@ -50,4 +50,13 @@ class SparkLLMGateway(ABC, UserExtendableNamedConfigMixin):
50
50
  values: Union[Dict[str, "Column"], "Column"],
51
51
  model_id: Optional[str] = None,
52
52
  ) -> "Column":
53
- """Build a Spark ``Column`` that invokes the LLM Gateway per row."""
53
+ """Build a Spark ``Column`` that invokes the LLM Gateway per row and
54
+ yields a struct ``{status, response, error_code, error_message}``.
55
+
56
+ Select an individual field, e.g.
57
+ ``llm_gateway_generate_text_col(...)["response"]``. Returning a struct
58
+ means a single failing row doesn't abort the Spark job.
59
+ Failing row leaves the rest of the DataFrame intact — callers can
60
+ inspect ``status`` / ``error_code`` per row instead of having the
61
+ Spark job abort.
62
+ """
@@ -28,10 +28,16 @@ if TYPE_CHECKING:
28
28
  from pyspark.sql import Column
29
29
 
30
30
  from datacustomcode.llm_gateway.base import LLMGateway
31
+ from datacustomcode.llm_gateway.types.generate_text_response import (
32
+ GenerateTextResponse,
33
+ )
31
34
 
32
35
 
33
36
  _DEFAULT_LLM_MODEL_ID = "sfdc_ai__DefaultGPT4Omni"
34
37
 
38
+ _STATUS_SUCCESS = "SUCCESS"
39
+ _STATUS_ERROR = "ERROR"
40
+
35
41
 
36
42
  class DefaultSparkLLMGateway(SparkLLMGateway):
37
43
 
@@ -60,9 +66,17 @@ class DefaultSparkLLMGateway(SparkLLMGateway):
60
66
  values: Union[Dict[str, "Column"], "Column"],
61
67
  model_id: Optional[str] = None,
62
68
  ) -> "Column":
63
-
69
+ """Build a per-row UDF that returns a struct ``{status, response,
70
+ error_code, error_message}`` so per-row failures do not abort the
71
+ Spark job. Callers select the field they want, e.g.
72
+ ``llm_gateway_generate_text_col(...)["response"]``.
73
+ """
64
74
  from pyspark.sql.functions import struct, udf
65
- from pyspark.sql.types import StringType
75
+ from pyspark.sql.types import (
76
+ StringType,
77
+ StructField,
78
+ StructType,
79
+ )
66
80
 
67
81
  if isinstance(values, dict):
68
82
  values_col = struct(*[v.alias(k) for k, v in values.items()])
@@ -70,19 +84,32 @@ class DefaultSparkLLMGateway(SparkLLMGateway):
70
84
  values_col = values
71
85
 
72
86
  gateway = self._llm_gateway
87
+ result_schema = StructType(
88
+ [
89
+ StructField("status", StringType(), True),
90
+ StructField("response", StringType(), True),
91
+ StructField("error_code", StringType(), True),
92
+ StructField("error_message", StringType(), True),
93
+ ]
94
+ )
73
95
 
74
- def _generate(values_row: Any) -> str:
96
+ def _generate(values_row: Any) -> Dict[str, Optional[str]]:
75
97
  if values_row is None:
76
- return ""
98
+ return {
99
+ "status": _STATUS_ERROR,
100
+ "response": None,
101
+ "error_code": None,
102
+ "error_message": "values column was null for this row",
103
+ }
77
104
  subs = (
78
105
  values_row.asDict()
79
106
  if hasattr(values_row, "asDict")
80
107
  else dict(values_row)
81
108
  )
82
109
  prompt = template.format(**subs)
83
- return _invoke_llm_gateway(gateway, prompt, model_id)
110
+ return _invoke_llm_gateway_as_struct(gateway, prompt, model_id)
84
111
 
85
- return udf(_generate, StringType())(values_col)
112
+ return udf(_generate, result_schema)(values_col)
86
113
 
87
114
 
88
115
  def _build_underlying_gateway() -> "LLMGateway":
@@ -97,22 +124,33 @@ def _build_underlying_gateway() -> "LLMGateway":
97
124
  return cfg.to_object()
98
125
 
99
126
 
100
- def _invoke_llm_gateway(
127
+ def _call_llm_gateway(
101
128
  gateway: "LLMGateway",
102
129
  prompt: str,
103
130
  model_id: Optional[str],
104
- ) -> str:
105
- from datacustomcode.llm_gateway.errors import LLMGatewayCallError
131
+ ) -> "GenerateTextResponse":
132
+ """Build the request and dispatch it to the underlying gateway."""
106
133
  from datacustomcode.llm_gateway.types.generate_text_request_builder import (
107
134
  GenerateTextRequestBuilder,
108
135
  )
109
136
 
110
- builder = (
137
+ request = (
111
138
  GenerateTextRequestBuilder()
112
139
  .set_prompt(prompt)
113
140
  .set_model(model_id or _DEFAULT_LLM_MODEL_ID)
141
+ .build()
114
142
  )
115
- response = gateway.generate_text(builder.build())
143
+ return gateway.generate_text(request)
144
+
145
+
146
+ def _invoke_llm_gateway(
147
+ gateway: "LLMGateway",
148
+ prompt: str,
149
+ model_id: Optional[str],
150
+ ) -> str:
151
+ from datacustomcode.llm_gateway.errors import LLMGatewayCallError
152
+
153
+ response = _call_llm_gateway(gateway, prompt, model_id)
116
154
  if response.is_error:
117
155
  raise LLMGatewayCallError(
118
156
  f"LLM Gateway call failed: status_code={response.status_code}, "
@@ -123,3 +161,24 @@ def _invoke_llm_gateway(
123
161
  error_message=str(response.data) if response.data else None,
124
162
  )
125
163
  return response.text
164
+
165
+
166
+ def _invoke_llm_gateway_as_struct(
167
+ gateway: "LLMGateway",
168
+ prompt: str,
169
+ model_id: Optional[str],
170
+ ) -> Dict[str, Optional[str]]:
171
+ response = _call_llm_gateway(gateway, prompt, model_id)
172
+ if response.is_error:
173
+ return {
174
+ "status": _STATUS_ERROR,
175
+ "response": None,
176
+ "error_code": response.error_code or None,
177
+ "error_message": str(response.data) if response.data else None,
178
+ }
179
+ return {
180
+ "status": _STATUS_SUCCESS,
181
+ "response": response.text,
182
+ "error_code": None,
183
+ "error_message": None,
184
+ }
@@ -16,7 +16,9 @@ def main():
16
16
  You can use your AI models configured in Salesforce to generate column
17
17
  values. See README.md for how to test locally before deploying to Data Cloud.
18
18
 
19
- Example:
19
+ Example (the per-row helper returns a struct
20
+ ``{status, response, error_code, error_message}`` — pick the field you
21
+ want with ``[...]``):
20
22
 
21
23
  >>> from datacustomcode.client import llm_gateway_generate_text_col
22
24
  df_generated = df.withColumn(
@@ -25,7 +27,7 @@ def main():
25
27
  ... "In one sentence, greet {name} from {city}.",
26
28
  ... {"name": col("name__c"), "city": col("homecity__c")},
27
29
  ... model_id="sfdc_ai__DefaultGPT4Omni",
28
- ... ),
30
+ ... )["response"],
29
31
  ... )
30
32
 
31
33
  You can also invoke the LLM with a literal plain text prompt — no