nv-ingest-api 2025.4.17.dev20250417__py3-none-any.whl → 2025.4.19.dev20250419__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/__init__.py +0 -3
- nv_ingest_api/{internal/primitives → primitives}/control_message_task.py +0 -4
- nv_ingest_api/{internal/primitives → primitives}/ingest_control_message.py +2 -5
- {nv_ingest_api-2025.4.17.dev20250417.dist-info → nv_ingest_api-2025.4.19.dev20250419.dist-info}/METADATA +1 -1
- nv_ingest_api-2025.4.19.dev20250419.dist-info/RECORD +9 -0
- {nv_ingest_api-2025.4.17.dev20250417.dist-info → nv_ingest_api-2025.4.19.dev20250419.dist-info}/WHEEL +1 -1
- nv_ingest_api/interface/__init__.py +0 -215
- nv_ingest_api/interface/extract.py +0 -972
- nv_ingest_api/interface/mutate.py +0 -154
- nv_ingest_api/interface/store.py +0 -218
- nv_ingest_api/interface/transform.py +0 -382
- nv_ingest_api/interface/utility.py +0 -200
- nv_ingest_api/internal/enums/__init__.py +0 -3
- nv_ingest_api/internal/enums/common.py +0 -494
- nv_ingest_api/internal/extract/__init__.py +0 -3
- nv_ingest_api/internal/extract/audio/__init__.py +0 -3
- nv_ingest_api/internal/extract/audio/audio_extraction.py +0 -149
- nv_ingest_api/internal/extract/docx/__init__.py +0 -5
- nv_ingest_api/internal/extract/docx/docx_extractor.py +0 -205
- nv_ingest_api/internal/extract/docx/engines/__init__.py +0 -0
- nv_ingest_api/internal/extract/docx/engines/docxreader_helpers/__init__.py +0 -3
- nv_ingest_api/internal/extract/docx/engines/docxreader_helpers/docx_helper.py +0 -122
- nv_ingest_api/internal/extract/docx/engines/docxreader_helpers/docxreader.py +0 -895
- nv_ingest_api/internal/extract/image/__init__.py +0 -3
- nv_ingest_api/internal/extract/image/chart_extractor.py +0 -353
- nv_ingest_api/internal/extract/image/image_extractor.py +0 -204
- nv_ingest_api/internal/extract/image/image_helpers/__init__.py +0 -3
- nv_ingest_api/internal/extract/image/image_helpers/common.py +0 -403
- nv_ingest_api/internal/extract/image/infographic_extractor.py +0 -253
- nv_ingest_api/internal/extract/image/table_extractor.py +0 -344
- nv_ingest_api/internal/extract/pdf/__init__.py +0 -3
- nv_ingest_api/internal/extract/pdf/engines/__init__.py +0 -19
- nv_ingest_api/internal/extract/pdf/engines/adobe.py +0 -484
- nv_ingest_api/internal/extract/pdf/engines/llama.py +0 -243
- nv_ingest_api/internal/extract/pdf/engines/nemoretriever.py +0 -597
- nv_ingest_api/internal/extract/pdf/engines/pdf_helpers/__init__.py +0 -146
- nv_ingest_api/internal/extract/pdf/engines/pdfium.py +0 -603
- nv_ingest_api/internal/extract/pdf/engines/tika.py +0 -96
- nv_ingest_api/internal/extract/pdf/engines/unstructured_io.py +0 -426
- nv_ingest_api/internal/extract/pdf/pdf_extractor.py +0 -74
- nv_ingest_api/internal/extract/pptx/__init__.py +0 -5
- nv_ingest_api/internal/extract/pptx/engines/__init__.py +0 -0
- nv_ingest_api/internal/extract/pptx/engines/pptx_helper.py +0 -799
- nv_ingest_api/internal/extract/pptx/pptx_extractor.py +0 -187
- nv_ingest_api/internal/mutate/__init__.py +0 -3
- nv_ingest_api/internal/mutate/deduplicate.py +0 -110
- nv_ingest_api/internal/mutate/filter.py +0 -133
- nv_ingest_api/internal/primitives/__init__.py +0 -0
- nv_ingest_api/internal/primitives/nim/__init__.py +0 -8
- nv_ingest_api/internal/primitives/nim/default_values.py +0 -15
- nv_ingest_api/internal/primitives/nim/model_interface/__init__.py +0 -3
- nv_ingest_api/internal/primitives/nim/model_interface/cached.py +0 -274
- nv_ingest_api/internal/primitives/nim/model_interface/decorators.py +0 -56
- nv_ingest_api/internal/primitives/nim/model_interface/deplot.py +0 -270
- nv_ingest_api/internal/primitives/nim/model_interface/helpers.py +0 -275
- nv_ingest_api/internal/primitives/nim/model_interface/nemoretriever_parse.py +0 -238
- nv_ingest_api/internal/primitives/nim/model_interface/paddle.py +0 -462
- nv_ingest_api/internal/primitives/nim/model_interface/parakeet.py +0 -367
- nv_ingest_api/internal/primitives/nim/model_interface/text_embedding.py +0 -132
- nv_ingest_api/internal/primitives/nim/model_interface/vlm.py +0 -152
- nv_ingest_api/internal/primitives/nim/model_interface/yolox.py +0 -1400
- nv_ingest_api/internal/primitives/nim/nim_client.py +0 -344
- nv_ingest_api/internal/primitives/nim/nim_model_interface.py +0 -81
- nv_ingest_api/internal/primitives/tracing/__init__.py +0 -0
- nv_ingest_api/internal/primitives/tracing/latency.py +0 -69
- nv_ingest_api/internal/primitives/tracing/logging.py +0 -96
- nv_ingest_api/internal/primitives/tracing/tagging.py +0 -197
- nv_ingest_api/internal/schemas/__init__.py +0 -3
- nv_ingest_api/internal/schemas/extract/__init__.py +0 -3
- nv_ingest_api/internal/schemas/extract/extract_audio_schema.py +0 -130
- nv_ingest_api/internal/schemas/extract/extract_chart_schema.py +0 -135
- nv_ingest_api/internal/schemas/extract/extract_docx_schema.py +0 -124
- nv_ingest_api/internal/schemas/extract/extract_image_schema.py +0 -124
- nv_ingest_api/internal/schemas/extract/extract_infographic_schema.py +0 -128
- nv_ingest_api/internal/schemas/extract/extract_pdf_schema.py +0 -218
- nv_ingest_api/internal/schemas/extract/extract_pptx_schema.py +0 -124
- nv_ingest_api/internal/schemas/extract/extract_table_schema.py +0 -129
- nv_ingest_api/internal/schemas/message_brokers/__init__.py +0 -3
- nv_ingest_api/internal/schemas/message_brokers/message_broker_client_schema.py +0 -23
- nv_ingest_api/internal/schemas/message_brokers/request_schema.py +0 -34
- nv_ingest_api/internal/schemas/message_brokers/response_schema.py +0 -19
- nv_ingest_api/internal/schemas/meta/__init__.py +0 -3
- nv_ingest_api/internal/schemas/meta/base_model_noext.py +0 -11
- nv_ingest_api/internal/schemas/meta/ingest_job_schema.py +0 -237
- nv_ingest_api/internal/schemas/meta/metadata_schema.py +0 -221
- nv_ingest_api/internal/schemas/mutate/__init__.py +0 -3
- nv_ingest_api/internal/schemas/mutate/mutate_image_dedup_schema.py +0 -16
- nv_ingest_api/internal/schemas/store/__init__.py +0 -3
- nv_ingest_api/internal/schemas/store/store_embedding_schema.py +0 -28
- nv_ingest_api/internal/schemas/store/store_image_schema.py +0 -30
- nv_ingest_api/internal/schemas/transform/__init__.py +0 -3
- nv_ingest_api/internal/schemas/transform/transform_image_caption_schema.py +0 -15
- nv_ingest_api/internal/schemas/transform/transform_image_filter_schema.py +0 -17
- nv_ingest_api/internal/schemas/transform/transform_text_embedding_schema.py +0 -25
- nv_ingest_api/internal/schemas/transform/transform_text_splitter_schema.py +0 -22
- nv_ingest_api/internal/store/__init__.py +0 -3
- nv_ingest_api/internal/store/embed_text_upload.py +0 -236
- nv_ingest_api/internal/store/image_upload.py +0 -232
- nv_ingest_api/internal/transform/__init__.py +0 -3
- nv_ingest_api/internal/transform/caption_image.py +0 -205
- nv_ingest_api/internal/transform/embed_text.py +0 -496
- nv_ingest_api/internal/transform/split_text.py +0 -157
- nv_ingest_api/util/__init__.py +0 -0
- nv_ingest_api/util/control_message/__init__.py +0 -0
- nv_ingest_api/util/control_message/validators.py +0 -47
- nv_ingest_api/util/converters/__init__.py +0 -0
- nv_ingest_api/util/converters/bytetools.py +0 -78
- nv_ingest_api/util/converters/containers.py +0 -65
- nv_ingest_api/util/converters/datetools.py +0 -90
- nv_ingest_api/util/converters/dftools.py +0 -127
- nv_ingest_api/util/converters/formats.py +0 -64
- nv_ingest_api/util/converters/type_mappings.py +0 -27
- nv_ingest_api/util/detectors/__init__.py +0 -5
- nv_ingest_api/util/detectors/language.py +0 -38
- nv_ingest_api/util/exception_handlers/__init__.py +0 -0
- nv_ingest_api/util/exception_handlers/converters.py +0 -72
- nv_ingest_api/util/exception_handlers/decorators.py +0 -223
- nv_ingest_api/util/exception_handlers/detectors.py +0 -74
- nv_ingest_api/util/exception_handlers/pdf.py +0 -116
- nv_ingest_api/util/exception_handlers/schemas.py +0 -68
- nv_ingest_api/util/image_processing/__init__.py +0 -5
- nv_ingest_api/util/image_processing/clustering.py +0 -260
- nv_ingest_api/util/image_processing/processing.py +0 -179
- nv_ingest_api/util/image_processing/table_and_chart.py +0 -449
- nv_ingest_api/util/image_processing/transforms.py +0 -407
- nv_ingest_api/util/logging/__init__.py +0 -0
- nv_ingest_api/util/logging/configuration.py +0 -31
- nv_ingest_api/util/message_brokers/__init__.py +0 -3
- nv_ingest_api/util/message_brokers/simple_message_broker/__init__.py +0 -9
- nv_ingest_api/util/message_brokers/simple_message_broker/broker.py +0 -465
- nv_ingest_api/util/message_brokers/simple_message_broker/ordered_message_queue.py +0 -71
- nv_ingest_api/util/message_brokers/simple_message_broker/simple_client.py +0 -435
- nv_ingest_api/util/metadata/__init__.py +0 -5
- nv_ingest_api/util/metadata/aggregators.py +0 -469
- nv_ingest_api/util/multi_processing/__init__.py +0 -8
- nv_ingest_api/util/multi_processing/mp_pool_singleton.py +0 -194
- nv_ingest_api/util/nim/__init__.py +0 -56
- nv_ingest_api/util/pdf/__init__.py +0 -3
- nv_ingest_api/util/pdf/pdfium.py +0 -427
- nv_ingest_api/util/schema/__init__.py +0 -0
- nv_ingest_api/util/schema/schema_validator.py +0 -10
- nv_ingest_api/util/service_clients/__init__.py +0 -3
- nv_ingest_api/util/service_clients/client_base.py +0 -72
- nv_ingest_api/util/service_clients/kafka/__init__.py +0 -3
- nv_ingest_api/util/service_clients/redis/__init__.py +0 -0
- nv_ingest_api/util/service_clients/redis/redis_client.py +0 -334
- nv_ingest_api/util/service_clients/rest/__init__.py +0 -0
- nv_ingest_api/util/service_clients/rest/rest_client.py +0 -398
- nv_ingest_api/util/string_processing/__init__.py +0 -51
- nv_ingest_api-2025.4.17.dev20250417.dist-info/RECORD +0 -152
- /nv_ingest_api/{internal → primitives}/__init__.py +0 -0
- {nv_ingest_api-2025.4.17.dev20250417.dist-info → nv_ingest_api-2025.4.19.dev20250419.dist-info}/licenses/LICENSE +0 -0
- {nv_ingest_api-2025.4.17.dev20250417.dist-info → nv_ingest_api-2025.4.19.dev20250419.dist-info}/top_level.txt +0 -0
|
@@ -1,334 +0,0 @@
|
|
|
1
|
-
# SPDX-FileCopyrightText: Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES.
|
|
2
|
-
# All rights reserved.
|
|
3
|
-
# SPDX-License-Identifier: Apache-2.0
|
|
4
|
-
|
|
5
|
-
import json
|
|
6
|
-
import logging
|
|
7
|
-
import time
|
|
8
|
-
from typing import Any
|
|
9
|
-
from typing import Dict
|
|
10
|
-
from typing import List
|
|
11
|
-
from typing import Optional
|
|
12
|
-
from typing import Tuple
|
|
13
|
-
from typing import Union
|
|
14
|
-
|
|
15
|
-
import redis
|
|
16
|
-
from redis.exceptions import RedisError
|
|
17
|
-
|
|
18
|
-
from nv_ingest_api.util.service_clients.client_base import MessageBrokerClientBase
|
|
19
|
-
|
|
20
|
-
# pylint: skip-file
|
|
21
|
-
|
|
22
|
-
logger = logging.getLogger(__name__)
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
class RedisClient(MessageBrokerClientBase):
|
|
26
|
-
"""
|
|
27
|
-
A client for interfacing with Redis, providing mechanisms for sending and receiving messages
|
|
28
|
-
with retry logic and connection management.
|
|
29
|
-
|
|
30
|
-
Parameters
|
|
31
|
-
----------
|
|
32
|
-
host : str
|
|
33
|
-
The hostname of the Redis server.
|
|
34
|
-
port : int
|
|
35
|
-
The port number of the Redis server.
|
|
36
|
-
db : int, optional
|
|
37
|
-
The database number to connect to. Default is 0.
|
|
38
|
-
max_retries : int, optional
|
|
39
|
-
The maximum number of retry attempts for operations. Default is 0 (no retries).
|
|
40
|
-
max_backoff : int, optional
|
|
41
|
-
The maximum backoff delay between retries in seconds. Default is 32 seconds.
|
|
42
|
-
connection_timeout : int, optional
|
|
43
|
-
The timeout in seconds for connecting to the Redis server. Default is 300 seconds.
|
|
44
|
-
max_pool_size : int, optional
|
|
45
|
-
The maximum number of connections in the Redis connection pool. Default is 128.
|
|
46
|
-
use_ssl : bool, optional
|
|
47
|
-
Specifies if SSL should be used for the connection. Default is False.
|
|
48
|
-
redis_allocator : Any, optional
|
|
49
|
-
The Redis client allocator, allowing for custom Redis client instances. Default is redis.Redis.
|
|
50
|
-
|
|
51
|
-
Attributes
|
|
52
|
-
----------
|
|
53
|
-
client : Any
|
|
54
|
-
The Redis client instance used for operations.
|
|
55
|
-
"""
|
|
56
|
-
|
|
57
|
-
def __init__(
|
|
58
|
-
self,
|
|
59
|
-
host: str,
|
|
60
|
-
port: int,
|
|
61
|
-
db: int = 0,
|
|
62
|
-
max_retries: int = 0,
|
|
63
|
-
max_backoff: int = 32,
|
|
64
|
-
connection_timeout: int = 300,
|
|
65
|
-
max_pool_size: int = 128,
|
|
66
|
-
use_ssl: bool = False,
|
|
67
|
-
redis_allocator: Any = redis.Redis, # Type hint as 'Any' due to dynamic nature
|
|
68
|
-
):
|
|
69
|
-
self._host = host
|
|
70
|
-
self._port = port
|
|
71
|
-
self._db = db
|
|
72
|
-
self._max_retries = max_retries
|
|
73
|
-
self._max_backoff = max_backoff
|
|
74
|
-
self._connection_timeout = connection_timeout
|
|
75
|
-
self._use_ssl = use_ssl
|
|
76
|
-
self._pool = redis.ConnectionPool(
|
|
77
|
-
host=self._host,
|
|
78
|
-
port=self._port,
|
|
79
|
-
db=self._db,
|
|
80
|
-
socket_connect_timeout=self._connection_timeout,
|
|
81
|
-
max_connections=max_pool_size,
|
|
82
|
-
)
|
|
83
|
-
self._redis_allocator = redis_allocator
|
|
84
|
-
self._client = self._redis_allocator(connection_pool=self._pool)
|
|
85
|
-
self._retries = 0
|
|
86
|
-
|
|
87
|
-
def _connect(self) -> None:
|
|
88
|
-
"""
|
|
89
|
-
Attempts to reconnect to the Redis server if the current connection is not responsive.
|
|
90
|
-
"""
|
|
91
|
-
if not self.ping():
|
|
92
|
-
logger.debug("Reconnecting to Redis")
|
|
93
|
-
self._client = self._redis_allocator(connection_pool=self._pool)
|
|
94
|
-
|
|
95
|
-
@property
|
|
96
|
-
def max_retries(self) -> int:
|
|
97
|
-
return self._max_retries
|
|
98
|
-
|
|
99
|
-
@max_retries.setter
|
|
100
|
-
def max_retries(self, value: int) -> None:
|
|
101
|
-
self._max_retries = value
|
|
102
|
-
|
|
103
|
-
def get_client(self) -> Any:
|
|
104
|
-
"""
|
|
105
|
-
Returns a Redis client instance, reconnecting if necessary.
|
|
106
|
-
|
|
107
|
-
Returns
|
|
108
|
-
-------
|
|
109
|
-
Any
|
|
110
|
-
The Redis client instance.
|
|
111
|
-
"""
|
|
112
|
-
if self._client is None or not self.ping():
|
|
113
|
-
self._connect()
|
|
114
|
-
return self._client
|
|
115
|
-
|
|
116
|
-
def ping(self) -> bool:
|
|
117
|
-
"""
|
|
118
|
-
Checks if the Redis server is responsive.
|
|
119
|
-
|
|
120
|
-
Returns
|
|
121
|
-
-------
|
|
122
|
-
bool
|
|
123
|
-
True if the server responds to a ping, False otherwise.
|
|
124
|
-
"""
|
|
125
|
-
try:
|
|
126
|
-
self._client.ping()
|
|
127
|
-
return True
|
|
128
|
-
except (RedisError, AttributeError):
|
|
129
|
-
return False
|
|
130
|
-
|
|
131
|
-
def _check_response(
|
|
132
|
-
self, channel_name: str, timeout: float
|
|
133
|
-
) -> Tuple[Optional[Dict[str, Any]], Optional[int], Optional[int]]:
|
|
134
|
-
"""
|
|
135
|
-
Checks for a response from the Redis queue and processes it into a message, fragment, and fragment count.
|
|
136
|
-
|
|
137
|
-
Parameters
|
|
138
|
-
----------
|
|
139
|
-
channel_name : str
|
|
140
|
-
The name of the Redis channel from which to receive the response.
|
|
141
|
-
timeout : float
|
|
142
|
-
The time in seconds to wait for a response from the Redis queue before timing out.
|
|
143
|
-
|
|
144
|
-
Returns
|
|
145
|
-
-------
|
|
146
|
-
Tuple[Optional[Dict[str, Any]], Optional[int], Optional[int]]
|
|
147
|
-
A tuple containing:
|
|
148
|
-
- message: A dictionary containing the decoded message if successful,
|
|
149
|
-
or None if no message was retrieved.
|
|
150
|
-
- fragment: An integer representing the fragment number of the message,
|
|
151
|
-
or None if no fragment was found.
|
|
152
|
-
- fragment_count: An integer representing the total number of message fragments,
|
|
153
|
-
or None if no fragment count was found.
|
|
154
|
-
|
|
155
|
-
Raises
|
|
156
|
-
------
|
|
157
|
-
ValueError
|
|
158
|
-
If the message retrieved from Redis cannot be decoded from JSON.
|
|
159
|
-
"""
|
|
160
|
-
|
|
161
|
-
response = self.get_client().blpop([channel_name], timeout)
|
|
162
|
-
if response is None:
|
|
163
|
-
raise TimeoutError("No response was received in the specified timeout period")
|
|
164
|
-
|
|
165
|
-
if len(response) > 1 and response[1]:
|
|
166
|
-
try:
|
|
167
|
-
message = json.loads(response[1])
|
|
168
|
-
fragment = message.get("fragment", 0)
|
|
169
|
-
fragment_count = message.get("fragment_count", 1)
|
|
170
|
-
|
|
171
|
-
return message, fragment, fragment_count
|
|
172
|
-
except json.JSONDecodeError as e:
|
|
173
|
-
logger.error(f"Failed to decode message: {e}")
|
|
174
|
-
raise ValueError(f"Failed to decode message from Redis: {e}")
|
|
175
|
-
|
|
176
|
-
return None, None, None
|
|
177
|
-
|
|
178
|
-
def fetch_message(self, channel_name: str, timeout: float = 10) -> Optional[Union[str, Dict]]:
|
|
179
|
-
"""
|
|
180
|
-
Fetches a message from the specified queue with retries on failure. If the message is fragmented, it will
|
|
181
|
-
continue fetching fragments until all parts have been collected.
|
|
182
|
-
|
|
183
|
-
Parameters
|
|
184
|
-
----------
|
|
185
|
-
channel_name: str
|
|
186
|
-
Channel to fetch the message from.
|
|
187
|
-
timeout : float
|
|
188
|
-
The timeout in seconds for blocking until a message is available. If we receive a multi-part message,
|
|
189
|
-
this value will be temporarily extended in order to collect all fragments.
|
|
190
|
-
|
|
191
|
-
Returns
|
|
192
|
-
-------
|
|
193
|
-
Optional[str or Dict]
|
|
194
|
-
The full fetched message, or None if no message could be fetched after retries.
|
|
195
|
-
|
|
196
|
-
Raises
|
|
197
|
-
------
|
|
198
|
-
ValueError
|
|
199
|
-
If fetching the message fails after the specified number of retries or due to other critical errors.
|
|
200
|
-
"""
|
|
201
|
-
accumulated_time = 0
|
|
202
|
-
collected_fragments = []
|
|
203
|
-
fragment_count = None
|
|
204
|
-
retries = 0
|
|
205
|
-
|
|
206
|
-
logger.debug(f"Starting fetch_message on channel '{channel_name}' with timeout {timeout}s.")
|
|
207
|
-
|
|
208
|
-
while True:
|
|
209
|
-
try:
|
|
210
|
-
# Attempt to fetch a message from the Redis queue
|
|
211
|
-
message, fragment, fragment_count = self._check_response(channel_name, timeout)
|
|
212
|
-
logger.debug(f"Fetched fragment: {fragment} (fragment_count: {fragment_count}).")
|
|
213
|
-
|
|
214
|
-
if message is not None:
|
|
215
|
-
if fragment_count == 1:
|
|
216
|
-
return message
|
|
217
|
-
|
|
218
|
-
collected_fragments.append(message)
|
|
219
|
-
logger.debug(f"Collected {len(collected_fragments)} of {fragment_count} fragments so far.")
|
|
220
|
-
|
|
221
|
-
# If we have collected all fragments, combine and return
|
|
222
|
-
if len(collected_fragments) == fragment_count:
|
|
223
|
-
logger.debug("All fragments received. Sorting and combining fragments.")
|
|
224
|
-
# Sort fragments by the 'fragment' field to ensure correct order
|
|
225
|
-
collected_fragments.sort(key=lambda x: x["fragment"])
|
|
226
|
-
reconstructed_message = self._combine_fragments(collected_fragments)
|
|
227
|
-
logger.debug("Message reconstructed successfully. Returning combined message.")
|
|
228
|
-
return reconstructed_message
|
|
229
|
-
else:
|
|
230
|
-
logger.debug("Received empty response; returning None.")
|
|
231
|
-
return message
|
|
232
|
-
|
|
233
|
-
except TimeoutError:
|
|
234
|
-
# When fragments are expected but not all received before timeout
|
|
235
|
-
if fragment_count and fragment_count > 1:
|
|
236
|
-
accumulated_time += timeout
|
|
237
|
-
logger.debug(
|
|
238
|
-
f"Timeout occurred waiting for fragments. "
|
|
239
|
-
f"Accumulated timeout: {accumulated_time}s (Threshold: {timeout * fragment_count}s)."
|
|
240
|
-
)
|
|
241
|
-
if accumulated_time >= (timeout * fragment_count):
|
|
242
|
-
err_msg = f"Failed to reconstruct message from {channel_name} after {accumulated_time} sec."
|
|
243
|
-
logger.error(err_msg)
|
|
244
|
-
raise ValueError(err_msg)
|
|
245
|
-
else:
|
|
246
|
-
raise # This is expected in many cases, so re-raise it
|
|
247
|
-
|
|
248
|
-
except RedisError as err:
|
|
249
|
-
retries += 1
|
|
250
|
-
logger.error(f"Redis error during fetch: {err}")
|
|
251
|
-
backoff_delay = min(2**retries, self._max_backoff)
|
|
252
|
-
|
|
253
|
-
if self.max_retries > 0 and retries <= self.max_retries:
|
|
254
|
-
logger.error(f"Fetch attempt failed, retrying in {backoff_delay}s...")
|
|
255
|
-
time.sleep(backoff_delay)
|
|
256
|
-
else:
|
|
257
|
-
logger.error(f"Failed to fetch message from {channel_name} after {retries} attempts.")
|
|
258
|
-
raise ValueError(f"Failed to fetch message from Redis queue after {retries} attempts: {err}")
|
|
259
|
-
|
|
260
|
-
# Invalidate client to force reconnection on the next try
|
|
261
|
-
self._client = None
|
|
262
|
-
|
|
263
|
-
except Exception as e:
|
|
264
|
-
# Handle non-Redis specific exceptions
|
|
265
|
-
logger.error(f"Unexpected error during fetch from {channel_name}: {e}")
|
|
266
|
-
raise ValueError(f"Unexpected error during fetch: {e}")
|
|
267
|
-
|
|
268
|
-
@staticmethod
|
|
269
|
-
def _combine_fragments(fragments: List[Dict[str, Any]]) -> Dict:
|
|
270
|
-
"""
|
|
271
|
-
Combines multiple message fragments into a single message by extending the 'data' elements,
|
|
272
|
-
retaining the 'status' and 'description' of the first fragment, and removing 'fragment' and 'fragment_counts'.
|
|
273
|
-
|
|
274
|
-
Parameters
|
|
275
|
-
----------
|
|
276
|
-
fragments : List[Dict[str, Any]]
|
|
277
|
-
A list of fragments to be combined.
|
|
278
|
-
|
|
279
|
-
Returns
|
|
280
|
-
-------
|
|
281
|
-
str
|
|
282
|
-
The combined message as a JSON string, containing 'status', 'description', and combined 'data'.
|
|
283
|
-
"""
|
|
284
|
-
if not fragments:
|
|
285
|
-
raise ValueError("Fragments list is empty")
|
|
286
|
-
|
|
287
|
-
# Use 'status' and 'description' from the first fragment
|
|
288
|
-
combined_message = {
|
|
289
|
-
"status": fragments[0]["status"],
|
|
290
|
-
"description": fragments[0]["description"],
|
|
291
|
-
"data": [],
|
|
292
|
-
"trace": fragments[0].get("trace", {}),
|
|
293
|
-
}
|
|
294
|
-
|
|
295
|
-
# Combine the 'data' elements from all fragments
|
|
296
|
-
for fragment in fragments:
|
|
297
|
-
combined_message["data"].extend(fragment["data"])
|
|
298
|
-
|
|
299
|
-
return combined_message
|
|
300
|
-
|
|
301
|
-
def submit_message(self, channel_name: str, message: str) -> None:
|
|
302
|
-
"""
|
|
303
|
-
Submits a message to a specified Redis queue with retries on failure.
|
|
304
|
-
|
|
305
|
-
Parameters
|
|
306
|
-
----------
|
|
307
|
-
channel_name : str
|
|
308
|
-
The name of the queue to submit the message to.
|
|
309
|
-
message : str
|
|
310
|
-
The message to submit.
|
|
311
|
-
|
|
312
|
-
Raises
|
|
313
|
-
------
|
|
314
|
-
RedisError
|
|
315
|
-
If submitting the message fails after the specified number of retries.
|
|
316
|
-
"""
|
|
317
|
-
retries = 0
|
|
318
|
-
while True:
|
|
319
|
-
try:
|
|
320
|
-
self.get_client().rpush(channel_name, message)
|
|
321
|
-
logger.debug(f"Message submitted to {channel_name}")
|
|
322
|
-
break
|
|
323
|
-
except RedisError as e:
|
|
324
|
-
logger.error(f"Failed to submit message, retrying... Error: {e}")
|
|
325
|
-
self._client = None # Invalidate client to force reconnection
|
|
326
|
-
retries += 1
|
|
327
|
-
backoff_delay = min(2**retries, self._max_backoff)
|
|
328
|
-
|
|
329
|
-
if self.max_retries == 0 or retries < self.max_retries:
|
|
330
|
-
logger.error(f"Submit attempt failed, retrying in {backoff_delay}s...")
|
|
331
|
-
time.sleep(backoff_delay)
|
|
332
|
-
else:
|
|
333
|
-
logger.error(f"Failed to submit message to {channel_name} after {retries} attempts.")
|
|
334
|
-
raise
|
|
File without changes
|