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,283 @@
1
+ # SPDX-FileCopyrightText: Copyright (c) 2024-25, NVIDIA CORPORATION & AFFILIATES.
2
+ # All rights reserved.
3
+ # SPDX-License-Identifier: Apache-2.0
4
+
5
+ from __future__ import annotations
6
+
7
+ from typing import Dict, Optional
8
+ import logging
9
+ import time
10
+ import random
11
+
12
+
13
+ class _SchedulingStrategy:
14
+ """
15
+ Base scheduling strategy interface. Implementations must provide a non-blocking
16
+ single-sweep attempt over non-immediate queues and return a job or None.
17
+ """
18
+
19
+ def try_once(self, client, queues: Dict[str, str], order: list[str]) -> Optional[dict]:
20
+ raise NotImplementedError
21
+
22
+
23
+ class _LotteryStrategy(_SchedulingStrategy):
24
+ """
25
+ Lottery scheduling with fixed weights.
26
+ Weights: micro=4, small=2, large=1, medium=1, default=1
27
+ """
28
+
29
+ def __init__(self, prioritize_immediate: bool = True) -> None:
30
+ self._weights: Dict[str, int] = {
31
+ "micro": 4,
32
+ "small": 2,
33
+ "large": 1,
34
+ "medium": 1,
35
+ "default": 1,
36
+ }
37
+ self._prioritize_immediate: bool = bool(prioritize_immediate)
38
+
39
+ def try_once(self, client, queues: Dict[str, str], order: list[str]) -> Optional[dict]:
40
+ # Immediate-first if enabled (non-blocking)
41
+ if self._prioritize_immediate:
42
+ try:
43
+ job = client.fetch_message(queues["immediate"], 0)
44
+ if job is not None:
45
+ return job
46
+ except TimeoutError:
47
+ pass
48
+ candidates = list(order)
49
+ weights = [self._weights[q] for q in candidates]
50
+ while candidates:
51
+ try:
52
+ chosen = random.choices(candidates, weights=weights, k=1)[0]
53
+ job = client.fetch_message(queues[chosen], 0)
54
+ if job is not None:
55
+ return job
56
+ except TimeoutError:
57
+ pass
58
+ finally:
59
+ idx = candidates.index(chosen)
60
+ del candidates[idx]
61
+ del weights[idx]
62
+ return None
63
+
64
+
65
+ class _SimpleStrategy(_SchedulingStrategy):
66
+ """
67
+ Simple strategy placeholder. Actual simple-mode handling is done in QosScheduler.fetch_next
68
+ to directly fetch from the base 'default' queue using the provided timeout.
69
+ """
70
+
71
+ def try_once(self, client, queues: Dict[str, str], order: list[str]) -> Optional[dict]:
72
+ # Block up to 30s on the base/default queue and return first available job
73
+ try:
74
+ return client.fetch_message(queues["default"], 30.0)
75
+ except TimeoutError:
76
+ return None
77
+
78
+
79
+ class _RoundRobinStrategy(_SchedulingStrategy):
80
+ """
81
+ Simple round-robin over non-immediate queues. Maintains rotation across calls.
82
+ """
83
+
84
+ def __init__(self, order: list[str], prioritize_immediate: bool = True) -> None:
85
+ self._order = list(order)
86
+ self._len = len(self._order)
87
+ self._idx = 0
88
+ self._prioritize_immediate: bool = bool(prioritize_immediate)
89
+
90
+ def try_once(self, client, queues: Dict[str, str], order: list[str]) -> Optional[dict]:
91
+ # Immediate-first if enabled (non-blocking)
92
+ if self._prioritize_immediate:
93
+ try:
94
+ job = client.fetch_message(queues["immediate"], 0)
95
+ if job is not None:
96
+ return job
97
+ except TimeoutError:
98
+ pass
99
+ start_idx = self._idx
100
+ for step in range(self._len):
101
+ i = (start_idx + step) % self._len
102
+ qname = self._order[i]
103
+ try:
104
+ job = client.fetch_message(queues[qname], 0)
105
+ if job is not None:
106
+ # advance rotation to the position after the chosen one
107
+ self._idx = (i + 1) % self._len
108
+ return job
109
+ except TimeoutError:
110
+ continue
111
+ return None
112
+
113
+
114
+ class _WeightedRoundRobinStrategy(_SchedulingStrategy):
115
+ """
116
+ Smooth Weighted Round Robin (SWRR) using weights micro=4, small=2, large=1, medium=1, default=1.
117
+ Maintains current weights across calls.
118
+ """
119
+
120
+ def __init__(self, prioritize_immediate: bool = True) -> None:
121
+ self._weights: Dict[str, int] = {
122
+ "micro": 4,
123
+ "small": 2,
124
+ "large": 1,
125
+ "medium": 1,
126
+ "default": 1,
127
+ }
128
+ self._current: Dict[str, int] = {k: 0 for k in self._weights.keys()}
129
+ self._total: int = sum(self._weights.values())
130
+ self._prioritize_immediate: bool = bool(prioritize_immediate)
131
+
132
+ def try_once(self, client, queues: Dict[str, str], order: list[str]) -> Optional[dict]:
133
+ # Immediate-first if enabled (non-blocking)
134
+ if self._prioritize_immediate:
135
+ try:
136
+ job = client.fetch_message(queues["immediate"], 0)
137
+ if job is not None:
138
+ return job
139
+ except TimeoutError:
140
+ pass
141
+ # Attempt up to len(order) selections per sweep, excluding queues that prove empty
142
+ active = list(order)
143
+ for _ in range(len(order)):
144
+ if not active:
145
+ break
146
+ for q in active:
147
+ self._current[q] += self._weights[q]
148
+ chosen = max(active, key=lambda q: self._current[q])
149
+ self._current[chosen] -= self._total
150
+ try:
151
+ job = client.fetch_message(queues[chosen], 0)
152
+ if job is not None:
153
+ return job
154
+ except TimeoutError:
155
+ job = None
156
+ # If no job available from chosen, exclude it for the remainder of this sweep
157
+ if job is None and chosen in active:
158
+ active.remove(chosen)
159
+ # Fallback: single non-blocking attempt for each queue in order
160
+ for q in order:
161
+ try:
162
+ job = client.fetch_message(queues[q], 0)
163
+ if job is not None:
164
+ return job
165
+ except TimeoutError:
166
+ continue
167
+ return None
168
+
169
+
170
+ class QosScheduler:
171
+ """
172
+ Simplified scheduler that fetches jobs from the default queue only.
173
+ Uses the provided timeout value when polling the broker.
174
+ """
175
+
176
+ def __init__(
177
+ self,
178
+ base_queue: str,
179
+ total_buffer_capacity: int = 1,
180
+ num_prefetch_threads: int = 0,
181
+ prefetch_poll_interval: float = 0.0,
182
+ prefetch_non_immediate: bool = False,
183
+ strategy: str = "lottery",
184
+ prioritize_immediate: bool = True,
185
+ ) -> None:
186
+ self.base_queue = base_queue
187
+
188
+ # Define all derived queues; default behavior still uses only "default"
189
+ self.queues: Dict[str, str] = {
190
+ "default": f"{base_queue}",
191
+ "immediate": f"{base_queue}_immediate",
192
+ "micro": f"{base_queue}_micro",
193
+ "small": f"{base_queue}_small",
194
+ "medium": f"{base_queue}_medium",
195
+ "large": f"{base_queue}_large",
196
+ }
197
+
198
+ # Priority order for multi-queue fetching; "immediate" always first
199
+ self._priority_order = [
200
+ "immediate",
201
+ "micro",
202
+ "small",
203
+ "medium",
204
+ "large",
205
+ "default",
206
+ ]
207
+
208
+ # Non-immediate queue order reference
209
+ self._non_immediate_order = ["micro", "small", "large", "medium", "default"]
210
+
211
+ # Logger
212
+ self._logger = logging.getLogger(__name__)
213
+
214
+ # No prefetching - just direct calls
215
+ self._total_buffer_capacity: int = int(total_buffer_capacity)
216
+ self._num_prefetch_threads: int = int(num_prefetch_threads)
217
+ self._prefetch_poll_interval: float = float(prefetch_poll_interval)
218
+ self._prefetch_non_immediate: bool = bool(prefetch_non_immediate)
219
+
220
+ # Strategy selection
221
+ self._simple_mode: bool = False
222
+ if strategy == "simple":
223
+ self._strategy_impl: _SchedulingStrategy = _SimpleStrategy()
224
+ self._simple_mode = True
225
+ elif strategy == "round_robin":
226
+ self._strategy_impl = _RoundRobinStrategy(self._non_immediate_order, prioritize_immediate)
227
+ elif strategy == "weighted_round_robin":
228
+ self._strategy_impl = _WeightedRoundRobinStrategy(prioritize_immediate)
229
+ else:
230
+ self._strategy_impl = _LotteryStrategy(prioritize_immediate)
231
+
232
+ # Context manager helpers for clean shutdown
233
+ def __enter__(self) -> "QosScheduler":
234
+ return self
235
+
236
+ def __exit__(self, exc_type, exc, tb) -> None:
237
+ self.close()
238
+
239
+ # ---------------------------- Public API ----------------------------
240
+ def close(self) -> None:
241
+ """
242
+ Cleanly close the scheduler. No-op for the current implementation
243
+ since we do not spin background threads.
244
+ """
245
+ return None
246
+
247
+ def fetch_next(self, client, timeout: float = 0.0) -> Optional[dict]:
248
+ """
249
+ Immediate-first, then strategy-based scheduling among non-immediate queues.
250
+
251
+ Behavior:
252
+ - Always check 'immediate' first (non-blocking). If present, return immediately.
253
+ - If not, select using the configured strategy (lottery, round_robin, weighted_round_robin).
254
+ - If no job is found in a full pass:
255
+ - If timeout <= 0: return None.
256
+ - Else: sleep in 0.5s increments and retry until accumulated elapsed time >= timeout.
257
+ """
258
+ # Simple mode: delegate to the strategy (blocks up to 30s on base queue)
259
+ if getattr(self, "_simple_mode", False):
260
+ return self._strategy_impl.try_once(client, self.queues, self._non_immediate_order)
261
+
262
+ start = time.monotonic()
263
+ while True:
264
+ # Strategy-based attempt (strategy may include immediate priority internally)
265
+ job = self._strategy_impl.try_once(client, self.queues, self._non_immediate_order)
266
+ if job is not None:
267
+ return job
268
+
269
+ # No job found in this sweep
270
+ if timeout <= 0:
271
+ return None
272
+
273
+ elapsed = time.monotonic() - start
274
+ if elapsed >= timeout:
275
+ return None
276
+
277
+ # Sleep up to 0.5s, but not beyond remaining timeout
278
+ remaining = timeout - elapsed
279
+ sleep_time = 0.5 if remaining > 0.5 else remaining
280
+ if sleep_time > 0:
281
+ time.sleep(sleep_time)
282
+ else:
283
+ return None
@@ -0,0 +1,9 @@
1
+ # SPDX-FileCopyrightText: Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES.
2
+ # All rights reserved.
3
+ # SPDX-License-Identifier: Apache-2.0
4
+
5
+ from .broker import SimpleMessageBroker
6
+ from .broker import ResponseSchema
7
+ from .simple_client import SimpleClient
8
+
9
+ __all__ = ["SimpleMessageBroker", "SimpleClient", "ResponseSchema"]