nv-ingest-api 26.1.0rc4__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.

Potentially problematic release.


This version of nv-ingest-api might be problematic. Click here for more details.

Files changed (177) hide show
  1. nv_ingest_api/__init__.py +3 -0
  2. nv_ingest_api/interface/__init__.py +218 -0
  3. nv_ingest_api/interface/extract.py +977 -0
  4. nv_ingest_api/interface/mutate.py +154 -0
  5. nv_ingest_api/interface/store.py +200 -0
  6. nv_ingest_api/interface/transform.py +382 -0
  7. nv_ingest_api/interface/utility.py +186 -0
  8. nv_ingest_api/internal/__init__.py +0 -0
  9. nv_ingest_api/internal/enums/__init__.py +3 -0
  10. nv_ingest_api/internal/enums/common.py +550 -0
  11. nv_ingest_api/internal/extract/__init__.py +3 -0
  12. nv_ingest_api/internal/extract/audio/__init__.py +3 -0
  13. nv_ingest_api/internal/extract/audio/audio_extraction.py +202 -0
  14. nv_ingest_api/internal/extract/docx/__init__.py +5 -0
  15. nv_ingest_api/internal/extract/docx/docx_extractor.py +232 -0
  16. nv_ingest_api/internal/extract/docx/engines/__init__.py +0 -0
  17. nv_ingest_api/internal/extract/docx/engines/docxreader_helpers/__init__.py +3 -0
  18. nv_ingest_api/internal/extract/docx/engines/docxreader_helpers/docx_helper.py +127 -0
  19. nv_ingest_api/internal/extract/docx/engines/docxreader_helpers/docxreader.py +971 -0
  20. nv_ingest_api/internal/extract/html/__init__.py +3 -0
  21. nv_ingest_api/internal/extract/html/html_extractor.py +84 -0
  22. nv_ingest_api/internal/extract/image/__init__.py +3 -0
  23. nv_ingest_api/internal/extract/image/chart_extractor.py +375 -0
  24. nv_ingest_api/internal/extract/image/image_extractor.py +208 -0
  25. nv_ingest_api/internal/extract/image/image_helpers/__init__.py +3 -0
  26. nv_ingest_api/internal/extract/image/image_helpers/common.py +433 -0
  27. nv_ingest_api/internal/extract/image/infographic_extractor.py +290 -0
  28. nv_ingest_api/internal/extract/image/ocr_extractor.py +407 -0
  29. nv_ingest_api/internal/extract/image/table_extractor.py +391 -0
  30. nv_ingest_api/internal/extract/pdf/__init__.py +3 -0
  31. nv_ingest_api/internal/extract/pdf/engines/__init__.py +19 -0
  32. nv_ingest_api/internal/extract/pdf/engines/adobe.py +484 -0
  33. nv_ingest_api/internal/extract/pdf/engines/llama.py +246 -0
  34. nv_ingest_api/internal/extract/pdf/engines/nemotron_parse.py +598 -0
  35. nv_ingest_api/internal/extract/pdf/engines/pdf_helpers/__init__.py +166 -0
  36. nv_ingest_api/internal/extract/pdf/engines/pdfium.py +652 -0
  37. nv_ingest_api/internal/extract/pdf/engines/tika.py +96 -0
  38. nv_ingest_api/internal/extract/pdf/engines/unstructured_io.py +426 -0
  39. nv_ingest_api/internal/extract/pdf/pdf_extractor.py +74 -0
  40. nv_ingest_api/internal/extract/pptx/__init__.py +5 -0
  41. nv_ingest_api/internal/extract/pptx/engines/__init__.py +0 -0
  42. nv_ingest_api/internal/extract/pptx/engines/pptx_helper.py +968 -0
  43. nv_ingest_api/internal/extract/pptx/pptx_extractor.py +210 -0
  44. nv_ingest_api/internal/meta/__init__.py +3 -0
  45. nv_ingest_api/internal/meta/udf.py +232 -0
  46. nv_ingest_api/internal/mutate/__init__.py +3 -0
  47. nv_ingest_api/internal/mutate/deduplicate.py +110 -0
  48. nv_ingest_api/internal/mutate/filter.py +133 -0
  49. nv_ingest_api/internal/primitives/__init__.py +0 -0
  50. nv_ingest_api/internal/primitives/control_message_task.py +16 -0
  51. nv_ingest_api/internal/primitives/ingest_control_message.py +307 -0
  52. nv_ingest_api/internal/primitives/nim/__init__.py +9 -0
  53. nv_ingest_api/internal/primitives/nim/default_values.py +14 -0
  54. nv_ingest_api/internal/primitives/nim/model_interface/__init__.py +3 -0
  55. nv_ingest_api/internal/primitives/nim/model_interface/cached.py +274 -0
  56. nv_ingest_api/internal/primitives/nim/model_interface/decorators.py +56 -0
  57. nv_ingest_api/internal/primitives/nim/model_interface/deplot.py +270 -0
  58. nv_ingest_api/internal/primitives/nim/model_interface/helpers.py +338 -0
  59. nv_ingest_api/internal/primitives/nim/model_interface/nemotron_parse.py +239 -0
  60. nv_ingest_api/internal/primitives/nim/model_interface/ocr.py +776 -0
  61. nv_ingest_api/internal/primitives/nim/model_interface/parakeet.py +367 -0
  62. nv_ingest_api/internal/primitives/nim/model_interface/text_embedding.py +129 -0
  63. nv_ingest_api/internal/primitives/nim/model_interface/vlm.py +177 -0
  64. nv_ingest_api/internal/primitives/nim/model_interface/yolox.py +1681 -0
  65. nv_ingest_api/internal/primitives/nim/nim_client.py +801 -0
  66. nv_ingest_api/internal/primitives/nim/nim_model_interface.py +126 -0
  67. nv_ingest_api/internal/primitives/tracing/__init__.py +0 -0
  68. nv_ingest_api/internal/primitives/tracing/latency.py +69 -0
  69. nv_ingest_api/internal/primitives/tracing/logging.py +96 -0
  70. nv_ingest_api/internal/primitives/tracing/tagging.py +288 -0
  71. nv_ingest_api/internal/schemas/__init__.py +3 -0
  72. nv_ingest_api/internal/schemas/extract/__init__.py +3 -0
  73. nv_ingest_api/internal/schemas/extract/extract_audio_schema.py +133 -0
  74. nv_ingest_api/internal/schemas/extract/extract_chart_schema.py +144 -0
  75. nv_ingest_api/internal/schemas/extract/extract_docx_schema.py +129 -0
  76. nv_ingest_api/internal/schemas/extract/extract_html_schema.py +34 -0
  77. nv_ingest_api/internal/schemas/extract/extract_image_schema.py +126 -0
  78. nv_ingest_api/internal/schemas/extract/extract_infographic_schema.py +137 -0
  79. nv_ingest_api/internal/schemas/extract/extract_ocr_schema.py +137 -0
  80. nv_ingest_api/internal/schemas/extract/extract_pdf_schema.py +220 -0
  81. nv_ingest_api/internal/schemas/extract/extract_pptx_schema.py +128 -0
  82. nv_ingest_api/internal/schemas/extract/extract_table_schema.py +137 -0
  83. nv_ingest_api/internal/schemas/message_brokers/__init__.py +3 -0
  84. nv_ingest_api/internal/schemas/message_brokers/message_broker_client_schema.py +37 -0
  85. nv_ingest_api/internal/schemas/message_brokers/request_schema.py +34 -0
  86. nv_ingest_api/internal/schemas/message_brokers/response_schema.py +19 -0
  87. nv_ingest_api/internal/schemas/meta/__init__.py +3 -0
  88. nv_ingest_api/internal/schemas/meta/base_model_noext.py +11 -0
  89. nv_ingest_api/internal/schemas/meta/ingest_job_schema.py +355 -0
  90. nv_ingest_api/internal/schemas/meta/metadata_schema.py +394 -0
  91. nv_ingest_api/internal/schemas/meta/udf.py +23 -0
  92. nv_ingest_api/internal/schemas/mixins.py +39 -0
  93. nv_ingest_api/internal/schemas/mutate/__init__.py +3 -0
  94. nv_ingest_api/internal/schemas/mutate/mutate_image_dedup_schema.py +16 -0
  95. nv_ingest_api/internal/schemas/store/__init__.py +3 -0
  96. nv_ingest_api/internal/schemas/store/store_embedding_schema.py +28 -0
  97. nv_ingest_api/internal/schemas/store/store_image_schema.py +45 -0
  98. nv_ingest_api/internal/schemas/transform/__init__.py +3 -0
  99. nv_ingest_api/internal/schemas/transform/transform_image_caption_schema.py +36 -0
  100. nv_ingest_api/internal/schemas/transform/transform_image_filter_schema.py +17 -0
  101. nv_ingest_api/internal/schemas/transform/transform_text_embedding_schema.py +48 -0
  102. nv_ingest_api/internal/schemas/transform/transform_text_splitter_schema.py +24 -0
  103. nv_ingest_api/internal/store/__init__.py +3 -0
  104. nv_ingest_api/internal/store/embed_text_upload.py +236 -0
  105. nv_ingest_api/internal/store/image_upload.py +251 -0
  106. nv_ingest_api/internal/transform/__init__.py +3 -0
  107. nv_ingest_api/internal/transform/caption_image.py +219 -0
  108. nv_ingest_api/internal/transform/embed_text.py +702 -0
  109. nv_ingest_api/internal/transform/split_text.py +182 -0
  110. nv_ingest_api/util/__init__.py +3 -0
  111. nv_ingest_api/util/control_message/__init__.py +0 -0
  112. nv_ingest_api/util/control_message/validators.py +47 -0
  113. nv_ingest_api/util/converters/__init__.py +0 -0
  114. nv_ingest_api/util/converters/bytetools.py +78 -0
  115. nv_ingest_api/util/converters/containers.py +65 -0
  116. nv_ingest_api/util/converters/datetools.py +90 -0
  117. nv_ingest_api/util/converters/dftools.py +127 -0
  118. nv_ingest_api/util/converters/formats.py +64 -0
  119. nv_ingest_api/util/converters/type_mappings.py +27 -0
  120. nv_ingest_api/util/dataloader/__init__.py +9 -0
  121. nv_ingest_api/util/dataloader/dataloader.py +409 -0
  122. nv_ingest_api/util/detectors/__init__.py +5 -0
  123. nv_ingest_api/util/detectors/language.py +38 -0
  124. nv_ingest_api/util/exception_handlers/__init__.py +0 -0
  125. nv_ingest_api/util/exception_handlers/converters.py +72 -0
  126. nv_ingest_api/util/exception_handlers/decorators.py +429 -0
  127. nv_ingest_api/util/exception_handlers/detectors.py +74 -0
  128. nv_ingest_api/util/exception_handlers/pdf.py +116 -0
  129. nv_ingest_api/util/exception_handlers/schemas.py +68 -0
  130. nv_ingest_api/util/image_processing/__init__.py +5 -0
  131. nv_ingest_api/util/image_processing/clustering.py +260 -0
  132. nv_ingest_api/util/image_processing/processing.py +177 -0
  133. nv_ingest_api/util/image_processing/table_and_chart.py +504 -0
  134. nv_ingest_api/util/image_processing/transforms.py +850 -0
  135. nv_ingest_api/util/imports/__init__.py +3 -0
  136. nv_ingest_api/util/imports/callable_signatures.py +108 -0
  137. nv_ingest_api/util/imports/dynamic_resolvers.py +158 -0
  138. nv_ingest_api/util/introspection/__init__.py +3 -0
  139. nv_ingest_api/util/introspection/class_inspect.py +145 -0
  140. nv_ingest_api/util/introspection/function_inspect.py +65 -0
  141. nv_ingest_api/util/logging/__init__.py +0 -0
  142. nv_ingest_api/util/logging/configuration.py +102 -0
  143. nv_ingest_api/util/logging/sanitize.py +84 -0
  144. nv_ingest_api/util/message_brokers/__init__.py +3 -0
  145. nv_ingest_api/util/message_brokers/qos_scheduler.py +283 -0
  146. nv_ingest_api/util/message_brokers/simple_message_broker/__init__.py +9 -0
  147. nv_ingest_api/util/message_brokers/simple_message_broker/broker.py +465 -0
  148. nv_ingest_api/util/message_brokers/simple_message_broker/ordered_message_queue.py +71 -0
  149. nv_ingest_api/util/message_brokers/simple_message_broker/simple_client.py +455 -0
  150. nv_ingest_api/util/metadata/__init__.py +5 -0
  151. nv_ingest_api/util/metadata/aggregators.py +516 -0
  152. nv_ingest_api/util/multi_processing/__init__.py +8 -0
  153. nv_ingest_api/util/multi_processing/mp_pool_singleton.py +200 -0
  154. nv_ingest_api/util/nim/__init__.py +161 -0
  155. nv_ingest_api/util/pdf/__init__.py +3 -0
  156. nv_ingest_api/util/pdf/pdfium.py +428 -0
  157. nv_ingest_api/util/schema/__init__.py +3 -0
  158. nv_ingest_api/util/schema/schema_validator.py +10 -0
  159. nv_ingest_api/util/service_clients/__init__.py +3 -0
  160. nv_ingest_api/util/service_clients/client_base.py +86 -0
  161. nv_ingest_api/util/service_clients/kafka/__init__.py +3 -0
  162. nv_ingest_api/util/service_clients/redis/__init__.py +3 -0
  163. nv_ingest_api/util/service_clients/redis/redis_client.py +983 -0
  164. nv_ingest_api/util/service_clients/rest/__init__.py +0 -0
  165. nv_ingest_api/util/service_clients/rest/rest_client.py +595 -0
  166. nv_ingest_api/util/string_processing/__init__.py +51 -0
  167. nv_ingest_api/util/string_processing/configuration.py +682 -0
  168. nv_ingest_api/util/string_processing/yaml.py +109 -0
  169. nv_ingest_api/util/system/__init__.py +0 -0
  170. nv_ingest_api/util/system/hardware_info.py +594 -0
  171. nv_ingest_api-26.1.0rc4.dist-info/METADATA +237 -0
  172. nv_ingest_api-26.1.0rc4.dist-info/RECORD +177 -0
  173. nv_ingest_api-26.1.0rc4.dist-info/WHEEL +5 -0
  174. nv_ingest_api-26.1.0rc4.dist-info/licenses/LICENSE +201 -0
  175. nv_ingest_api-26.1.0rc4.dist-info/top_level.txt +2 -0
  176. udfs/__init__.py +5 -0
  177. udfs/llm_summarizer_udf.py +259 -0
@@ -0,0 +1,3 @@
1
+ # SPDX-FileCopyrightText: Copyright (c) 2024-25, NVIDIA CORPORATION & AFFILIATES.
2
+ # All rights reserved.
3
+ # SPDX-License-Identifier: Apache-2.0
@@ -0,0 +1,218 @@
1
+ # SPDX-FileCopyrightText: Copyright (c) 2024-25, NVIDIA CORPORATION & AFFILIATES.
2
+ # All rights reserved.
3
+ # SPDX-License-Identifier: Apache-2.0
4
+
5
+ import logging
6
+ import functools
7
+ import inspect
8
+ import pprint
9
+ from typing import Dict, Any, Optional, List
10
+
11
+ from pydantic import BaseModel
12
+
13
+ from nv_ingest_api.internal.schemas.extract.extract_pdf_schema import PDFiumConfigSchema, NemotronParseConfigSchema
14
+ from nv_ingest_api.util.logging.sanitize import sanitize_for_logging
15
+
16
+ logger = logging.getLogger(__name__)
17
+
18
+ ## CONFIG_SCHEMAS is a global dictionary that maps extraction methods to Pydantic schemas.
19
+ CONFIG_SCHEMAS: Dict[str, Any] = {
20
+ "adobe": PDFiumConfigSchema,
21
+ "llama": PDFiumConfigSchema,
22
+ "nemotron_parse": NemotronParseConfigSchema,
23
+ "pdfium": PDFiumConfigSchema,
24
+ "tika": PDFiumConfigSchema,
25
+ "unstructured_io": PDFiumConfigSchema,
26
+ }
27
+
28
+
29
+ def _build_config_from_schema(schema_class: type[BaseModel], args: Dict[str, Any]) -> Dict[str, Any]:
30
+ """
31
+ Build and validate a configuration dictionary from the provided arguments using a Pydantic schema.
32
+
33
+ This function filters the supplied arguments to include only those keys defined in the given
34
+ Pydantic schema (using Pydantic v2's `model_fields`), instantiates the schema for validation,
35
+ and returns the validated configuration as a dictionary.
36
+
37
+ Parameters
38
+ ----------
39
+ schema_class : type[BaseModel]
40
+ The Pydantic BaseModel subclass used for validating the configuration.
41
+ args : dict
42
+ A dictionary of arguments from which to extract and validate configuration data.
43
+
44
+ Returns
45
+ -------
46
+ dict
47
+ A dictionary containing the validated configuration data as defined by the schema.
48
+
49
+ Raises
50
+ ------
51
+ pydantic.ValidationError
52
+ If the provided arguments do not conform to the schema.
53
+ """
54
+ field_names = schema_class.model_fields.keys()
55
+ config_data = {k: v for k, v in args.items() if k in field_names}
56
+ # Instantiate the schema to perform validation, then return the model's dictionary representation.
57
+
58
+ return schema_class(**config_data).dict()
59
+
60
+
61
+ def extraction_interface_relay_constructor(api_fn, task_keys: Optional[List[str]] = None):
62
+ """
63
+ Decorator for constructing and validating configuration using Pydantic schemas.
64
+
65
+ This decorator wraps a user-facing interface function. It extracts common task parameters
66
+ (using the provided task_keys, or defaults if not specified) and method-specific configuration
67
+ parameters based on a required 'extract_method' keyword argument. It then uses the corresponding
68
+ Pydantic schema (from the global CONFIG_SCHEMAS registry) to validate and build a method-specific
69
+ configuration. The resulting composite configuration, along with the extraction ledger and
70
+ execution trace log, is passed to the backend API function.
71
+
72
+ Parameters
73
+ ----------
74
+ api_fn : callable
75
+ The backend API function that will be called with the extraction ledger, the task configuration
76
+ dictionary, the extractor configuration, and the execution trace log. This function must conform
77
+ to the signature:
78
+
79
+ extract_primitives_from_pdf_internal(df_extraction_ledger: pd.DataFrame,
80
+ task_config: Dict[str, Any],
81
+ extractor_config: Any,
82
+ execution_trace_log: Optional[List[Any]] = None)
83
+ task_keys : list of str, optional
84
+ A list of keyword names that should be extracted from the user function as common task parameters.
85
+ If not provided, defaults to ["extract_text", "extract_images", "extract_tables", "extract_charts"].
86
+
87
+ Returns
88
+ -------
89
+ callable
90
+ A wrapped function that builds and validates the configuration before invoking the backend API function.
91
+
92
+ Raises
93
+ ------
94
+ ValueError
95
+ If the extraction method specified is not supported (i.e., no corresponding Pydantic schema exists
96
+ in CONFIG_SCHEMAS), if api_fn does not conform to the expected signature, or if the required
97
+ 'extract_method' parameter is not provided.
98
+ """
99
+ # Verify that api_fn conforms to the expected signature.
100
+ try:
101
+ # Try binding four arguments: ledger, task_config, extractor_config, and execution_trace_log.
102
+ inspect.signature(api_fn).bind("dummy_ledger", {"dummy": True}, {"dummy": True}, {})
103
+ except TypeError as e:
104
+ raise ValueError(
105
+ "api_fn must conform to the signature: "
106
+ "extract_primitives_from_pdf(df_extraction_ledger, task_config, extractor_config, execution_trace_log)"
107
+ ) from e
108
+
109
+ if task_keys is None:
110
+ task_keys = []
111
+
112
+ def decorator(user_fn):
113
+ @functools.wraps(user_fn)
114
+ def wrapper(*args, **kwargs):
115
+ # Use bind_partial so that missing required arguments can be handled gracefully.
116
+ sig = inspect.signature(user_fn)
117
+ bound = sig.bind_partial(*args, **kwargs)
118
+ bound.apply_defaults()
119
+
120
+ # The first parameter is assumed to be the extraction ledger.
121
+ param_names = list(sig.parameters.keys())
122
+ if param_names[0] not in bound.arguments:
123
+ raise ValueError("Missing required ledger argument.")
124
+ ledger = bound.arguments[param_names[0]]
125
+
126
+ # Process reserved 'execution_trace_log'.
127
+ execution_trace_log = bound.arguments.get("execution_trace_log", None)
128
+ if execution_trace_log is None:
129
+ execution_trace_log = {} # Replace None with an empty dict.
130
+ if "execution_trace_log" in bound.arguments:
131
+ del bound.arguments["execution_trace_log"]
132
+
133
+ # Ensure that 'extract_method' is provided.
134
+ if "extract_method" not in bound.arguments or bound.arguments["extract_method"] is None:
135
+ raise ValueError("The 'extract_method' parameter is required.")
136
+ extract_method = bound.arguments["extract_method"]
137
+ del bound.arguments["extract_method"]
138
+
139
+ # Extract common task parameters using the specified task_keys.
140
+ task_params = {key: bound.arguments[key] for key in task_keys if key in bound.arguments}
141
+ task_params["extract_method"] = extract_method
142
+ task_config = {"params": task_params}
143
+
144
+ # Look up the appropriate Pydantic schema.
145
+ schema_class = CONFIG_SCHEMAS.get(extract_method)
146
+ if schema_class is None:
147
+ raise ValueError(f"Unsupported extraction method: {extract_method}")
148
+
149
+ # Build the method-specific configuration using the schema class.
150
+ extraction_config_dict = _build_config_from_schema(schema_class, bound.arguments)
151
+
152
+ # Create a Pydantic object instead of a dictionary for the specific extractor config
153
+ extractor_schema = None
154
+ try:
155
+ # Find the appropriate extractor schema class based on the extraction method
156
+ extractor_schema_name = f"{extract_method.capitalize()}ExtractorSchema"
157
+ extractor_schema_class = globals().get(extractor_schema_name)
158
+
159
+ if extractor_schema_class is None:
160
+ # Try another common naming pattern
161
+ extractor_schema_name = f"{extract_method.upper()}ExtractorSchema"
162
+ extractor_schema_class = globals().get(extractor_schema_name)
163
+
164
+ if extractor_schema_class is None:
165
+ # Final fallback attempt with camelCase
166
+ extractor_schema_name = f"{extract_method[0].upper() + extract_method[1:]}ExtractorSchema"
167
+ extractor_schema_class = globals().get(extractor_schema_name)
168
+
169
+ if extractor_schema_class is not None:
170
+ # Create the extractor schema with the method-specific config
171
+ config_key = f"{extract_method}_config"
172
+ extractor_schema = extractor_schema_class(**{config_key: extraction_config_dict})
173
+ else:
174
+ logger.warning(f"Could not find extractor schema class for method: {extract_method}")
175
+ except Exception as e:
176
+ logger.warning(f"Error creating extractor schema: {str(e)}")
177
+ # Fall back to dictionary approach if schema creation fails
178
+ extractor_schema = {f"{extract_method}_config": extraction_config_dict}
179
+
180
+ # If schema creation failed, fall back to dictionary
181
+ if extractor_schema is None:
182
+ extractor_schema = {f"{extract_method}_config": extraction_config_dict}
183
+
184
+ # Log the task and extractor configurations for debugging (sanitized)
185
+ logger.debug("\n" + "=" * 80)
186
+ logger.debug(f"DEBUG - API Function: {api_fn.__name__}")
187
+ logger.debug(f"DEBUG - Extract Method: {extract_method}")
188
+ logger.debug("-" * 80)
189
+
190
+ # Sanitize and format the task config as a string and log it
191
+ sanitized_task_config = sanitize_for_logging(task_config)
192
+ task_config_str = pprint.pformat(sanitized_task_config, width=100, sort_dicts=False)
193
+ logger.debug(f"DEBUG - Task Config (sanitized):\n{task_config_str}")
194
+ logger.debug("-" * 80)
195
+
196
+ # Sanitize and format the extractor config as a string and log it
197
+ if hasattr(extractor_schema, "model_dump"):
198
+ sanitized_extractor_config = sanitize_for_logging(extractor_schema.model_dump())
199
+ else:
200
+ sanitized_extractor_config = sanitize_for_logging(extractor_schema)
201
+ extractor_config_str = pprint.pformat(sanitized_extractor_config, width=100, sort_dicts=False)
202
+ logger.debug(f"DEBUG - Extractor Config Type: {type(extractor_schema)}")
203
+ logger.debug(f"DEBUG - Extractor Config (sanitized):\n{extractor_config_str}")
204
+ logger.debug("=" * 80 + "\n")
205
+
206
+ # Call the backend API function. Print sanitized configs for any debug consumers of stdout.
207
+ pprint.pprint(sanitized_task_config)
208
+ pprint.pprint(sanitized_extractor_config)
209
+ result = api_fn(ledger, task_config, extractor_schema, execution_trace_log)
210
+
211
+ # If the result is a tuple, return only the first element
212
+ if isinstance(result, tuple):
213
+ return result[0]
214
+ return result
215
+
216
+ return wrapper
217
+
218
+ return decorator