nv-ingest-api 2025.9.22.dev20250922__py3-none-any.whl → 2025.9.23.dev20250923__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.
- nv_ingest_api/internal/extract/image/chart_extractor.py +38 -35
- nv_ingest_api/internal/extract/image/image_helpers/common.py +0 -4
- nv_ingest_api/internal/extract/image/infographic_extractor.py +23 -25
- nv_ingest_api/internal/extract/image/table_extractor.py +43 -30
- nv_ingest_api/internal/extract/pdf/engines/pdfium.py +0 -7
- nv_ingest_api/internal/primitives/nim/__init__.py +2 -1
- nv_ingest_api/internal/primitives/nim/model_interface/helpers.py +8 -4
- nv_ingest_api/internal/primitives/nim/model_interface/ocr.py +402 -211
- nv_ingest_api/internal/primitives/nim/nim_client.py +271 -16
- nv_ingest_api/internal/primitives/nim/nim_model_interface.py +45 -0
- nv_ingest_api/util/nim/__init__.py +19 -6
- {nv_ingest_api-2025.9.22.dev20250922.dist-info → nv_ingest_api-2025.9.23.dev20250923.dist-info}/METADATA +1 -1
- {nv_ingest_api-2025.9.22.dev20250922.dist-info → nv_ingest_api-2025.9.23.dev20250923.dist-info}/RECORD +16 -16
- {nv_ingest_api-2025.9.22.dev20250922.dist-info → nv_ingest_api-2025.9.23.dev20250923.dist-info}/WHEEL +0 -0
- {nv_ingest_api-2025.9.22.dev20250922.dist-info → nv_ingest_api-2025.9.23.dev20250923.dist-info}/licenses/LICENSE +0 -0
- {nv_ingest_api-2025.9.22.dev20250922.dist-info → nv_ingest_api-2025.9.23.dev20250923.dist-info}/top_level.txt +0 -0
|
@@ -2,10 +2,14 @@
|
|
|
2
2
|
# All rights reserved.
|
|
3
3
|
# SPDX-License-Identifier: Apache-2.0
|
|
4
4
|
|
|
5
|
+
import hashlib
|
|
6
|
+
import json
|
|
5
7
|
import logging
|
|
6
8
|
import threading
|
|
7
9
|
import time
|
|
8
|
-
|
|
10
|
+
import queue
|
|
11
|
+
from collections import namedtuple
|
|
12
|
+
from concurrent.futures import Future, ThreadPoolExecutor, as_completed
|
|
9
13
|
from typing import Any
|
|
10
14
|
from typing import Optional
|
|
11
15
|
from typing import Tuple, Union
|
|
@@ -17,8 +21,12 @@ import tritonclient.grpc as grpcclient
|
|
|
17
21
|
from nv_ingest_api.internal.primitives.tracing.tagging import traceable_func
|
|
18
22
|
from nv_ingest_api.util.string_processing import generate_url
|
|
19
23
|
|
|
24
|
+
|
|
20
25
|
logger = logging.getLogger(__name__)
|
|
21
26
|
|
|
27
|
+
# A simple structure to hold a request's data and its Future for the result
|
|
28
|
+
InferenceRequest = namedtuple("InferenceRequest", ["data", "future", "model_name", "dims", "kwargs"])
|
|
29
|
+
|
|
22
30
|
|
|
23
31
|
class NimClient:
|
|
24
32
|
"""
|
|
@@ -34,6 +42,9 @@ class NimClient:
|
|
|
34
42
|
timeout: float = 120.0,
|
|
35
43
|
max_retries: int = 5,
|
|
36
44
|
max_429_retries: int = 5,
|
|
45
|
+
enable_dynamic_batching: bool = False,
|
|
46
|
+
dynamic_batch_timeout: float = 0.1, # 100 milliseconds
|
|
47
|
+
dynamic_batch_memory_budget_mb: Optional[float] = None,
|
|
37
48
|
):
|
|
38
49
|
"""
|
|
39
50
|
Initialize the NimClient with the specified model interface, protocol, and server endpoints.
|
|
@@ -60,7 +71,6 @@ class NimClient:
|
|
|
60
71
|
ValueError
|
|
61
72
|
If an invalid protocol is specified or if required endpoints are missing.
|
|
62
73
|
"""
|
|
63
|
-
|
|
64
74
|
self.client = None
|
|
65
75
|
self.model_interface = model_interface
|
|
66
76
|
self.protocol = protocol.lower()
|
|
@@ -88,12 +98,32 @@ class NimClient:
|
|
|
88
98
|
else:
|
|
89
99
|
raise ValueError("Invalid protocol specified. Must be 'grpc' or 'http'.")
|
|
90
100
|
|
|
101
|
+
self.dynamic_batching_enabled = enable_dynamic_batching
|
|
102
|
+
if self.dynamic_batching_enabled:
|
|
103
|
+
self._batch_timeout = dynamic_batch_timeout
|
|
104
|
+
if dynamic_batch_memory_budget_mb is not None:
|
|
105
|
+
self._batch_memory_budget_bytes = dynamic_batch_memory_budget_mb * 1024 * 1024
|
|
106
|
+
else:
|
|
107
|
+
self._batch_memory_budget_bytes = None
|
|
108
|
+
|
|
109
|
+
self._request_queue = queue.Queue()
|
|
110
|
+
self._stop_event = threading.Event()
|
|
111
|
+
self._batcher_thread = threading.Thread(target=self._batcher_loop, daemon=True)
|
|
112
|
+
|
|
113
|
+
def start(self):
|
|
114
|
+
"""Starts the dynamic batching worker thread if enabled."""
|
|
115
|
+
if self.dynamic_batching_enabled and not self._batcher_thread.is_alive():
|
|
116
|
+
self._batcher_thread.start()
|
|
117
|
+
|
|
91
118
|
def _fetch_max_batch_size(self, model_name, model_version: str = "") -> int:
|
|
92
119
|
"""Fetch the maximum batch size from the Triton model configuration in a thread-safe manner."""
|
|
93
120
|
|
|
94
121
|
if model_name == "yolox_ensemble":
|
|
95
122
|
model_name = "yolox"
|
|
96
123
|
|
|
124
|
+
if model_name == "scene_text_ensemble":
|
|
125
|
+
model_name = "scene_text_pre"
|
|
126
|
+
|
|
97
127
|
if model_name in self._max_batch_sizes:
|
|
98
128
|
return self._max_batch_sizes[model_name]
|
|
99
129
|
|
|
@@ -102,13 +132,12 @@ class NimClient:
|
|
|
102
132
|
if model_name in self._max_batch_sizes:
|
|
103
133
|
return self._max_batch_sizes[model_name]
|
|
104
134
|
|
|
105
|
-
if not self._grpc_endpoint:
|
|
135
|
+
if not self._grpc_endpoint or not self.client:
|
|
106
136
|
self._max_batch_sizes[model_name] = 1
|
|
107
137
|
return 1
|
|
108
138
|
|
|
109
139
|
try:
|
|
110
|
-
|
|
111
|
-
model_config = client.get_model_config(model_name=model_name, model_version=model_version)
|
|
140
|
+
model_config = self.client.get_model_config(model_name=model_name, model_version=model_version)
|
|
112
141
|
self._max_batch_sizes[model_name] = model_config.config.max_batch_size
|
|
113
142
|
logger.debug(f"Max batch size for model '{model_name}': {self._max_batch_sizes[model_name]}")
|
|
114
143
|
except Exception as e:
|
|
@@ -176,17 +205,40 @@ class NimClient:
|
|
|
176
205
|
Any
|
|
177
206
|
The processed inference results, coalesced in the same order as the input images.
|
|
178
207
|
"""
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
208
|
+
# 1. Retrieve or default to the model's maximum batch size.
|
|
209
|
+
batch_size = self._fetch_max_batch_size(model_name)
|
|
210
|
+
max_requested_batch_size = kwargs.pop("max_batch_size", batch_size)
|
|
211
|
+
force_requested_batch_size = kwargs.pop("force_max_batch_size", False)
|
|
212
|
+
max_batch_size = (
|
|
213
|
+
max(1, min(batch_size, max_requested_batch_size))
|
|
214
|
+
if not force_requested_batch_size
|
|
215
|
+
else max_requested_batch_size
|
|
216
|
+
)
|
|
217
|
+
self._batch_size = max_batch_size
|
|
218
|
+
|
|
219
|
+
if self.dynamic_batching_enabled:
|
|
220
|
+
# DYNAMIC BATCHING PATH
|
|
221
|
+
try:
|
|
222
|
+
data = self.model_interface.prepare_data_for_inference(data)
|
|
223
|
+
|
|
224
|
+
futures = []
|
|
225
|
+
for base64_image, image_array in zip(data["base64_images"], data["images"]):
|
|
226
|
+
dims = image_array.shape[:2]
|
|
227
|
+
futures.append(self.submit(base64_image, model_name, dims, **kwargs))
|
|
228
|
+
|
|
229
|
+
results = [future.result() for future in futures]
|
|
230
|
+
|
|
231
|
+
return results
|
|
232
|
+
|
|
233
|
+
except Exception as err:
|
|
234
|
+
error_str = (
|
|
235
|
+
f"Error during synchronous infer with dynamic batching [{self.model_interface.name()}]: {err}"
|
|
236
|
+
)
|
|
237
|
+
logger.error(error_str)
|
|
238
|
+
raise RuntimeError(error_str) from err
|
|
189
239
|
|
|
240
|
+
# OFFLINE BATCHING PATH
|
|
241
|
+
try:
|
|
190
242
|
# 2. Prepare data for inference.
|
|
191
243
|
data = self.model_interface.prepare_data_for_inference(data)
|
|
192
244
|
|
|
@@ -390,6 +442,209 @@ class NimClient:
|
|
|
390
442
|
logger.error(f"Failed to get a successful response after {self.max_retries} retries.")
|
|
391
443
|
raise Exception(f"Failed to get a successful response after {self.max_retries} retries.")
|
|
392
444
|
|
|
445
|
+
def _batcher_loop(self):
|
|
446
|
+
"""The main loop for the background thread to form and process batches."""
|
|
447
|
+
while not self._stop_event.is_set():
|
|
448
|
+
requests_batch = []
|
|
449
|
+
try:
|
|
450
|
+
first_req = self._request_queue.get(timeout=self._batch_timeout)
|
|
451
|
+
if first_req is None:
|
|
452
|
+
continue
|
|
453
|
+
requests_batch.append(first_req)
|
|
454
|
+
|
|
455
|
+
start_time = time.monotonic()
|
|
456
|
+
|
|
457
|
+
while len(requests_batch) < self._batch_size:
|
|
458
|
+
if (time.monotonic() - start_time) >= self._batch_timeout:
|
|
459
|
+
break
|
|
460
|
+
|
|
461
|
+
if self._request_queue.empty():
|
|
462
|
+
break
|
|
463
|
+
|
|
464
|
+
next_req_peek = self._request_queue.queue[0]
|
|
465
|
+
if next_req_peek is None:
|
|
466
|
+
break
|
|
467
|
+
|
|
468
|
+
if self._batch_memory_budget_bytes:
|
|
469
|
+
if not self.model_interface.does_item_fit_in_batch(
|
|
470
|
+
requests_batch,
|
|
471
|
+
next_req_peek,
|
|
472
|
+
self._batch_memory_budget_bytes,
|
|
473
|
+
):
|
|
474
|
+
break
|
|
475
|
+
|
|
476
|
+
try:
|
|
477
|
+
next_req = self._request_queue.get_nowait()
|
|
478
|
+
if next_req is None:
|
|
479
|
+
break
|
|
480
|
+
requests_batch.append(next_req)
|
|
481
|
+
except queue.Empty:
|
|
482
|
+
break
|
|
483
|
+
|
|
484
|
+
except queue.Empty:
|
|
485
|
+
continue
|
|
486
|
+
|
|
487
|
+
if requests_batch:
|
|
488
|
+
self._process_dynamic_batch(requests_batch)
|
|
489
|
+
|
|
490
|
+
def _process_dynamic_batch(self, requests: list[InferenceRequest]):
|
|
491
|
+
"""Coalesces, infers, and distributes results for a dynamic batch."""
|
|
492
|
+
if not requests:
|
|
493
|
+
return
|
|
494
|
+
|
|
495
|
+
first_req = requests[0]
|
|
496
|
+
model_name = first_req.model_name
|
|
497
|
+
kwargs = first_req.kwargs
|
|
498
|
+
|
|
499
|
+
try:
|
|
500
|
+
# 1. Coalesce individual data items into a single batch input
|
|
501
|
+
batch_input, batch_data = self.model_interface.coalesce_requests_to_batch(
|
|
502
|
+
[req.data for req in requests],
|
|
503
|
+
[req.dims for req in requests],
|
|
504
|
+
protocol=self.protocol,
|
|
505
|
+
model_name=model_name,
|
|
506
|
+
**kwargs,
|
|
507
|
+
)
|
|
508
|
+
|
|
509
|
+
# 2. Perform inference using the existing _process_batch logic
|
|
510
|
+
parsed_output, _ = self._process_batch(batch_input, batch_data=batch_data, model_name=model_name, **kwargs)
|
|
511
|
+
|
|
512
|
+
# 3. Process the batched output to get final results
|
|
513
|
+
all_results = self.model_interface.process_inference_results(
|
|
514
|
+
parsed_output,
|
|
515
|
+
original_image_shapes=batch_data.get("original_image_shapes"),
|
|
516
|
+
protocol=self.protocol,
|
|
517
|
+
**kwargs,
|
|
518
|
+
)
|
|
519
|
+
|
|
520
|
+
# 4. Distribute the individual results back to the correct Future
|
|
521
|
+
if len(all_results) != len(requests):
|
|
522
|
+
raise ValueError("Mismatch between result count and request count.")
|
|
523
|
+
|
|
524
|
+
for i, req in enumerate(requests):
|
|
525
|
+
req.future.set_result(all_results[i])
|
|
526
|
+
|
|
527
|
+
except Exception as e:
|
|
528
|
+
# If anything fails, propagate the exception to all futures in the batch
|
|
529
|
+
logger.error(f"Error processing dynamic batch: {e}")
|
|
530
|
+
for req in requests:
|
|
531
|
+
req.future.set_exception(e)
|
|
532
|
+
|
|
533
|
+
def submit(self, data: Any, model_name: str, dims: Tuple[int, int], **kwargs) -> Future:
|
|
534
|
+
"""
|
|
535
|
+
Submits a single inference request to the dynamic batcher.
|
|
536
|
+
|
|
537
|
+
This method is non-blocking and returns a Future object that will
|
|
538
|
+
eventually contain the inference result.
|
|
539
|
+
|
|
540
|
+
Parameters
|
|
541
|
+
----------
|
|
542
|
+
data : Any
|
|
543
|
+
The single data item for inference (e.g., one image, one text prompt).
|
|
544
|
+
|
|
545
|
+
Returns
|
|
546
|
+
-------
|
|
547
|
+
concurrent.futures.Future
|
|
548
|
+
A future that will be fulfilled with the inference result.
|
|
549
|
+
"""
|
|
550
|
+
if not self.dynamic_batching_enabled:
|
|
551
|
+
raise RuntimeError(
|
|
552
|
+
"Dynamic batching is not enabled. Please initialize NimClient with " "enable_dynamic_batching=True."
|
|
553
|
+
)
|
|
554
|
+
|
|
555
|
+
future = Future()
|
|
556
|
+
request = InferenceRequest(data=data, future=future, model_name=model_name, dims=dims, kwargs=kwargs)
|
|
557
|
+
self._request_queue.put(request)
|
|
558
|
+
return future
|
|
559
|
+
|
|
393
560
|
def close(self):
|
|
394
|
-
|
|
561
|
+
"""Stops the dynamic batching worker and closes client connections."""
|
|
562
|
+
|
|
563
|
+
if self.dynamic_batching_enabled:
|
|
564
|
+
self._stop_event.set()
|
|
565
|
+
# Unblock the queue in case the thread is waiting on get()
|
|
566
|
+
self._request_queue.put(None)
|
|
567
|
+
if self._batcher_thread.is_alive():
|
|
568
|
+
self._batcher_thread.join()
|
|
569
|
+
|
|
570
|
+
if self.client:
|
|
395
571
|
self.client.close()
|
|
572
|
+
|
|
573
|
+
|
|
574
|
+
class NimClientManager:
|
|
575
|
+
"""
|
|
576
|
+
A thread-safe, singleton manager for creating and sharing NimClient instances.
|
|
577
|
+
|
|
578
|
+
This manager ensures that only one NimClient is created per unique configuration.
|
|
579
|
+
"""
|
|
580
|
+
|
|
581
|
+
_instance = None
|
|
582
|
+
_lock = threading.Lock()
|
|
583
|
+
|
|
584
|
+
def __new__(cls):
|
|
585
|
+
# Singleton pattern
|
|
586
|
+
if cls._instance is None:
|
|
587
|
+
with cls._lock:
|
|
588
|
+
if cls._instance is None:
|
|
589
|
+
cls._instance = super(NimClientManager, cls).__new__(cls)
|
|
590
|
+
return cls._instance
|
|
591
|
+
|
|
592
|
+
def __init__(self):
|
|
593
|
+
if not hasattr(self, "_initialized"):
|
|
594
|
+
with self._lock:
|
|
595
|
+
if not hasattr(self, "_initialized"):
|
|
596
|
+
self._clients = {} # Key: config_hash, Value: NimClient instance
|
|
597
|
+
self._client_lock = threading.Lock()
|
|
598
|
+
self._initialized = True
|
|
599
|
+
|
|
600
|
+
def _generate_config_key(self, **kwargs) -> str:
|
|
601
|
+
"""Creates a stable, hashable key from client configuration."""
|
|
602
|
+
sorted_config = sorted(kwargs.items())
|
|
603
|
+
config_str = json.dumps(sorted_config)
|
|
604
|
+
return hashlib.md5(config_str.encode("utf-8")).hexdigest()
|
|
605
|
+
|
|
606
|
+
def get_client(self, model_interface, **kwargs) -> "NimClient":
|
|
607
|
+
"""
|
|
608
|
+
Gets or creates a NimClient for the given configuration.
|
|
609
|
+
"""
|
|
610
|
+
config_key = self._generate_config_key(model_interface_name=model_interface.name(), **kwargs)
|
|
611
|
+
|
|
612
|
+
if config_key in self._clients:
|
|
613
|
+
return self._clients[config_key]
|
|
614
|
+
|
|
615
|
+
with self._client_lock:
|
|
616
|
+
if config_key in self._clients:
|
|
617
|
+
return self._clients[config_key]
|
|
618
|
+
|
|
619
|
+
logger.debug(f"Creating new NimClient for config hash: {config_key}")
|
|
620
|
+
|
|
621
|
+
new_client = NimClient(model_interface=model_interface, **kwargs)
|
|
622
|
+
|
|
623
|
+
if new_client.dynamic_batching_enabled:
|
|
624
|
+
new_client.start()
|
|
625
|
+
|
|
626
|
+
self._clients[config_key] = new_client
|
|
627
|
+
|
|
628
|
+
return new_client
|
|
629
|
+
|
|
630
|
+
def shutdown(self):
|
|
631
|
+
"""
|
|
632
|
+
Gracefully closes all managed NimClient instances.
|
|
633
|
+
This is called automatically on application exit by `atexit`.
|
|
634
|
+
"""
|
|
635
|
+
logger.debug(f"Shutting down NimClientManager and {len(self._clients)} client(s)...")
|
|
636
|
+
with self._client_lock:
|
|
637
|
+
for config_key, client in self._clients.items():
|
|
638
|
+
logger.debug(f"Closing client for config: {config_key}")
|
|
639
|
+
try:
|
|
640
|
+
client.close()
|
|
641
|
+
except Exception as e:
|
|
642
|
+
logger.error(f"Error closing client for config {config_key}: {e}")
|
|
643
|
+
self._clients.clear()
|
|
644
|
+
logger.debug("NimClientManager shutdown complete.")
|
|
645
|
+
|
|
646
|
+
|
|
647
|
+
# A global helper function to make access even easier
|
|
648
|
+
def get_nim_client_manager(*args, **kwargs) -> NimClientManager:
|
|
649
|
+
"""Returns the singleton instance of the NimClientManager."""
|
|
650
|
+
return NimClientManager(*args, **kwargs)
|
|
@@ -3,7 +3,10 @@
|
|
|
3
3
|
# SPDX-License-Identifier: Apache-2.0
|
|
4
4
|
|
|
5
5
|
import logging
|
|
6
|
+
from typing import Any
|
|
7
|
+
from typing import Dict
|
|
6
8
|
from typing import Optional
|
|
9
|
+
from typing import Tuple
|
|
7
10
|
|
|
8
11
|
|
|
9
12
|
logger = logging.getLogger(__name__)
|
|
@@ -79,3 +82,45 @@ class ModelInterface:
|
|
|
79
82
|
The name of the model interface.
|
|
80
83
|
"""
|
|
81
84
|
raise NotImplementedError("Subclasses should implement this method")
|
|
85
|
+
|
|
86
|
+
def coalesce_requests_to_batch(self, requests, protocol: str, **kwargs) -> Tuple[Any, Dict[str, Any]]:
|
|
87
|
+
"""
|
|
88
|
+
Takes a list of InferenceRequest objects and combines them into a single
|
|
89
|
+
formatted batch ready for inference.
|
|
90
|
+
|
|
91
|
+
THIS METHOD IS REQUIRED FOR DYNAMIC BATCHING SUPPORT.
|
|
92
|
+
|
|
93
|
+
Parameters
|
|
94
|
+
----------
|
|
95
|
+
requests : List[InferenceRequest]
|
|
96
|
+
A list of InferenceRequest namedtuples collected for the batch.
|
|
97
|
+
Each tuple contains the data, dimensions, and other context for a single item.
|
|
98
|
+
protocol : str
|
|
99
|
+
The inference protocol, either "grpc" or "http".
|
|
100
|
+
**kwargs : Any
|
|
101
|
+
Additional keyword arguments passed from the original request.
|
|
102
|
+
|
|
103
|
+
Returns
|
|
104
|
+
-------
|
|
105
|
+
Tuple[Any, Dict[str, Any]]
|
|
106
|
+
A tuple containing the single formatted batch and its scratch-pad data.
|
|
107
|
+
"""
|
|
108
|
+
raise NotImplementedError(
|
|
109
|
+
f"{self.__class__.__name__} does not support dynamic batching "
|
|
110
|
+
"because `coalesce_requests_to_batch` is not implemented."
|
|
111
|
+
)
|
|
112
|
+
|
|
113
|
+
def does_item_fit_in_batch(self, current_batch, next_request, memory_budget_bytes: int) -> bool:
|
|
114
|
+
"""
|
|
115
|
+
Checks if adding another request to the current batch would exceed the memory budget.
|
|
116
|
+
|
|
117
|
+
This is a model-specific calculation. The default implementation always
|
|
118
|
+
returns True, effectively ignoring the memory budget. Interfaces for models
|
|
119
|
+
that require memory management (like padded image models) must override this.
|
|
120
|
+
|
|
121
|
+
Returns
|
|
122
|
+
-------
|
|
123
|
+
bool
|
|
124
|
+
True if the item fits within the budget, False otherwise.
|
|
125
|
+
"""
|
|
126
|
+
return True
|
|
@@ -4,7 +4,8 @@
|
|
|
4
4
|
|
|
5
5
|
from typing import Tuple, Optional
|
|
6
6
|
|
|
7
|
-
from nv_ingest_api.internal.primitives.nim.nim_client import
|
|
7
|
+
from nv_ingest_api.internal.primitives.nim.nim_client import NimClientManager
|
|
8
|
+
from nv_ingest_api.internal.primitives.nim.nim_client import get_nim_client_manager
|
|
8
9
|
from nv_ingest_api.internal.primitives.nim.nim_model_interface import ModelInterface
|
|
9
10
|
|
|
10
11
|
__all__ = ["create_inference_client"]
|
|
@@ -17,9 +18,10 @@ def create_inference_client(
|
|
|
17
18
|
infer_protocol: Optional[str] = None,
|
|
18
19
|
timeout: float = 120.0,
|
|
19
20
|
max_retries: int = 5,
|
|
20
|
-
|
|
21
|
+
**kwargs,
|
|
22
|
+
) -> NimClientManager:
|
|
21
23
|
"""
|
|
22
|
-
Create a
|
|
24
|
+
Create a NimClientManager for interfacing with a model inference server.
|
|
23
25
|
|
|
24
26
|
Parameters
|
|
25
27
|
----------
|
|
@@ -34,8 +36,8 @@ def create_inference_client(
|
|
|
34
36
|
|
|
35
37
|
Returns
|
|
36
38
|
-------
|
|
37
|
-
|
|
38
|
-
The initialized
|
|
39
|
+
NimClientManager
|
|
40
|
+
The initialized NimClientManager.
|
|
39
41
|
|
|
40
42
|
Raises
|
|
41
43
|
------
|
|
@@ -53,4 +55,15 @@ def create_inference_client(
|
|
|
53
55
|
if infer_protocol not in ["grpc", "http"]:
|
|
54
56
|
raise ValueError("Invalid infer_protocol specified. Must be 'grpc' or 'http'.")
|
|
55
57
|
|
|
56
|
-
|
|
58
|
+
manager = get_nim_client_manager()
|
|
59
|
+
client = manager.get_client(
|
|
60
|
+
model_interface=model_interface,
|
|
61
|
+
protocol=infer_protocol,
|
|
62
|
+
endpoints=endpoints,
|
|
63
|
+
auth_token=auth_token,
|
|
64
|
+
timeout=timeout,
|
|
65
|
+
max_retries=max_retries,
|
|
66
|
+
**kwargs,
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
return client
|
|
@@ -20,19 +20,19 @@ nv_ingest_api/internal/extract/docx/engines/docxreader_helpers/docxreader.py,sha
|
|
|
20
20
|
nv_ingest_api/internal/extract/html/__init__.py,sha256=wQSlVx3T14ZgQAt-EPzEczQusXVW0W8yynnUaFFGE3s,143
|
|
21
21
|
nv_ingest_api/internal/extract/html/html_extractor.py,sha256=I9oWfj6_As4898GDDh0zsSuKxO3lBsvyYzhvUotjzJI,3282
|
|
22
22
|
nv_ingest_api/internal/extract/image/__init__.py,sha256=wQSlVx3T14ZgQAt-EPzEczQusXVW0W8yynnUaFFGE3s,143
|
|
23
|
-
nv_ingest_api/internal/extract/image/chart_extractor.py,sha256=
|
|
23
|
+
nv_ingest_api/internal/extract/image/chart_extractor.py,sha256=Pojiu7R1BT8tUUzD5DsF-dDEwakz1ZfUrL_agalUsNc,13591
|
|
24
24
|
nv_ingest_api/internal/extract/image/image_extractor.py,sha256=gBKjlx28hA_e-dupatu46YQgOHJ0DLpAWxREiLaZLyo,9039
|
|
25
|
-
nv_ingest_api/internal/extract/image/infographic_extractor.py,sha256=
|
|
26
|
-
nv_ingest_api/internal/extract/image/table_extractor.py,sha256=
|
|
25
|
+
nv_ingest_api/internal/extract/image/infographic_extractor.py,sha256=gP-WiBIHruDmNFchq4BbVAci3XStMtyeN99M8dLm1j4,10225
|
|
26
|
+
nv_ingest_api/internal/extract/image/table_extractor.py,sha256=T80-Smkf54Y5OkSaOquXpcoLbAf5uMnV-LOsBgD0L7E,14440
|
|
27
27
|
nv_ingest_api/internal/extract/image/image_helpers/__init__.py,sha256=wQSlVx3T14ZgQAt-EPzEczQusXVW0W8yynnUaFFGE3s,143
|
|
28
|
-
nv_ingest_api/internal/extract/image/image_helpers/common.py,sha256=
|
|
28
|
+
nv_ingest_api/internal/extract/image/image_helpers/common.py,sha256=VhqjsBqvUz-2y92t6iryVERTuRfcGUdTHOOScYr8GLo,14916
|
|
29
29
|
nv_ingest_api/internal/extract/pdf/__init__.py,sha256=wQSlVx3T14ZgQAt-EPzEczQusXVW0W8yynnUaFFGE3s,143
|
|
30
30
|
nv_ingest_api/internal/extract/pdf/pdf_extractor.py,sha256=CxtWaD6mql9MEqSdk2CfSQ9T-Bn87beBkCOuGGjxGt8,2934
|
|
31
31
|
nv_ingest_api/internal/extract/pdf/engines/__init__.py,sha256=u4GnAZmDKRl0RwYGIRiozIRw70Kybw3A72-lcKFeoTI,582
|
|
32
32
|
nv_ingest_api/internal/extract/pdf/engines/adobe.py,sha256=VT0dEqkU-y2uGkaCqxtKYov_Q8R1028UQVBchgMLca4,17466
|
|
33
33
|
nv_ingest_api/internal/extract/pdf/engines/llama.py,sha256=MwzM-n2tu0FHM0wDe_0mONLlzHrPte7EOTuPtzCh7Zs,8384
|
|
34
34
|
nv_ingest_api/internal/extract/pdf/engines/nemoretriever.py,sha256=IVbNcH_phMiRSxnkZ04pGfQrPJ-x1zVR3hXyhxv7juc,22977
|
|
35
|
-
nv_ingest_api/internal/extract/pdf/engines/pdfium.py,sha256=
|
|
35
|
+
nv_ingest_api/internal/extract/pdf/engines/pdfium.py,sha256=CCfxcHAS3mED8zD6GKTGNUi02CzBMs7FsSopevhsiyk,22720
|
|
36
36
|
nv_ingest_api/internal/extract/pdf/engines/tika.py,sha256=6GyR2l6EsgNZl9jnYDXLeKNK9Fj2Mw9y2UWDq-eSkOc,3169
|
|
37
37
|
nv_ingest_api/internal/extract/pdf/engines/unstructured_io.py,sha256=jrv2B4VZAH4PevAQrFz965qz8UyXq3rViiOTbGLejec,14908
|
|
38
38
|
nv_ingest_api/internal/extract/pdf/engines/pdf_helpers/__init__.py,sha256=uTPTUTWQsGM1oeTUo49_hzwC5Yy9iEokrnS3z3WvtIo,5988
|
|
@@ -48,17 +48,17 @@ nv_ingest_api/internal/mutate/filter.py,sha256=H-hOTBVP-zLpvQr-FoGIJKxkhtj4l_sZ9
|
|
|
48
48
|
nv_ingest_api/internal/primitives/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
49
49
|
nv_ingest_api/internal/primitives/control_message_task.py,sha256=nWVB3QsP6p8BKwHThd-SNv_zwJAEA1mKCRharuju1mc,439
|
|
50
50
|
nv_ingest_api/internal/primitives/ingest_control_message.py,sha256=8rA0UbPDSB3avReAKNxiUa_FCy7fIQpqk6tfmcYUibA,9879
|
|
51
|
-
nv_ingest_api/internal/primitives/nim/__init__.py,sha256
|
|
51
|
+
nv_ingest_api/internal/primitives/nim/__init__.py,sha256=-dFBTHQnMKV0yc5tfSqIT-rkJXKtpcmyUfTPs8TJAi8,339
|
|
52
52
|
nv_ingest_api/internal/primitives/nim/default_values.py,sha256=W92XjfyeC6uuVxut6J7p00x1kpNsnXIDb97gSVytZJk,380
|
|
53
|
-
nv_ingest_api/internal/primitives/nim/nim_client.py,sha256=
|
|
54
|
-
nv_ingest_api/internal/primitives/nim/nim_model_interface.py,sha256=
|
|
53
|
+
nv_ingest_api/internal/primitives/nim/nim_client.py,sha256=kQAHWwZ6kjTVYZSfa0qRyIOFcqrhMe8LUygGtgzAly0,26321
|
|
54
|
+
nv_ingest_api/internal/primitives/nim/nim_model_interface.py,sha256=gWhyR33mIgEOYirq53WOk1bRl1SL0C_SVrM4w1-JmKU,4166
|
|
55
55
|
nv_ingest_api/internal/primitives/nim/model_interface/__init__.py,sha256=wQSlVx3T14ZgQAt-EPzEczQusXVW0W8yynnUaFFGE3s,143
|
|
56
56
|
nv_ingest_api/internal/primitives/nim/model_interface/cached.py,sha256=b1HX-PY1ExW5V6pXC1ZiHdobeG_BmbPr3rBbVJef13s,11003
|
|
57
57
|
nv_ingest_api/internal/primitives/nim/model_interface/decorators.py,sha256=qwubkHs4WjnexM6rI0wkjWCsrVNEbA4Wjk2oKL9OYCU,1499
|
|
58
58
|
nv_ingest_api/internal/primitives/nim/model_interface/deplot.py,sha256=TvKdk6PTuI1WNhRmNNrvygaI_DIutkJkDL-XdtLZQac,10787
|
|
59
|
-
nv_ingest_api/internal/primitives/nim/model_interface/helpers.py,sha256=
|
|
59
|
+
nv_ingest_api/internal/primitives/nim/model_interface/helpers.py,sha256=jqbEbavvr9giODpzsGQSRDu5yZ4YfNfKAQfqUm9yUDI,11698
|
|
60
60
|
nv_ingest_api/internal/primitives/nim/model_interface/nemoretriever_parse.py,sha256=WysjDZeegclO3mZgVcGOwzWbr8wSI4pWRiYD4iC2EXo,7098
|
|
61
|
-
nv_ingest_api/internal/primitives/nim/model_interface/ocr.py,sha256=
|
|
61
|
+
nv_ingest_api/internal/primitives/nim/model_interface/ocr.py,sha256=QOjKEJaL7Z_aT-luyV4eJSNQX4o-a9-P0CB0ZwSxFk4,29282
|
|
62
62
|
nv_ingest_api/internal/primitives/nim/model_interface/parakeet.py,sha256=5PqD2JuHY2rwd-6SSB4axr2Dd79vm95sAEkcmI3U7ME,12977
|
|
63
63
|
nv_ingest_api/internal/primitives/nim/model_interface/text_embedding.py,sha256=lFhppNqrq5X_fzbCWKphvZQMzaJd3gHrkWsyJORzFrU,5010
|
|
64
64
|
nv_ingest_api/internal/primitives/nim/model_interface/vlm.py,sha256=qJ382PU1ZrIM-SR3cqIhtY_W2rmHec2HIa2aUB2SvaU,6031
|
|
@@ -145,7 +145,7 @@ nv_ingest_api/util/metadata/__init__.py,sha256=HIHfzSig66GT0Uk8qsGBm_f13fKYcPtIt
|
|
|
145
145
|
nv_ingest_api/util/metadata/aggregators.py,sha256=YYdvJ1E04eGFZKKHUxXoH6mzLg8nor9Smvnv0qzqK5w,15988
|
|
146
146
|
nv_ingest_api/util/multi_processing/__init__.py,sha256=4fojP8Rp_5Hu1YAkqGylqTyEZ-HBVVEunn5Z9I99swA,242
|
|
147
147
|
nv_ingest_api/util/multi_processing/mp_pool_singleton.py,sha256=dTfP82DgGPaXEJH3jywTO8rNlLZUniD4FFzwv84_giE,7372
|
|
148
|
-
nv_ingest_api/util/nim/__init__.py,sha256=
|
|
148
|
+
nv_ingest_api/util/nim/__init__.py,sha256=No45pMstom1Jo0EENT6VEFkZn3YmTha7lYaBZU7xtHk,2116
|
|
149
149
|
nv_ingest_api/util/pdf/__init__.py,sha256=uLsBITo_XfgbwpzqXUm1IYX6XlZrTfx6T1cIhdILwG8,140
|
|
150
150
|
nv_ingest_api/util/pdf/pdfium.py,sha256=1aPCnPKXHWnncYoMO8HllYjrhODSXIeRBIsSLDevpYs,15667
|
|
151
151
|
nv_ingest_api/util/schema/__init__.py,sha256=wQSlVx3T14ZgQAt-EPzEczQusXVW0W8yynnUaFFGE3s,143
|
|
@@ -162,10 +162,10 @@ nv_ingest_api/util/string_processing/configuration.py,sha256=2HS08msccuPCT0fn_jf
|
|
|
162
162
|
nv_ingest_api/util/string_processing/yaml.py,sha256=6SW2O6wbXRhGbhETMbtXjYCZn53HeCNOP6a96AaxlHs,1454
|
|
163
163
|
nv_ingest_api/util/system/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
164
164
|
nv_ingest_api/util/system/hardware_info.py,sha256=1UFM8XE6M3pgQcpbVsCsqDQ7Dj-zzptL-XRE-DEu9UA,27213
|
|
165
|
-
nv_ingest_api-2025.9.
|
|
165
|
+
nv_ingest_api-2025.9.23.dev20250923.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
|
|
166
166
|
udfs/__init__.py,sha256=pXFqPgXIUqHDfj7SAR1Q19tt8KwGv_iMvhHyziz4AYM,205
|
|
167
167
|
udfs/llm_summarizer_udf.py,sha256=sIMfcH4GRyciTKUtq4dmhd6fZmAp07X32irIC4k7nEI,7316
|
|
168
|
-
nv_ingest_api-2025.9.
|
|
169
|
-
nv_ingest_api-2025.9.
|
|
170
|
-
nv_ingest_api-2025.9.
|
|
171
|
-
nv_ingest_api-2025.9.
|
|
168
|
+
nv_ingest_api-2025.9.23.dev20250923.dist-info/METADATA,sha256=GxHkBLbWifHcUO7FVUS2urfBqa9eo_2ClRT3o61Yc-U,13947
|
|
169
|
+
nv_ingest_api-2025.9.23.dev20250923.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
170
|
+
nv_ingest_api-2025.9.23.dev20250923.dist-info/top_level.txt,sha256=I1lseG9FF0CH93SPx4kFblsxFuv190cfzaas_CLNIiw,19
|
|
171
|
+
nv_ingest_api-2025.9.23.dev20250923.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|