From dd631acc69899bfec373d3ccd9eaf7e3e69a9b65 Mon Sep 17 00:00:00 2001 From: faywang123 Date: Mon, 13 Jan 2025 12:07:51 -0800 Subject: [PATCH 1/7] audio integration --- .../primitives/tasks/extract.py | 6 + .../util/file_processing/extract.py | 5 +- docker-compose.yaml | 26 ++ .../schemas/audio_extractor_schema.py | 131 +++++++++ src/nv_ingest/schemas/ingest_job_schema.py | 3 +- src/nv_ingest/stages/nim/audio_extraction.py | 254 ++++++++++++++++++ src/nv_ingest/util/nim/helpers.py | 51 ++++ .../util/pipeline/pipeline_builders.py | 6 +- src/nv_ingest/util/pipeline/stage_builders.py | 52 ++++ 9 files changed, 531 insertions(+), 3 deletions(-) create mode 100755 src/nv_ingest/schemas/audio_extractor_schema.py create mode 100755 src/nv_ingest/stages/nim/audio_extraction.py diff --git a/client/src/nv_ingest_client/primitives/tasks/extract.py b/client/src/nv_ingest_client/primitives/tasks/extract.py index 6d3722f5..ae2a8bce 100644 --- a/client/src/nv_ingest_client/primitives/tasks/extract.py +++ b/client/src/nv_ingest_client/primitives/tasks/extract.py @@ -45,6 +45,8 @@ "svg": "image", "tiff": "image", "xml": "lxml", + "mp3": "audio", + "wav": "audio", } _Type_Extract_Method_PDF = Literal[ @@ -63,6 +65,8 @@ _Type_Extract_Method_Image = Literal["image"] +_Type_Extract_Method_Audio = Literal["audio"] + _Type_Extract_Method_Map = { "docx": get_args(_Type_Extract_Method_DOCX), "jpeg": get_args(_Type_Extract_Method_Image), @@ -72,6 +76,8 @@ "pptx": get_args(_Type_Extract_Method_PPTX), "svg": get_args(_Type_Extract_Method_Image), "tiff": get_args(_Type_Extract_Method_Image), + "mp3": get_args(_Type_Extract_Method_Audio), + "wav": get_args(_Type_Extract_Method_Audio), } _Type_Extract_Tables_Method_PDF = Literal["yolox", "pdfium"] diff --git a/client/src/nv_ingest_client/util/file_processing/extract.py b/client/src/nv_ingest_client/util/file_processing/extract.py index 97851481..d82ad6c7 100644 --- a/client/src/nv_ingest_client/util/file_processing/extract.py +++ b/client/src/nv_ingest_client/util/file_processing/extract.py @@ -32,7 +32,8 @@ class DocumentTypeEnum(str, Enum): svg = "svg" tiff = "tiff" txt = "text" - + mp3 = "mp3" + wav = "wav" # Maps MIME types to DocumentTypeEnum MIME_TO_DOCUMENT_TYPE = { @@ -64,6 +65,8 @@ class DocumentTypeEnum(str, Enum): "svg": DocumentTypeEnum.svg, "tiff": DocumentTypeEnum.tiff, "txt": DocumentTypeEnum.txt, + "mp3": DocumentTypeEnum.mp3, + "wav": DocumentTypeEnum.wav, # Add more as needed } diff --git a/docker-compose.yaml b/docker-compose.yaml index 8d9c307a..3f5fd918 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -125,6 +125,29 @@ services: capabilities: [gpu] runtime: nvidia + audio: + image: ads/audio:latest + shm_size: 2gb + ports: + - "8015:8000" + user: root + environment: + - NIM_HTTP_API_PORT=8000 + - NIM_TRITON_LOG_VERBOSE=1 + - NGC_API_KEY=${NIM_NGC_API_KEY:-${NGC_API_KEY:-ngcapikey}} + - CUDA_VISIBLE_DEVICES=0 + deploy: + resources: + reservations: + devices: + - driver: nvidia + device_ids: ["1"] + capabilities: [gpu] + runtime: nvidia + working_dir: /app/audio_retrieval/src + + + nv-ingest-ms-runtime: image: nvcr.io/ohlfw0olaadg/ea-participants/nv-ingest:24.10.1 build: @@ -141,6 +164,9 @@ services: cap_add: - sys_nice environment: + # Self-hosted audio endpoints. + - AUDIO_HTTP_ENDPOINT=http://audio:8000/v1/transcribe + - AUDIO_INFER_PROTOCOL=http # Self-hosted cached endpoints. - CACHED_GRPC_ENDPOINT=cached:8001 - CACHED_HTTP_ENDPOINT=http://cached:8000/v1/infer diff --git a/src/nv_ingest/schemas/audio_extractor_schema.py b/src/nv_ingest/schemas/audio_extractor_schema.py new file mode 100755 index 00000000..49a3dc5d --- /dev/null +++ b/src/nv_ingest/schemas/audio_extractor_schema.py @@ -0,0 +1,131 @@ +# SPDX-FileCopyrightText: Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. +# All rights reserved. +# SPDX-License-Identifier: Apache-2.0 + + +import logging +from typing import Optional +from typing import Tuple + +from pydantic import BaseModel +from pydantic import root_validator + +logger = logging.getLogger(__name__) + + +class AudioConfigSchema(BaseModel): + """ + Configuration schema for audio extraction endpoints and options. + + Parameters + ---------- + auth_token : Optional[str], default=None + Authentication token required for secure services. + + audio_endpoints : Tuple[str, str] + A tuple containing the gRPC and HTTP services for the audio_retriever endpoint. + Either the gRPC or HTTP service can be empty, but not both. + + Methods + ------- + validate_endpoints(values) + Validates that at least one of the gRPC or HTTP services is provided for each endpoint. + + Raises + ------ + ValueError + If both gRPC and HTTP services are empty for any endpoint. + + Config + ------ + extra : str + Pydantic config option to forbid extra fields. + """ + + auth_token: Optional[str] = None + audio_endpoints: Tuple[Optional[str], Optional[str]] = (None, None) + audio_infer_protocol: Optional[str] = None + + @root_validator(pre=True) + def validate_endpoints(cls, values): + """ + Validates the gRPC and HTTP services for all endpoints. + + Parameters + ---------- + values : dict + Dictionary containing the values of the attributes for the class. + + Returns + ------- + dict + The validated dictionary of values. + + Raises + ------ + ValueError + If both gRPC and HTTP services are empty for any endpoint. + """ + + def clean_service(service): + """Set service to None if it's an empty string or contains only spaces or quotes.""" + if service is None or not service.strip() or service.strip(" \"'") == "": + return None + return service + + + print ('===> audio extractor schema values:', values) + endpoint_name = "audio_endpoints" + grpc_service, http_service = values.get(endpoint_name) + print ("grpc_service:", grpc_service) + print ("http_service:", http_service) + grpc_service = clean_service(grpc_service) + http_service = clean_service(http_service) + + if not grpc_service and not http_service: + raise ValueError(f"Both gRPC and HTTP services cannot be empty for {endpoint_name}.") + + values[endpoint_name] = (grpc_service, http_service) + + protocol_name = "audio_infer_protocol" + protocol_value = values.get(protocol_name) + + print("protocol_value:", protocol_value) + if not protocol_value: + protocol_value = "http" if http_service else "grpc" if grpc_service else "" + protocol_value = protocol_value.lower() + values[protocol_name] = protocol_value + + return values + + class Config: + extra = "forbid" + + +class AudioExtractorSchema(BaseModel): + """ + Configuration schema for the PDF extractor settings. + + Parameters + ---------- + max_queue_size : int, default=1 + The maximum number of items allowed in the processing queue. + + n_workers : int, default=16 + The number of worker threads to use for processing. + + raise_on_failure : bool, default=False + A flag indicating whether to raise an exception on processing failure. + + audio_extraction_config: Optional[AudioConfigSchema], default=None + Configuration schema for the audio extraction stage. + """ + + max_queue_size: int = 1 + n_workers: int = 16 + raise_on_failure: bool = False + + audio_extraction_config: Optional[AudioConfigSchema] = None + + class Config: + extra = "forbid" diff --git a/src/nv_ingest/schemas/ingest_job_schema.py b/src/nv_ingest/schemas/ingest_job_schema.py index 97ffc539..09975228 100644 --- a/src/nv_ingest/schemas/ingest_job_schema.py +++ b/src/nv_ingest/schemas/ingest_job_schema.py @@ -33,7 +33,8 @@ class DocumentTypeEnum(str, Enum): svg = "svg" tiff = "tiff" txt = "text" - + mp3 = "mp3" + wav = "wav" class TaskTypeEnum(str, Enum): caption = "caption" diff --git a/src/nv_ingest/stages/nim/audio_extraction.py b/src/nv_ingest/stages/nim/audio_extraction.py new file mode 100755 index 00000000..55556936 --- /dev/null +++ b/src/nv_ingest/stages/nim/audio_extraction.py @@ -0,0 +1,254 @@ +# SPDX-FileCopyrightText: Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. +# All rights reserved. +# SPDX-License-Identifier: Apache-2.0 + +import logging +import functools +import pandas as pd +from typing import Any +from typing import Dict +from typing import Optional +from typing import Tuple + +import tritonclient.grpc as grpcclient +from morpheus.config import Config +from nv_ingest.schemas.audio_extractor_schema import AudioExtractorSchema +from nv_ingest.stages.multiprocessing_stage import MultiProcessingBaseStage + +import sys +sys.path.append('../../..') + +from nv_ingest.util.nim.helpers import call_audio_inference_model, create_inference_client +from nv_ingest.util.nim.helpers import get_version + +logger = logging.getLogger(f"morpheus.{__name__}") + + +def _update_metadata(row: pd.Series, audio_client: Any, audio_version: Any, trace_info: Dict) -> Dict: + """ + Modifies the metadata of a row if the conditions for table extraction are met. + + Parameters + ---------- + row : pd.Series + A row from the DataFrame containing metadata for the audio extraction. + + audio_client : Any + The client used to call the audio inference model. + + trace_info : Dict + Trace information used for logging or debugging. + + Returns + ------- + Dict + The modified metadata if conditions are met, otherwise the original metadata. + + Raises + ------ + ValueError + If critical information (such as metadata) is missing from the row. + """ + + + metadata = row.get("metadata") + + if metadata is None: + logger.error("Row does not contain 'metadata'.") + raise ValueError("Row does not contain 'metadata'.") + + content_metadata = metadata.get("content_metadata", {}) + + # Only modify if content type is audio + if content_metadata.get("type") != "audio" : + return metadata + + source_metadata = metadata.get("source_metadata") + audio_id = source_metadata['source_id'] + + content_metadata = metadata.get("content_metadata") + content_metadata = content_metadata['content'] + audio_content = content_metadata['content'] + + + # Modify audio metadata with the result from the inference model + try: + audio_result = call_audio_inference_model(audio_client, audio_content, audio_id, trace_info=trace_info) + print(audio_result) + metadata['audio_metadata'] = {'content': audio_result} + except Exception as e: + logger.error(f"Unhandled error calling audio inference model: {e}", exc_info=True) + raise + + return metadata + + +def _transcribe_audio(df: pd.DataFrame, task_props: Dict[str, Any], + validated_config: Any, trace_info: Optional[Dict] = None) -> Tuple[pd.DataFrame, Dict]: + """ + Extracts audio data from a DataFrame. + + Parameters + ---------- + df : pd.DataFrame + DataFrame containing the content from which audio data is to be extracted. + + task_props : Dict[str, Any] + Dictionary containing task properties and configurations. + + validated_config : Any + The validated configuration object for audio extraction. + + trace_info : Optional[Dict], optional + Optional trace information for debugging or logging. Defaults to None. + + Returns + ------- + Tuple[pd.DataFrame, Dict] + A tuple containing the updated DataFrame and the trace information. + + Raises + ------ + Exception + If any error occurs during the audio data extraction process. + """ + + #port = 32783 + #audio_client = create_inference_client( + # (None, f'http://0.0.0.0:{port}/v1/transcribe'), + # None, + # "http" + #) + + + audio_client = create_inference_client( + validated_config.stage_config.audio_endpoints, + None, + "http" + ) + + if trace_info is None: + trace_info = {} + logger.debug("No trace_info provided. Initialized empty trace_info dictionary.") + + try: + # Apply the _update_metadata function to each row in the DataFrame + #audio_version = get_version(validated_config.stage_config.audio_endpoints[1]) + audio_version = get_version(f'http://audio:{port}') + df["metadata"] = df.apply(_update_metadata, axis=1, args=(audio_client, audio_version, trace_info)) + + return df, trace_info + + except Exception as e: + logger.error("Error occurred while extracting audio data.", exc_info=True) + raise + + +def generate_audio_extractor_stage( + c: Config, + stage_config: Dict[str, Any], + task: str = "audio_data_extract", + task_desc: str = "audio_data_extraction", + pe_count: int = 1, +): + """ + Generates a multiprocessing stage to perform audio data extraction. + + Parameters + ---------- + c : Config + Morpheus global configuration object. + + stage_config : Dict[str, Any] + Configuration parameters for the audio content extractor, passed as a dictionary + validated against the `AudioExtractorSchema`. + + task : str, optional + The task name for the stage worker function, defining the specific audio extraction process. + Default is "audio_data_extract". + + task_desc : str, optional + A descriptor used for latency tracing and logging during audio extraction. + Default is "audio_data_extraction". + + pe_count : int, optional + The number of process engines to use for audio data extraction. This value controls + how many worker processes will run concurrently. Default is 1. + + Returns + ------- + MultiProcessingBaseStage + A configured Morpheus stage with an applied worker function that handles audio data extraction + from PDF content. + """ + + validated_config = AudioExtractorSchema(**stage_config) + _wrapped_process_fn = functools.partial(_transcribe_audio, validated_config=validated_config) + + return MultiProcessingBaseStage( + c=c, + pe_count=pe_count, + task=task, + task_desc=task_desc, + process_fn=_wrapped_process_fn, + document_type="regex:^(mp3|wav)$", + ) + + + +if __name__ == "__main__": + metadata = { + "source_metadata": { + "access_level": 1, + "collection_id": "", + "date_created": "2024-11-04T12:29:08", + "last_modified": "2024-11-04T12:29:08", + "partition_id": -1, + "source_id": "https://audio.listennotes.com/e/p/3946bc3aba1f425f8b2e146f0b3f72fc/", + "source_location": "", + "source_type": "wav", + "summary": "" + }, + + "content_metadata": { + "description": "Audio wav file", + "type": "audio", + "content": '' + } + } + + + metadata = { + "source_metadata": { + "access_level": 1, + "collection_id": "", + "date_created": "2024-11-04T12:29:08", + "last_modified": "2024-11-04T12:29:08", + "partition_id": -1, + "source_id": "test.mp3", + "source_location": "", + "source_type": "mp3", + "summary": "" + }, + + "content_metadata": { + "description": "Audio wav file", + "type": "audio", + "content": 'some base64 string' + } + } + + + + data = [{"metadata": metadata}] + df = pd.DataFrame(data) + + df.to_csv('test.csv', index=False) + + df_result, _ = _transcribe_audio(df) + + df_result.to_csv('result.csv', index=False) + + + + print("Done!") diff --git a/src/nv_ingest/util/nim/helpers.py b/src/nv_ingest/util/nim/helpers.py index db7e0fdd..61a41634 100644 --- a/src/nv_ingest/util/nim/helpers.py +++ b/src/nv_ingest/util/nim/helpers.py @@ -593,3 +593,54 @@ def get_version(http_endpoint: str, metadata_endpoint: str = "/v1/metadata", ver # Don't let anything squeeze by logger.warning(f"Exception: {ex}") return "" + + +def call_audio_inference_model(client, audio_content: str, audio_id: str, trace_info: dict): + """ + Calls an audio inference model using the provided client. + + If the client is a gRPC client, the inference is performed using gRPC. Otherwise, it is performed using HTTP. + + Parameters + ---------- + client : grpcclient.InferenceServerClient or dict + The inference client, which is an HTTP client. + audio_source : str + The audio source to transcribe. + + Returns + ------- + str or None + The result of the inference as a string if successful, otherwise `None`. + + Raises + ------ + RuntimeError + If the HTTP request fails or if the response format is not as expected. + """ + + try: + url = client["endpoint_url"] + headers = client["headers"] + + payload = {"audio_content": audio_content, "audio_id": audio_id} + response = requests.post(url, json=payload, headers=headers) + + response.raise_for_status() # Raise an exception for HTTP errors + + # Parse the JSON response + json_response = response.json() + + except requests.exceptions.RequestException as e: + raise RuntimeError(f"HTTP request failed: {e}") + except KeyError as e: + raise RuntimeError(f"Missing expected key in response: {e}") + except Exception as e: + raise RuntimeError(f"An error occurred during inference: {e}") + + return json_response + + + + + diff --git a/src/nv_ingest/util/pipeline/pipeline_builders.py b/src/nv_ingest/util/pipeline/pipeline_builders.py index 842682f0..5a1c25cb 100644 --- a/src/nv_ingest/util/pipeline/pipeline_builders.py +++ b/src/nv_ingest/util/pipeline/pipeline_builders.py @@ -32,6 +32,8 @@ def setup_ingestion_pipeline( image_extractor_stage = add_image_extractor_stage(pipe, morpheus_pipeline_config, ingest_config, default_cpu_count) docx_extractor_stage = add_docx_extractor_stage(pipe, morpheus_pipeline_config, default_cpu_count) pptx_extractor_stage = add_pptx_extractor_stage(pipe, morpheus_pipeline_config, default_cpu_count) + ## audio extraction + audio_extractor_stage = add_audio_extractor_stage(pipe, morpheus_pipeline_config, ingest_config, default_cpu_count) ######################################################################################################## ######################################################################################################## @@ -76,7 +78,9 @@ def setup_ingestion_pipeline( pipe.add_edge(pdf_extractor_stage, image_extractor_stage) pipe.add_edge(image_extractor_stage, docx_extractor_stage) pipe.add_edge(docx_extractor_stage, pptx_extractor_stage) - pipe.add_edge(pptx_extractor_stage, image_dedup_stage) + pipe.add_edge(pptx_extractor_stage, audio_extractor_stage) + pipe.add_edge(audio_extractor_stage, image_dedup_stage) + pipe.add_edge(image_dedup_stage, image_filter_stage) pipe.add_edge(image_filter_stage, table_extraction_stage) pipe.add_edge(table_extraction_stage, chart_extraction_stage) diff --git a/src/nv_ingest/util/pipeline/stage_builders.py b/src/nv_ingest/util/pipeline/stage_builders.py index 352ed006..b5153cbb 100644 --- a/src/nv_ingest/util/pipeline/stage_builders.py +++ b/src/nv_ingest/util/pipeline/stage_builders.py @@ -28,6 +28,7 @@ from nv_ingest.stages.filters import generate_image_filter_stage from nv_ingest.stages.nim.chart_extraction import generate_chart_extractor_stage from nv_ingest.stages.nim.table_extraction import generate_table_extractor_stage +from nv_ingest.stages.nim.audio_extraction import generate_audio_extractor_stage from nv_ingest.stages.pdf_extractor_stage import generate_pdf_extractor_stage from nv_ingest.stages.pptx_extractor_stage import generate_pptx_extractor_stage from nv_ingest.stages.storages.embedding_storage_stage import generate_embedding_storage_stage @@ -303,6 +304,57 @@ def add_pptx_extractor_stage(pipe, morpheus_pipeline_config, default_cpu_count): return pptx_extractor_stage +def get_audio_retrieval_service(env_var_prefix): + prefix = env_var_prefix.upper() + grpc_endpoint = os.environ.get( + "AUDIO_GRPC_ENDPOINT", + "", + ) + http_endpoint = os.environ.get( + "AUDIO_HTTP_ENDPOINT", + "", + ) + auth_token = os.environ.get( + "NVIDIA_BUILD_API_KEY", + "", + ) or os.environ.get( + "NGC_API_KEY", + "", + ) + infer_protocol = os.environ.get( + "AUDIO_INFER_PROTOCOL", + "http" if http_endpoint else "grpc" if grpc_endpoint else "", + ) + + logger.info(f"{prefix}_GRPC_TRITON: {grpc_endpoint}") + logger.info(f"{prefix}_HTTP_TRITON: {http_endpoint}") + logger.info(f"{prefix}_INFER_PROTOCOL: {infer_protocol}") + + return grpc_endpoint, http_endpoint, auth_token, infer_protocol + +def add_audio_extractor_stage(pipe, morpheus_pipeline_config, ingest_config, default_cpu_count): + audio_grpc, audio_http, audio_auth, audio_infer_protocol = get_audio_retrieval_service("audio") + audio_extractor_config = ingest_config.get("audio_extraction_module", + { + "audio_extraction_config": { + "audio_endpoints": (audio_grpc, audio_http), + "audio_infer_protocol": audio_infer_protocol, + "auth_token": audio_auth, + # All auth tokens are the same for the moment + } + }) + audio_extractor_stage = pipe.add_stage( + generate_audio_extractor_stage( + morpheus_pipeline_config, + stage_config=audio_extractor_config, + pe_count=8, + task="extract", + task_desc="audio_content_extractor", + ) + ) + return audio_extractor_stage + + def add_image_dedup_stage(pipe, morpheus_pipeline_config, ingest_config, default_cpu_count): image_dedup_config = ingest_config.get("dedup_module", {}) image_dedup_stage = pipe.add_stage( From 48fcfdd32b1f149444c2b7e884b00525139c76b0 Mon Sep 17 00:00:00 2001 From: faywang123 Date: Mon, 13 Jan 2025 18:35:06 -0800 Subject: [PATCH 2/7] audio image --- docker-compose.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker-compose.yaml b/docker-compose.yaml index 3f5fd918..11fc8236 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -126,7 +126,7 @@ services: runtime: nvidia audio: - image: ads/audio:latest + image: nvcr.io/nvidian/audio_retrieval:latest shm_size: 2gb ports: - "8015:8000" From 8dc7bf67faac4c4ba18d6d3e1319349182fa86ac Mon Sep 17 00:00:00 2001 From: Devin Robison Date: Fri, 17 Jan 2025 09:29:58 -0700 Subject: [PATCH 3/7] Merge upstream/main --- .../src/nv_ingest_client/client/interface.py | 17 +- client/src/nv_ingest_client/nv_ingest_cli.py | 4 +- .../primitives/tasks/caption.py | 1 + .../primitives/tasks/embed.py | 37 +- client/src/nv_ingest_client/util/milvus.py | 172 +++++- .../developer-guide/kubernetes-dev.md | 94 ++-- src/nv_ingest/api/v1/ingest.py | 171 +++++- .../extraction_workflows/docx/docx_helper.py | 21 +- .../extraction_workflows/docx/docxreader.py | 526 ++++++++++++++---- .../image/image_handlers.py | 193 +++---- .../extraction_workflows/pdf/pdfium_helper.py | 87 +-- .../extraction_workflows/pptx/pptx_helper.py | 301 ++++++---- .../modules/transforms/embed_extractions.py | 211 +++---- .../schemas/docx_extractor_schema.py | 124 +++++ src/nv_ingest/schemas/ingest_job_schema.py | 3 +- .../schemas/ingest_pipeline_config_schema.py | 4 +- src/nv_ingest/schemas/metadata_schema.py | 8 +- .../schemas/pptx_extractor_schema.py | 120 +++- .../schemas/processing_job_schema.py | 31 ++ .../impl/ingest/redis_ingest_service.py | 25 + .../meta/ingest/ingest_service_meta.py | 10 + src/nv_ingest/stages/docx_extractor_stage.py | 107 ++-- .../extractors/image_extractor_stage.py | 2 - src/nv_ingest/stages/nim/chart_extraction.py | 8 +- src/nv_ingest/stages/nim/table_extraction.py | 4 +- src/nv_ingest/stages/pptx_extractor_stage.py | 110 ++-- src/nv_ingest/util/converters/formats.py | 70 +++ src/nv_ingest/util/nim/cached.py | 2 +- src/nv_ingest/util/nim/deplot.py | 2 +- src/nv_ingest/util/nim/helpers.py | 13 +- src/nv_ingest/util/nim/yolox.py | 238 +++----- .../util/pdf/metadata_aggregators.py | 1 - .../util/pipeline/pipeline_builders.py | 6 +- src/nv_ingest/util/pipeline/stage_builders.py | 51 +- src/util/image_viewer.py | 27 +- .../docx/test_docx_helper.py | 17 +- .../image/test_image_handlers.py | 137 ----- .../pptx/test_pptx_helper.py | 20 +- .../test_message_broker_task_source.py | 2 +- .../schemas/test_ingest_job_schema.py | 10 +- .../multimodal_test_raw_results.json | 1 + .../nv_ingest/util/converters/test_formats.py | 101 ++++ tests/nv_ingest/util/nim/test_cached.py | 2 +- tests/nv_ingest/util/nim/test_yolox.py | 154 +++-- .../nv_ingest_client/client/test_interface.py | 25 +- .../nv_ingest_client/util/test_milvus_util.py | 67 +++ 46 files changed, 2152 insertions(+), 1185 deletions(-) create mode 100644 src/nv_ingest/schemas/docx_extractor_schema.py create mode 100644 src/nv_ingest/schemas/processing_job_schema.py create mode 100644 src/nv_ingest/util/converters/formats.py create mode 100644 tests/nv_ingest/util/converters/multimodal_test_raw_results.json create mode 100644 tests/nv_ingest/util/converters/test_formats.py create mode 100644 tests/nv_ingest_client/util/test_milvus_util.py diff --git a/client/src/nv_ingest_client/client/interface.py b/client/src/nv_ingest_client/client/interface.py index e5746651..0d4e3b0d 100644 --- a/client/src/nv_ingest_client/client/interface.py +++ b/client/src/nv_ingest_client/client/interface.py @@ -27,8 +27,8 @@ from nv_ingest_client.primitives.tasks import SplitTask from nv_ingest_client.primitives.tasks import StoreEmbedTask from nv_ingest_client.primitives.tasks import StoreTask -from nv_ingest_client.primitives.tasks import VdbUploadTask from nv_ingest_client.util.util import filter_function_kwargs +from nv_ingest_client.util.milvus import MilvusOperator DEFAULT_JOB_QUEUE_ID = "morpheus_task_queue" @@ -74,6 +74,7 @@ def __init__( self._documents = documents or [] self._client = client self._job_queue_id = job_queue_id + self._vdb_bulk_upload = None if self._client is None: client_kwargs = filter_function_kwargs(NvIngestClient, **kwargs) @@ -223,7 +224,10 @@ def ingest(self, **kwargs: Any) -> List[Dict[str, Any]]: fetch_kwargs = filter_function_kwargs(self._client.fetch_job_result, **kwargs) result = self._client.fetch_job_result(self._job_ids, **fetch_kwargs) - + if self._vdb_bulk_upload: + self._vdb_bulk_upload.run(result) + # only upload as part of jobs user specified this action + self._vdb_bulk_upload = None return result def ingest_async(self, **kwargs: Any) -> Future: @@ -271,6 +275,11 @@ def _done_callback(future): for future in future_to_job_id: future.add_done_callback(_done_callback) + if self._vdb_bulk_upload: + self._vdb_bulk_upload.run(combined_future) + # only upload as part of jobs user specified this action + self._vdb_bulk_upload = None + return combined_future @ensure_job_specs @@ -454,7 +463,6 @@ def store_embed(self, **kwargs: Any) -> "Ingestor": return self - @ensure_job_specs def vdb_upload(self, **kwargs: Any) -> "Ingestor": """ Adds a VdbUploadTask to the batch job specification. @@ -469,8 +477,7 @@ def vdb_upload(self, **kwargs: Any) -> "Ingestor": Ingestor Returns self for chaining. """ - vdb_upload_task = VdbUploadTask(**kwargs) - self._job_specs.add_task(vdb_upload_task) + self._vdb_bulk_upload = MilvusOperator(**kwargs) return self diff --git a/client/src/nv_ingest_client/nv_ingest_cli.py b/client/src/nv_ingest_client/nv_ingest_cli.py index fc6b96f4..f3cb0b9a 100644 --- a/client/src/nv_ingest_client/nv_ingest_cli.py +++ b/client/src/nv_ingest_client/nv_ingest_cli.py @@ -120,7 +120,7 @@ --task 'extract:{"document_type":"pdf", "extract_method":"unstructured_io"}' --task 'extract:{"document_type":"docx", "extract_text":true, "extract_images":true}' --task 'store:{"content_type":"image", "store_method":"minio", "endpoint":"minio:9000"}' - --task 'embed:{"text":true, "tables":true}' + --task 'embed' --task 'vdb_upload' --task 'caption:{}' @@ -143,8 +143,6 @@ - embed: Computes embeddings on multimodal extractions. Options: - filter_errors (bool): Flag to filter embedding errors. Optional. - - tables (bool): Flag to create embeddings for table extractions. Optional. - - text (bool): Flag to create embeddings for text extractions. Optional. \b - extract: Extracts content from documents, customizable per document type. Can be specified multiple times for different 'document_type' values. diff --git a/client/src/nv_ingest_client/primitives/tasks/caption.py b/client/src/nv_ingest_client/primitives/tasks/caption.py index 0f7297fe..adb5b922 100644 --- a/client/src/nv_ingest_client/primitives/tasks/caption.py +++ b/client/src/nv_ingest_client/primitives/tasks/caption.py @@ -24,6 +24,7 @@ class CaptionTaskSchema(BaseModel): model_name: Optional[str] = None model_config = ConfigDict(extra="forbid") + model_config["protected_namespaces"] = () class CaptionTask(Task): diff --git a/client/src/nv_ingest_client/primitives/tasks/embed.py b/client/src/nv_ingest_client/primitives/tasks/embed.py index 2949bc68..6bd51049 100644 --- a/client/src/nv_ingest_client/primitives/tasks/embed.py +++ b/client/src/nv_ingest_client/primitives/tasks/embed.py @@ -9,7 +9,7 @@ import logging from typing import Dict -from pydantic import BaseModel +from pydantic import BaseModel, root_validator from .task_base import Task @@ -17,10 +17,22 @@ class EmbedTaskSchema(BaseModel): - text: bool = True - tables: bool = True filter_errors: bool = False + @root_validator(pre=True) + def handle_deprecated_fields(cls, values): + if "text" in values: + logger.warning( + "'text' parameter is deprecated and will be ignored. Future versions will remove this argument." + ) + values.pop("text") + if "tables" in values: + logger.warning( + "'tables' parameter is deprecated and will be ignored. Future versions will remove this argument." + ) + values.pop("tables") + return values + class Config: extra = "forbid" @@ -30,13 +42,22 @@ class EmbedTask(Task): Object for document embedding task """ - def __init__(self, text: bool = True, tables: bool = True, filter_errors: bool = False) -> None: + def __init__(self, text: bool = None, tables: bool = None, filter_errors: bool = False) -> None: """ Setup Embed Task Config """ super().__init__() - self._text = text - self._tables = tables + + if text is not None: + logger.warning( + "'text' parameter is deprecated and will be ignored. Future versions will remove this argument." + ) + + if tables is not None: + logger.warning( + "'tables' parameter is deprecated and will be ignored. Future versions will remove this argument." + ) + self._filter_errors = filter_errors def __str__(self) -> str: @@ -45,8 +66,6 @@ def __str__(self) -> str: """ info = "" info += "Embed Task:\n" - info += f" text: {self._text}\n" - info += f" tables: {self._tables}\n" info += f" filter_errors: {self._filter_errors}\n" return info @@ -56,8 +75,6 @@ def to_dict(self) -> Dict: """ task_properties = { - "text": self._text, - "tables": self._tables, "filter_errors": False, } diff --git a/client/src/nv_ingest_client/util/milvus.py b/client/src/nv_ingest_client/util/milvus.py index 9e6e3230..8c8a9b6f 100644 --- a/client/src/nv_ingest_client/util/milvus.py +++ b/client/src/nv_ingest_client/util/milvus.py @@ -18,6 +18,87 @@ from typing import List import time from urllib.parse import urlparse +from typing import Union, Dict + + +def _dict_to_params(collections_dict: dict, write_params: dict): + params_tuple_list = [] + for coll_name, data_type in collections_dict.items(): + cp_write_params = write_params.copy() + enabled_dtypes = { + "enable_text": False, + "enable_charts": False, + "enable_tables": False, + } + if not isinstance(data_type, list): + data_type = [data_type] + for d_type in data_type: + enabled_dtypes[f"enable_{d_type}"] = True + cp_write_params.update(enabled_dtypes) + params_tuple_list.append((coll_name, cp_write_params)) + return params_tuple_list + + +class MilvusOperator: + def __init__( + self, + collection_name: Union[str, Dict] = "nv_ingest_collection", + milvus_uri: str = "http://localhost:19530", + sparse: bool = True, + recreate: bool = True, + gpu_index: bool = True, + gpu_search: bool = False, + dense_dim: int = 1024, + minio_endpoint: str = "localhost:9000", + enable_text: bool = True, + enable_charts: bool = True, + enable_tables: bool = True, + bm25_save_path: str = "bm25_model.json", + compute_bm25_stats: bool = True, + access_key: str = "minioadmin", + secret_key: str = "minioadmin", + bucket_name: str = "a-bucket", + **kwargs, + ): + self.milvus_kwargs = locals() + self.milvus_kwargs.pop("self") + self.collection_name = self.milvus_kwargs.pop("collection_name") + self.milvus_kwargs.pop("kwargs", None) + + def get_connection_params(self): + conn_dict = { + "milvus_uri": self.milvus_kwargs["milvus_uri"], + "sparse": self.milvus_kwargs["sparse"], + "recreate": self.milvus_kwargs["recreate"], + "gpu_index": self.milvus_kwargs["gpu_index"], + "gpu_search": self.milvus_kwargs["gpu_search"], + "dense_dim": self.milvus_kwargs["dense_dim"], + } + return (self.collection_name, conn_dict) + + def get_write_params(self): + write_params = self.milvus_kwargs.copy() + del write_params["recreate"] + del write_params["gpu_index"] + del write_params["gpu_search"] + del write_params["dense_dim"] + + return (self.collection_name, write_params) + + def run(self, records): + collection_name, create_params = self.get_connection_params() + _, write_params = self.get_write_params() + if isinstance(collection_name, str): + create_nvingest_collection(collection_name, **create_params) + write_to_nvingest_collection(records, collection_name, **write_params) + elif isinstance(collection_name, dict): + split_params_list = _dict_to_params(collection_name, write_params) + for sub_params in split_params_list: + coll_name, sub_write_params = sub_params + create_nvingest_collection(coll_name, **create_params) + write_to_nvingest_collection(records, coll_name, **sub_write_params) + else: + raise ValueError(f"Unsupported type for collection_name detected: {type(collection_name)}") def create_nvingest_schema(dense_dim: int = 1024, sparse: bool = False) -> CollectionSchema: @@ -54,7 +135,9 @@ def create_nvingest_schema(dense_dim: int = 1024, sparse: bool = False) -> Colle return schema -def create_nvingest_index_params(sparse: bool = False, gpu_index: bool = True, gpu_search: bool = False) -> IndexParams: +def create_nvingest_index_params( + sparse: bool = False, gpu_index: bool = True, gpu_search: bool = False, local_index: bool = True +) -> IndexParams: """ Creates index params necessary to create an index for a collection. At a minimum, this function will create a dense embedding index but can also create a sparse @@ -78,27 +161,35 @@ def create_nvingest_index_params(sparse: bool = False, gpu_index: bool = True, g embedding index. """ index_params = MilvusClient.prepare_index_params() - if gpu_index: + if local_index: index_params.add_index( field_name="vector", index_name="dense_index", - index_type="GPU_CAGRA", + index_type="FLAT", metric_type="L2", - params={ - "intermediate_graph_degree": 128, - "graph_degree": 64, - "build_algo": "NN_DESCENT", - "adapt_for_cpu": "false" if gpu_search else "true", - }, ) else: - index_params.add_index( - field_name="vector", - index_name="dense_index", - index_type="HNSW", - metric_type="L2", - params={"M": 64, "efConstruction": 512}, - ) + if gpu_index: + index_params.add_index( + field_name="vector", + index_name="dense_index", + index_type="GPU_CAGRA", + metric_type="L2", + params={ + "intermediate_graph_degree": 128, + "graph_degree": 64, + "build_algo": "NN_DESCENT", + "adapt_for_cpu": "false" if gpu_search else "true", + }, + ) + else: + index_params.add_index( + field_name="vector", + index_name="dense_index", + index_type="HNSW", + metric_type="L2", + params={"M": 64, "efConstruction": 512}, + ) if sparse: index_params.add_index( field_name="sparse", @@ -178,6 +269,7 @@ def create_nvingest_collection( Returns a milvus collection schema, that represents the fields in the created collection. """ + local_index = False if urlparse(milvus_uri).scheme: connections.connect(uri=milvus_uri) server_version = utility.get_server_version() @@ -185,9 +277,14 @@ def create_nvingest_collection( gpu_index = False else: gpu_index = False + if milvus_uri.endswith(".db"): + local_index = True + client = MilvusClient(milvus_uri) schema = create_nvingest_schema(dense_dim=dense_dim, sparse=sparse) - index_params = create_nvingest_index_params(sparse=sparse, gpu_index=gpu_index, gpu_search=gpu_search) + index_params = create_nvingest_index_params( + sparse=sparse, gpu_index=gpu_index, gpu_search=gpu_search, local_index=local_index + ) create_collection(client, collection_name, schema, index_params, recreate=recreate) @@ -398,11 +495,12 @@ def write_to_nvingest_collection( collection_name: str, milvus_uri: str = "http://localhost:19530", minio_endpoint: str = "localhost:9000", - sparse: bool = False, + sparse: bool = True, enable_text: bool = True, enable_charts: bool = True, enable_tables: bool = True, bm25_save_path: str = "bm25_model.json", + compute_bm25_stats: bool = True, access_key: str = "minioadmin", secret_key: str = "minioadmin", bucket_name: str = "a-bucket", @@ -449,11 +547,14 @@ def write_to_nvingest_collection( else: stream = True bm25_ef = None - if sparse: + if sparse and compute_bm25_stats: bm25_ef = create_bm25_model( records, enable_text=enable_text, enable_charts=enable_charts, enable_tables=enable_tables ) bm25_ef.save(bm25_save_path) + elif sparse and not compute_bm25_stats: + bm25_ef = BM25EmbeddingFunction(build_default_analyzer(language="en")) + bm25_ef.load(bm25_save_path) client = MilvusClient(milvus_uri) schema = Collection(collection_name).schema if stream: @@ -535,7 +636,6 @@ def dense_retrieval( collection_name=collection_name, data=dense_embeddings, anns_field=dense_field, - param={"metric_type": "L2"}, limit=top_k, output_fields=output_fields, ) @@ -552,6 +652,8 @@ def hybrid_retrieval( dense_field: str = "vector", sparse_field: str = "sparse", output_fields: List[str] = ["text"], + gpu_search: bool = False, + local_index: bool = False, ): """ This function takes the input queries and conducts a hybrid @@ -591,22 +693,27 @@ def hybrid_retrieval( dense_embeddings.append(dense_model.get_query_embedding(query)) sparse_embeddings.append(_format_sparse_embedding(sparse_model.encode_queries([query]))) + s_param_1 = { + "metric_type": "L2", + } + if not gpu_search and not local_index: + s_param_1["params"] = {"ef": top_k * 2} + # Create search requests for both vector types search_param_1 = { "data": dense_embeddings, "anns_field": dense_field, - "param": { - "metric_type": "L2", - }, - "limit": top_k, + "param": s_param_1, + "limit": top_k * 2, } + dense_req = AnnSearchRequest(**search_param_1) search_param_2 = { "data": sparse_embeddings, "anns_field": sparse_field, "param": {"metric_type": "IP", "params": {"drop_ratio_build": 0.2}}, - "limit": top_k, + "limit": top_k * 2, } sparse_req = AnnSearchRequest(**search_param_2) @@ -628,6 +735,7 @@ def nvingest_retrieval( sparse_model_filepath: str = "bm25_model.json", model_name: str = "nvidia/nv-embedqa-e5-v5", output_fields: List[str] = ["text", "source", "content_metadata"], + gpu_search: bool = False, ): """ This function takes the input queries and conducts a hybrid/dense @@ -665,14 +773,24 @@ def nvingest_retrieval( List Nested list of top_k results per query. """ + local_index = False embed_model = NVIDIAEmbedding(base_url=embedding_endpoint, model=model_name) client = MilvusClient(milvus_uri) - + if milvus_uri.endswith(".db"): + local_index = True if hybrid: bm25_ef = BM25EmbeddingFunction(build_default_analyzer(language="en")) bm25_ef.load(sparse_model_filepath) results = hybrid_retrieval( - queries, collection_name, client, embed_model, bm25_ef, top_k, output_fields=output_fields + queries, + collection_name, + client, + embed_model, + bm25_ef, + top_k, + output_fields=output_fields, + gpu_search=gpu_search, + local_index=local_index, ) else: results = dense_retrieval(queries, collection_name, client, embed_model, top_k, output_fields=output_fields) diff --git a/docs/docs/user-guide/developer-guide/kubernetes-dev.md b/docs/docs/user-guide/developer-guide/kubernetes-dev.md index 0254d385..8051ffd4 100644 --- a/docs/docs/user-guide/developer-guide/kubernetes-dev.md +++ b/docs/docs/user-guide/developer-guide/kubernetes-dev.md @@ -8,16 +8,16 @@ This page describes how to use Kubernetes generally, and how to deploy nv-ingest ## Kubernetes Cluster -To start you need a Kubernetes cluster. We recommend using `kind` that creates a single Docker container with a Kubernetes cluster inside it. +To start you need a Kubernetes cluster. We recommend that you use `kind`, which creates a single Docker container with a Kubernetes cluster inside it. -Also, because this the `kind` cluster needs access to the GPUs on your system you need to install `kind-with-gpus`. The easiest way to do this is following the instructions laid out in this GitHub repo https://github.com/klueska/kind-with-gpus-examples/tree/master +Because the `kind` cluster needs access to the GPUs on your system, you need to install `nvkind`. +For details, see [Running kind clusters with GPUs using nvkind](https://github.com/NVIDIA/nvkind/tree/main). +`nvkind` provides the following benefits: -Benefits of this: +- Multiple developers on the same system can have isolated Kubernetes clusters +- Easy to create and delete clusters -- Allows many developers on the same system to have isolated Kubernetes clusters -- Enables easy creation and deletion of clusters - -Run the following **from the root of the repo** to create a configuration file for your cluster. +From the root of the repo, run the following code to create a configuration file for your cluster. ```yaml mkdir -p ./.tmp @@ -80,10 +80,10 @@ docker ps | grep kind # aaf5216a3cc8 kindest/node:v1.27.11 "/usr/local/bin/entr…" 44 seconds ago Up 42 seconds 127.0.0.1:45099->6443/tcp jdyer-control-plane ``` -`kind create cluster` will do the following: +`kind create cluster` does the following: -- add a context for this cluster to `${HOME}/.kube/config`, the default config file used by tools like `kubectl` -- change the default context to that one +- Add a context for the cluster to `${HOME}/.kube/config`, the default config file used by tools like `kubectl` +- Change the default context to `${HOME}/.kube/config` You should be able to use `kubectl` immediately, and it should be pointed at that cluster you just created. @@ -100,22 +100,23 @@ NAME STATUS ROLES AGE VERSION jdyer-control-plane Ready control-plane 63s v1.27.11 ``` -Note: All of the containers created inside your Kubernetes cluster will not show up when you run `docker ps` as they are nested within a separate containerd namespace. +Note: Not all of the containers created inside your Kubernetes cluster appear when you run `docker ps` +because some of them are are nested within a separate namespace. + +For help with issues that arise, see [Troubleshooting](#troubleshooting). -Refer to "debugging tools" in the "Troubleshooting" section. ## Skaffold -Now that you have a Kubernetes cluster, you can use `skaffold` to build and deploy your development environment. +Now that you have a Kubernetes cluster, you can use [Skaffold](https://skaffold.dev/) to build and deploy your development environment. -Skaffold does a few things for you in a single command: +In a single command, Skaffold does the following: -- Build containers from the current directory (via `docker build`). -- Install the retriever-ingest helm charts (via `helm install`). -- Apply additional Kubernetes manifests (via `kustomize`). -- Hot reloading - skaffold watches your local directory for changes and syncs them into the Kubernetes container. - - _for details on this, see "Hot reloading" below ([link](#hot-reloading))_ -- Port forwards the -ingest service to the host. +- Build containers from the current directory (via `docker build`) +- Install the retriever-ingest helm charts (via `helm install`) +- Apply additional Kubernetes manifests (via `kustomize`) +- Hot reloading - Skaffold watches your local directory for changes and syncs them into the Kubernetes container +- Port forwards the ingest service to the host ### Directory Structure @@ -140,7 +141,9 @@ Skaffold does a few things for you in a single command: The retriever-ingest service's deployment requires pulling in configurations for other services from third-party sources, for example, Elasticsearch, OpenTelemetry, and Postgres. -The first time you try to deploy this project to a local Kubernetes, you may need to tell your local version of `Helm` (a package manager for Kubernetes configurations) where to find those third-party things, by running something like the following. +The first time you deploy this project to a local Kubernetes, +you might need to tell your local version of `Helm` (a package manager for Kubernetes configurations) +where to find third-party services by running code similar to the following. ```shell helm repo add \ @@ -164,11 +167,12 @@ helm repo add \ https://charts.bitnami.com/bitnami ``` -For the full list of repositories, refer to the `dependencies` section in [this project's Chart.yaml](../../helm/Chart.yaml). +For the full list of repositories, refer to the `dependencies` section in the [Chart.yaml](../../../../helm/Chart.yaml) file of this project. #### NVIDIA GPU Support -In order for the deployed Kubernetes pods to access the NVIDIA GPU resources, the [Nvidia k8s-device-plugin](https://github.com/NVIDIA/k8s-device-plugin) must be installed. There are a multitude of configurations for this plugin but for a straight forward route to start development you can simply run. +For the Kubernetes pods to access the NVIDIA GPU resources, you must install the [NVIDIA device plugin for Kubernetes](https://github.com/NVIDIA/k8s-device-plugin). +There are many configurations for this plugin, but to start development simply run the following code. ```shell kubectl create -f https://raw.githubusercontent.com/NVIDIA/k8s-device-plugin/v0.15.0/deployments/static/nvidia-device-plugin.yml @@ -197,8 +201,9 @@ data: EOF ``` -An NGC personal API key is needed to access models and images hosted on NGC. -Make sure that you have followed the steps of _[Ensure you have access to NGC](./index.md#ensure-you-have-access-to-ngc)_. Next, store the key in an environment variable: +You need an NGC personal API key to access models and images that are hosted on NGC. +First, [Generate an API key](ngc-api-key.md#generate-an-api-key). +Next, store the key in an environment variable by running the following code. ```shell export NGC_API_KEY="" @@ -253,9 +258,10 @@ Deployments stabilized in 23.08 seconds Watching for changes... ``` -When you run this command, `skaffold dev` finds a random open port on the system and exposes the retriever-ingest service on that port ([skaffold docs](https://skaffold.dev/docs/port-forwarding/)). +When you run this command, `skaffold dev` finds a random open port on the system and exposes the retriever-ingest service on that port. +For more information, see [Port Forwarding](https://skaffold.dev/docs/port-forwarding/). -You can find that port in `skaffold`'s logs, in a statement like this: +You can find that port in `skaffold`'s logs by running the following code. ```bash Port forwarding Service/nv-ingest in namespace , remote port http -> http://0.0.0.0:4503 @@ -283,7 +289,9 @@ curl \ "${API_HOST}/health" ``` -Additionally, running `skaffold verify` in a new terminal will run verification tests against the service ([integration tests](https://skaffold.dev/docs/verify/)). These are very lightweight health checks, and should not be confused with actual integration tests. +When you run `skaffold verify` in a new terminal, Skaffold runs verification tests against the service. +These are very lightweight health checks, and should not be confused with integration tests. +For more information, see [Verify](https://skaffold.dev/docs/verify/). ## Clean Up @@ -320,28 +328,25 @@ kubectl exec \ -- sh ``` -For an interactive, live-updating experience, try `k9s`. +For an interactive, live-updating experience, try [k9s](https://k9scli.io/). To launch it, run `k9s`. ```shell k9s ``` -You should see something like the following. - -![k9s example](./media/k9s-example.png){width=80%} - -For details on how to use it, refer to https://k9scli.io/topics/commands/. ### Installing Helm Repositories -You could encounter an error like this: +You could encounter an error like the following. +This indicates that your local installation of `Helm` (a package manager for Kubernetes configurations) +doesn't know how to access a remote repository containing Kubernetes configurations. -> _Error: no repository definition for https://helm.dask.org. Please add the missing repos via 'helm repo add'_ - -This indicates that your local installation of `Helm` (sort of a package manager for Kubernetes configurations) doesn't know how to access a remote repository containing Kubernetes configurations. +```shell +Error: no repository definition for https://helm.dask.org. Please add the missing repos via 'helm repo add' +``` -As that error message says, run `help repo add` with that URL and an informative name. +To resolve this issue, run `help repo add` with the URL and an informative name. ```shell helm repo add \ @@ -363,12 +368,11 @@ Cleaning up... building helm dependencies: exit status 1 ``` -Seeing only "building helm dependencies" likely means you ran `skaffold dev` or `skaffold run` in a fairly quiet mode. - -Rerun those commands with something like `-v info` or `-v debug` to get more information about what specifically failed. +If you only see `building helm dependencies`, you probably ran `skaffold dev` or `skaffold run` in quiet mode. +Rerun the commands with `-v info` or `-v debug` to get more information about what failed. ## References -- Helm quickstart: https://helm.sh/docs/intro/quickstart/ -- `kind` docs: https://kind.sigs.k8s.io/ -- `skaffold` docs: https://skaffold.dev/docs/ +- [Helm Quickstart](https://helm.sh/docs/intro/quickstart/) +- [Kind Quickstart](https://kind.sigs.k8s.io/) +- [Skaffold Quickstart](https://skaffold.dev/docs/quickstart) diff --git a/src/nv_ingest/api/v1/ingest.py b/src/nv_ingest/api/v1/ingest.py index 7618f078..628f3f79 100644 --- a/src/nv_ingest/api/v1/ingest.py +++ b/src/nv_ingest/api/v1/ingest.py @@ -10,27 +10,33 @@ # pylint: skip-file +from io import BytesIO +from typing import Annotated, Dict, List import base64 import json import logging import time import traceback -from io import BytesIO -from typing import Annotated +import uuid from fastapi import APIRouter, Request, Response from fastapi import Depends -from fastapi import File +from fastapi import File, UploadFile, Form from fastapi import HTTPException -from fastapi import UploadFile +from fastapi.responses import JSONResponse from nv_ingest_client.primitives.jobs.job_spec import JobSpec from nv_ingest_client.primitives.tasks.extract import ExtractTask from opentelemetry import trace from redis import RedisError +from nv_ingest.util.converters.formats import ingest_json_results_to_blob + from nv_ingest.schemas.message_wrapper_schema import MessageWrapper +from nv_ingest.schemas.processing_job_schema import ConversionStatus, ProcessingJob from nv_ingest.service.impl.ingest.redis_ingest_service import RedisIngestService from nv_ingest.service.meta.ingest.ingest_service_meta import IngestServiceMeta +from nv_ingest_client.primitives.tasks.table_extraction import TableExtractionTask +from nv_ingest_client.primitives.tasks.chart_extraction import ChartExtractionTask logger = logging.getLogger("uvicorn") tracer = trace.get_tracer(__name__) @@ -184,3 +190,160 @@ async def fetch_job(job_id: str, ingest_service: INGEST_SERVICE_T): # Catch-all for other exceptions, returning a 500 Internal Server Error traceback.print_exc() raise HTTPException(status_code=500, detail=f"Nv-Ingest Internal Server Error: {str(ex)}") + + +@router.post("/convert") +async def convert_pdf( + ingest_service: INGEST_SERVICE_T, + files: List[UploadFile] = File(...), + job_id: str = Form(...), + extract_text: bool = Form(True), + extract_images: bool = Form(True), + extract_tables: bool = Form(True), + extract_charts: bool = Form(False), +) -> Dict[str, str]: + try: + + if job_id is None: + job_id = str(uuid.uuid4()) + logger.debug(f"JobId is None, Created JobId: {job_id}") + + submitted_jobs: List[ProcessingJob] = [] + for file in files: + file_stream = BytesIO(file.file.read()) + doc_content = base64.b64encode(file_stream.read()).decode("utf-8") + + try: + content_type = file.content_type.split("/")[1] + except Exception: + err_message = f"Unsupported content_type: {file.content_type}" + logger.error(err_message) + raise HTTPException(status_code=500, detail=err_message) + + job_spec = JobSpec( + document_type=content_type, + payload=doc_content, + source_id=file.filename, + source_name=file.filename, + extended_options={ + "tracing_options": { + "trace": True, + "ts_send": time.time_ns(), + } + }, + ) + + extract_task = ExtractTask( + document_type=content_type, + extract_text=extract_text, + extract_images=extract_images, + extract_tables=extract_tables, + extract_charts=extract_charts, + ) + + job_spec.add_task(extract_task) + + # Conditionally add tasks as needed. + if extract_tables: + table_data_extract = TableExtractionTask() + job_spec.add_task(table_data_extract) + + if extract_charts: + chart_data_extract = ChartExtractionTask() + job_spec.add_task(chart_data_extract) + + submitted_job_id = await ingest_service.submit_job( + MessageWrapper(payload=json.dumps(job_spec.to_dict())), job_id + ) + + processing_job = ProcessingJob( + submitted_job_id=submitted_job_id, + filename=file.filename, + status=ConversionStatus.IN_PROGRESS, + ) + + submitted_jobs.append(processing_job) + + await ingest_service.set_processing_cache(job_id, submitted_jobs) + + logger.debug(f"Submitted: {len(submitted_jobs)} documents of type: '{content_type}' for processing") + + return { + "task_id": job_id, + "status": "processing", + "status_url": f"/status/{job_id}", + } + + except Exception as e: + logger.error(f"Error starting conversion: {str(e)}") + raise HTTPException(status_code=500, detail=str(e)) + + +@router.get("/status/{job_id}") +async def get_status(ingest_service: INGEST_SERVICE_T, job_id: str): + t_start = time.time() + try: + processing_jobs = await ingest_service.get_processing_cache(job_id) + except Exception as e: + logger.error(f"Error getting status: {str(e)}") + raise HTTPException(status_code=500, detail=str(e)) + + updated_cache: List[ProcessingJob] = [] + num_ready_docs = 0 + + for processing_job in processing_jobs: + logger.debug(f"submitted_job_id: {processing_job.submitted_job_id} - Status: {processing_job.status}") + + if processing_job.status == ConversionStatus.IN_PROGRESS: + # Attempt to fetch the job from the ingest service + try: + job_response = await ingest_service.fetch_job(processing_job.submitted_job_id) + + job_response = json.dumps(job_response) + + # Convert JSON into pseudo markdown format + blob_response = ingest_json_results_to_blob(job_response) + + processing_job.raw_result = job_response + processing_job.content = blob_response + processing_job.status = ConversionStatus.SUCCESS + num_ready_docs = num_ready_docs + 1 + updated_cache.append(processing_job) + + except TimeoutError: + logger.error(f"TimeoutError getting result for job_id: {processing_job.submitted_job_id}") + updated_cache.append(processing_job) + continue + except RedisError: + logger.error(f"RedisError getting result for job_id: {processing_job.submitted_job_id}") + updated_cache.append(processing_job) + continue + else: + logger.debug(f"{processing_job.submitted_job_id} has already finished successfully ....") + num_ready_docs = num_ready_docs + 1 + updated_cache.append(processing_job) + + await ingest_service.set_processing_cache(job_id, updated_cache) + + logger.debug(f"{num_ready_docs}/{len(updated_cache)} complete") + if num_ready_docs == len(updated_cache): + results = [] + raw_results = [] + for result in updated_cache: + results.append( + { + "filename": result.filename, + "status": "success", + "content": result.content, + } + ) + raw_results.append(result.raw_result) + + return JSONResponse( + content={"status": "completed", "result": results}, + status_code=200, + ) + else: + # Not yet ready ... + logger.debug(f"/status/{job_id} endpoint execution time: {time.time() - t_start}") + raise HTTPException(status_code=202, detail="Job is not ready yet. Retry later.") diff --git a/src/nv_ingest/extraction_workflows/docx/docx_helper.py b/src/nv_ingest/extraction_workflows/docx/docx_helper.py index 6bfa12b6..44a946ad 100644 --- a/src/nv_ingest/extraction_workflows/docx/docx_helper.py +++ b/src/nv_ingest/extraction_workflows/docx/docx_helper.py @@ -36,7 +36,14 @@ logger = logging.getLogger(__name__) -def python_docx(docx: Union[str, Path, IO], extract_text: bool, extract_images: bool, extract_tables: bool, **kwargs): +def python_docx( + docx: Union[str, Path, IO], + extract_text: bool, + extract_images: bool, + extract_tables: bool, + extract_charts: bool, + **kwargs +): """ Helper function that use python-docx to extract text from a bytestream document @@ -57,6 +64,8 @@ def python_docx(docx: Union[str, Path, IO], extract_text: bool, extract_images: Specifies whether to extract images. extract_tables : bool Specifies whether to extract tables. + extract_charts : bool + Specifies whether to extract charts. **kwargs The keyword arguments are used for additional extraction parameters. @@ -73,10 +82,12 @@ def python_docx(docx: Union[str, Path, IO], extract_text: bool, extract_images: source_id = row_data["source_id"] # get text_depth text_depth = kwargs.get("text_depth", "document") - text_depth = TextTypeEnum[text_depth.upper()] + text_depth = TextTypeEnum(text_depth) # get base metadata metadata_col = kwargs.get("metadata_column", "metadata") + docx_extractor_config = kwargs.get("docx_extraction_config", {}) + base_unified_metadata = row_data[metadata_col] if metadata_col in row_data.index else {} # get base source_metadata @@ -103,7 +114,9 @@ def python_docx(docx: Union[str, Path, IO], extract_text: bool, extract_images: } # Extract data from the document using python-docx - doc = DocxReader(docx, source_metadata) - extracted_data = doc.extract_data(base_unified_metadata, text_depth, extract_text, extract_tables, extract_images) + doc = DocxReader(docx, source_metadata, extraction_config=docx_extractor_config) + extracted_data = doc.extract_data( + base_unified_metadata, text_depth, extract_text, extract_charts, extract_tables, extract_images + ) return extracted_data diff --git a/src/nv_ingest/extraction_workflows/docx/docxreader.py b/src/nv_ingest/extraction_workflows/docx/docxreader.py index b550d936..b2920203 100644 --- a/src/nv_ingest/extraction_workflows/docx/docxreader.py +++ b/src/nv_ingest/extraction_workflows/docx/docxreader.py @@ -23,14 +23,16 @@ """ Parse document content and properties using python-docx """ - +import io import logging import re import uuid -from typing import Dict +from typing import Dict, Optional, Union from typing import List from typing import Tuple +from collections import defaultdict + import pandas as pd from docx import Document from docx.image.constants import MIME_TYPE @@ -42,7 +44,11 @@ from docx.text.hyperlink import Hyperlink from docx.text.paragraph import Paragraph from docx.text.run import Run +from pandas import DataFrame +from build.lib.nv_ingest.extraction_workflows.image.image_handlers import load_and_preprocess_image +from nv_ingest.extraction_workflows.image.image_handlers import extract_tables_and_charts_from_images +from nv_ingest.schemas.image_extractor_schema import ImageConfigSchema from nv_ingest.schemas.metadata_schema import ContentTypeEnum from nv_ingest.schemas.metadata_schema import ImageTypeEnum from nv_ingest.schemas.metadata_schema import StdContentDescEnum @@ -50,6 +56,7 @@ from nv_ingest.schemas.metadata_schema import validate_metadata from nv_ingest.util.converters import bytetools from nv_ingest.util.detectors.language import detect_language +from nv_ingest.util.pdf.metadata_aggregators import construct_table_and_chart_metadata, CroppedImageWithContent PARAGRAPH_FORMATS = ["text", "markdown"] TABLE_FORMATS = ["markdown", "markdown_light", "csv", "tag"] @@ -92,7 +99,7 @@ def __str__(self): def _update_source_meta_data(self): """ - Update the source meta data with the document's core properties + Update the source metadata with the document's core properties """ self.source_metadata.update( { @@ -132,9 +139,11 @@ def __init__( handle_text_styles: bool = True, image_tag="", table_tag="", + extraction_config: Dict = None, ): if paragraph_format not in PARAGRAPH_FORMATS: raise ValueError(f"Unknown paragraph format {paragraph_format}. Supported formats are: {PARAGRAPH_FORMATS}") + if table_format not in TABLE_FORMATS: raise ValueError(f"Unknown table format {table_format}. Supported formats are: {TABLE_FORMATS}") @@ -161,18 +170,47 @@ def __init__( # placeholders for metadata extraction self._accumulated_text = [] self._extracted_data = [] - self._prev_para_images = [] + self._extraction_config = extraction_config if extraction_config else {} + self._pending_images = [] self._prev_para_image_idx = 0 + self._prev_para_images = [] def is_text_empty(self, text: str) -> bool: """ - Check if text is available + Check if the given text is empty or matches the empty text pattern. + + Parameters + ---------- + text : str + The text to check. + + Returns + ------- + bool + True if the text is empty or matches the empty text pattern, False otherwise. """ + return self.empty_text_pattern.match(text) is not None - def format_text(self, text, bold: bool, italic: bool, underline: bool) -> str: + def format_text(self, text: str, bold: bool, italic: bool, underline: bool) -> str: """ - Apply markdown style to text (bold, italic, underline). + Apply markdown styling (bold, italic, underline) to the given text. + + Parameters + ---------- + text : str + The text to format. + bold : bool + Whether to apply bold styling. + italic : bool + Whether to apply italic styling. + underline : bool + Whether to apply underline styling. + + Returns + ------- + str + The formatted text with the applied styles. """ if self.is_text_empty(text): @@ -198,9 +236,20 @@ def format_text(self, text, bold: bool, italic: bool, underline: bool) -> str: return text - def format_paragraph(self, paragraph: Paragraph) -> Tuple[str, List[Image]]: - f""" - Format a paragraph into text. Supported formats are: {PARAGRAPH_FORMATS} + def format_paragraph(self, paragraph: "Paragraph") -> Tuple[str, List["Image"]]: + """ + Format a paragraph into styled text and extract associated images. + + Parameters + ---------- + paragraph : Paragraph + The paragraph to format. This includes text and potentially embedded images. + + Returns + ------- + tuple of (str, list of Image) + - The formatted paragraph text with markdown styling applied. + - A list of extracted images from the paragraph. """ paragraph_images = [] @@ -257,10 +306,22 @@ def format_paragraph(self, paragraph: Paragraph) -> Tuple[str, List[Image]]: paragraph_text = paragraph_text.strip() return paragraph_text, paragraph_images - def format_cell(self, cell: _Cell) -> Tuple[str, List[Image]]: + def format_cell(self, cell: "_Cell") -> Tuple[str, List["Image"]]: """ - Format a table cell into markdown text + Format a table cell into Markdown text and extract associated images. + + Parameters + ---------- + cell : _Cell + The table cell to format. + + Returns + ------- + tuple of (str, list of Image) + - The formatted text of the cell with markdown styling applied. + - A list of images extracted from the cell. """ + if self.paragraph_format == "markdown": newline = "
" else: @@ -268,10 +329,23 @@ def format_cell(self, cell: _Cell) -> Tuple[str, List[Image]]: paragraph_texts, paragraph_images = zip(*[self.format_paragraph(p) for p in cell.paragraphs]) return newline.join(paragraph_texts), paragraph_images - def format_table(self, table: Table) -> Tuple[str, List[Image]]: - f""" - Format a table into text. Supported formats are: {TABLE_FORMATS} + def format_table(self, table: "Table") -> Tuple[Optional[str], List["Image"], DataFrame]: + """ + Format a table into text, extract images, and represent it as a DataFrame. + + Parameters + ---------- + table : Table + The table to format. + + Returns + ------- + tuple of (str or None, list of Image, DataFrame) + - The formatted table as text, using the specified format (e.g., markdown, CSV). + - A list of images extracted from the table. + - A DataFrame representation of the table's content. """ + rows = [[self.format_cell(cell) for cell in row.cells] for row in table.rows] texts = [[text for text, _ in row] for row in rows] table_images = [image for row in rows for _, images in row for image in images] @@ -295,9 +369,24 @@ def format_table(self, table: Table) -> Tuple[str, List[Image]]: @staticmethod def apply_text_style(style: str, text: str, level: int = 0) -> str: """ - Apply style on a paragraph (heading, list, title, subtitle). - Not recommended if the document has been converted from pdf. + Apply a specific text style (e.g., heading, list, title, subtitle) to the given text. + + Parameters + ---------- + style : str + The style to apply. Supported styles include headings ("Heading 1" to "Heading 9"), + list items ("List"), and document structures ("Title", "Subtitle"). + text : str + The text to style. + level : int, optional + The indentation level for the styled text. Default is 0. + + Returns + ------- + str + The text with the specified style and indentation applied. """ + if re.match(r"^Heading [1-9]$", style): n = int(style.split(" ")[-1]) text = f"{'#' * n} {text}" @@ -313,43 +402,62 @@ def apply_text_style(style: str, text: str, level: int = 0) -> str: return text @staticmethod - def docx_content_type_to_image_type(content_type: MIME_TYPE) -> str: + def docx_content_type_to_image_type(content_type: "MIME_TYPE") -> str: """ - python-docx stores the content type in the image header as a string of format - "image/jpeg" etc. This is converted into one of ImageTypeEnum. - Reference: src/docx/image/jpeg.py + Convert a DOCX content type string to an image type. + + Parameters + ---------- + content_type : MIME_TYPE + The content type string from the image header, e.g., "image/jpeg". + + Returns + ------- + str + The image type extracted from the content type string. """ + return content_type.split("/")[1] - def _construct_image_metadata(self, image, para_idx, caption, base_unified_metadata): + def _construct_image_metadata( + self, para_idx: int, caption: str, base_unified_metadata: Dict, base64_img: str + ) -> List[Union[str, dict]]: """ - Fill the metadata for the extracted image + Build metadata for an image in a DOCX file. + + Parameters + ---------- + para_idx : int + The paragraph index containing the image. + caption : str + The caption associated with the image. + base_unified_metadata : dict + The base metadata to build upon. + base64_img : str + The image content encoded as a base64 string. + + Returns + ------- + list + A list containing the content type, validated metadata, and a unique identifier. """ - image_type = self.docx_content_type_to_image_type(image.content_type) - if ImageTypeEnum.has_value(image_type): - image_type = ImageTypeEnum[image_type.upper()] - - base64_img = bytetools.base64frombytes(image.blob) - # For docx there is no bounding box. The paragraph that follows the image is typically - # the caption. Add that para to the page nearby for now. fixme bbox = (0, 0, 0, 0) + caption_len = len(caption.splitlines()) + + page_idx = 0 # docx => single page + page_count = 1 + page_nearby_blocks = { "text": {"content": [], "bbox": []}, "images": {"content": [], "bbox": []}, "structured": {"content": [], "bbox": []}, } - caption_len = len(caption.splitlines()) + if caption_len: page_nearby_blocks["text"]["content"].append(caption) page_nearby_blocks["text"]["bbox"] = [[-1, -1, -1, -1]] * caption_len - page_block = para_idx - - # python-docx treats the entire document as a single page - page_count = 1 - page_idx = 0 - content_metadata = { "type": ContentTypeEnum.IMAGE, "description": StdContentDescEnum.DOCX_IMAGE, @@ -357,16 +465,15 @@ def _construct_image_metadata(self, image, para_idx, caption, base_unified_metad "hierarchy": { "page_count": page_count, "page": page_idx, - "block": page_block, + "block": para_idx, "line": -1, "span": -1, "nearby_objects": page_nearby_blocks, }, } - # bbox is not available in docx. the para following the image is typically the caption. image_metadata = { - "image_type": image_type, + "image_type": ImageTypeEnum.image_type_1, "structured_image_type": ImageTypeEnum.image_type_1, "caption": caption, "text": "", @@ -374,7 +481,6 @@ def _construct_image_metadata(self, image, para_idx, caption, base_unified_metad } unified_metadata = base_unified_metadata.copy() - unified_metadata.update( { "content": base64_img, @@ -386,24 +492,64 @@ def _construct_image_metadata(self, image, para_idx, caption, base_unified_metad validated_unified_metadata = validate_metadata(unified_metadata) - # Work around until https://github.com/apache/arrow/pull/40412 is resolved - return [ContentTypeEnum.IMAGE.value, validated_unified_metadata.model_dump(), str(uuid.uuid4())] + return [ + ContentTypeEnum.IMAGE.value, + validated_unified_metadata.model_dump(), + str(uuid.uuid4()), + ] - def _extract_para_images(self, images, para_idx, caption, base_unified_metadata, extracted_data): + def _extract_para_images( + self, images: List["Image"], para_idx: int, caption: str, base_unified_metadata: Dict + ) -> None: """ - Extract all images in a paragraph. These images share the same metadata. + Collect images from a paragraph and store them for metadata construction. + + Parameters + ---------- + images : list of Image + The images found in the paragraph. + para_idx : int + The index of the paragraph containing the images. + caption : str + The caption associated with the images. + base_unified_metadata : dict + The base metadata to associate with the images. + + Returns + ------- + None """ + for image in images: logger.debug("image content_type %s para_idx %d", image.content_type, para_idx) logger.debug("image caption %s", caption) - extracted_image = self._construct_image_metadata(image, para_idx, caption, base_unified_metadata) - extracted_data.append(extracted_image) - def _construct_text_metadata(self, accumulated_text, para_idx, text_depth, base_unified_metadata): + # Simply append a tuple so we can build the final metadata in _finalize_images + self._pending_images.append((image, para_idx, caption, base_unified_metadata)) + + def _construct_text_metadata( + self, accumulated_text: List[str], para_idx: int, text_depth: "TextTypeEnum", base_unified_metadata: Dict + ) -> List[Union[str, dict]]: """ - Store the text with associated metadata. Docx uses the same scheme as - PDF. + Build metadata for text content in a DOCX file. + + Parameters + ---------- + accumulated_text : list of str + The accumulated text to include in the metadata. + para_idx : int + The paragraph index containing the text. + text_depth : TextTypeEnum + The depth of the text content (e.g., page-level, paragraph-level). + base_unified_metadata : dict + The base metadata to build upon. + + Returns + ------- + list + A list containing the content type, validated metadata, and a unique identifier. """ + if len(accumulated_text) < 1: return [] @@ -447,36 +593,37 @@ def _construct_text_metadata(self, accumulated_text, para_idx, text_depth, base_ return [ContentTypeEnum.TEXT.value, validated_unified_metadata.model_dump(), str(uuid.uuid4())] - def _extract_para_data( - self, child, base_unified_metadata, text_depth: TextTypeEnum, extract_images: bool, para_idx: int - ): + def _extract_para_text( + self, + paragraph, + paragraph_text, + base_unified_metadata: Dict, + text_depth: "TextTypeEnum", + para_idx: int, + ) -> None: """ - Process the text and images in a docx paragraph + Process the text, images, and styles in a DOCX paragraph. + + Parameters + ---------- + paragraph: Paragraph + The paragraph to process. + paragraph_text: str + The text content of the paragraph. + base_unified_metadata : dict + The base metadata to associate with extracted data. + text_depth : TextTypeEnum + The depth of text extraction (e.g., block-level, document-level). + para_idx : int + The index of the paragraph being processed. + + Returns + ------- + None """ - # Paragraph - paragraph = Paragraph(child, self.document) - paragraph_text, paragraph_images = self.format_paragraph(paragraph) - - if self._prev_para_images: - # build image metadata with image from previous paragraph and text from current - self._extract_para_images( - self._prev_para_images, - self._prev_para_image_idx, - paragraph_text, - base_unified_metadata, - self._extracted_data, - ) - self._prev_para_images = [] - - if extract_images and paragraph_images: - # cache the images till the next paragraph is read - self._prev_para_images = paragraph_images - self._prev_para_image_idx = para_idx - - self.images += paragraph_images + # Handle text styles if desired if self.handle_text_styles: - # Get the level of the paragraph (especially for lists) try: numPr = paragraph._element.xpath("./w:pPr/w:numPr")[0] level = int(numPr.xpath("./w:ilvl/@w:val")[0]) @@ -486,6 +633,7 @@ def _extract_para_data( self._accumulated_text.append(paragraph_text + "\n") + # If text_depth is BLOCK, we flush after each paragraph if text_depth == TextTypeEnum.BLOCK: text_extraction = self._construct_text_metadata( self._accumulated_text, para_idx, text_depth, base_unified_metadata @@ -493,77 +641,233 @@ def _extract_para_data( self._extracted_data.append(text_extraction) self._accumulated_text = [] - def _extract_table_data(self, child, base_unified_metadata, text_depth: TextTypeEnum, para_idx: int): + def _finalize_images(self, extract_tables: bool, extract_charts: bool, **kwargs) -> None: + """ + Build and append final metadata for each pending image in batches. + + Parameters + ---------- + extract_tables : bool + Whether to attempt table detection in images. + extract_charts : bool + Whether to attempt chart detection in images. + **kwargs + Additional configuration for image processing. + + Returns + ------- + None + """ + if not self._pending_images: + return + + # 1) Convert all pending images into numpy arrays (and also store base64 + context), + # so we can run detection on them in one go. + all_image_arrays = [] + image_info = [] # parallel list to hold (para_idx, caption, base_unified_metadata, base64_img) + + for docx_image, para_idx, caption, base_unified_metadata in self._pending_images: + # Convert docx image blob to BytesIO, then to numpy array + image_bytes = docx_image.blob + image_stream = io.BytesIO(image_bytes) + image_array = load_and_preprocess_image(image_stream) + base64_img = str(bytetools.base64frombytes(image_bytes)) + + all_image_arrays.append(image_array) + + # Keep track of all needed metadata so we can rebuild final entries + image_info.append((para_idx, caption, base_unified_metadata, base64_img)) + + # 2) If the user wants to detect tables/charts, do it in one pass for all images. + detection_map = defaultdict(list) # maps image_index -> list of CroppedImageWithContent + + if extract_tables or extract_charts: + try: + # Perform the batched detection on all images + detection_results = extract_tables_and_charts_from_images( + images=all_image_arrays, + config=ImageConfigSchema(**self._extraction_config.model_dump()), + trace_info=kwargs.get("trace_info"), + ) + # detection_results is typically List[Tuple[int, CroppedImageWithContent]] + # Group by image_index + for image_idx, cropped_item in detection_results: + detection_map[image_idx].append(cropped_item) + + except Exception as e: + logger.error(f"Error extracting tables/charts in batch: {e}") + # If something goes wrong, we can fall back to empty detection map + # so that all images are treated normally + detection_map = {} + + # 3) For each pending image, decide if we found tables/charts or not. + for i, _ in enumerate(self._pending_images): + para_idx_i, caption_i, base_unified_metadata_i, base64_img_i = image_info[i] + + # If detection_map[i] is non-empty, we have found table(s)/chart(s). + if i in detection_map and detection_map[i]: + for table_chart_data in detection_map[i]: + # Build structured metadata for each table or chart + structured_entry = construct_table_and_chart_metadata( + structured_image=table_chart_data, # A CroppedImageWithContent + page_idx=0, # docx => single page + page_count=1, + source_metadata=self.properties.source_metadata, + base_unified_metadata=base_unified_metadata_i, + ) + self._extracted_data.append(structured_entry) + else: + # Either detection was not requested, or no table/chart was found + image_entry = self._construct_image_metadata( + para_idx_i, + caption_i, + base_unified_metadata_i, + base64_img_i, + ) + self._extracted_data.append(image_entry) + + # 4) Clear out the pending images after finalizing + self._pending_images = [] + + def _extract_table_data( + self, + child, + base_unified_metadata: Dict, + ) -> None: """ - Process the text in a docx paragraph + Process the text and images in a DOCX table. + + Parameters + ---------- + child : element + The table element to process. + base_unified_metadata : dict + The base metadata to associate with extracted data. + text_depth : TextTypeEnum + The depth of text extraction (e.g., block-level, document-level). + para_idx : int + The index of the table being processed. + + Returns + ------- + None """ + # Table table = Table(child, self.document) table_text, table_images, table_dataframe = self.format_table(table) + self.images += table_images self.tables.append(table_dataframe) - self._accumulated_text.append(table_text + "\n") - if text_depth == TextTypeEnum.BLOCK: - text_extraction = self._construct_text_metadata( - self._accumulated_text, para_idx, text_depth, base_unified_metadata + cropped_image_with_content = CroppedImageWithContent( + content=table_text, + image="", # no image content + bbox=(0, 0, 0, 0), + max_width=0, + max_height=0, + type_string="table", + ) + + self._extracted_data.append( + construct_table_and_chart_metadata( + structured_image=cropped_image_with_content, + page_idx=0, # docx => single page + page_count=1, + source_metadata=self.properties.source_metadata, + base_unified_metadata=base_unified_metadata, ) - if len(text_extraction) > 0: - self._extracted_data.append(text_extraction) - self._accumulated_text = [] + ) def extract_data( self, - base_unified_metadata, - text_depth: TextTypeEnum, + base_unified_metadata: Dict, + text_depth: "TextTypeEnum", extract_text: bool, + extract_charts: bool, extract_tables: bool, extract_images: bool, - ) -> Dict: + ) -> list[list[str | dict]]: """ - Iterate over paragraphs and tables + Iterate over paragraphs and tables in a DOCX document to extract data. + + Parameters + ---------- + base_unified_metadata : dict + The base metadata to associate with all extracted content. + text_depth : TextTypeEnum + The depth of text extraction (e.g., block-level, document-level). + extract_text : bool + Whether to extract text from the document. + extract_charts : bool + Whether to extract charts from the document. + extract_tables : bool + Whether to extract tables from the document. + extract_images : bool + Whether to extract images from the document. + + Returns + ------- + dict + A dictionary containing the extracted data from the document. """ + self._accumulated_text = [] self._extracted_data = [] - - para_idx = 0 + self._pending_images = [] self._prev_para_images = [] self._prev_para_image_idx = 0 + para_idx = 0 + for child in self.document.element.body.iterchildren(): if isinstance(child, CT_P): - if not extract_text: - continue - self._extract_para_data(child, base_unified_metadata, text_depth, extract_images, para_idx) - - if isinstance(child, CT_Tbl): - if not extract_tables: - continue - self._extract_table_data(child, base_unified_metadata, text_depth, para_idx) + paragraph = Paragraph(child, self.document) + paragraph_text, paragraph_images = self.format_paragraph(paragraph) + + if extract_text: + self._extract_para_text( + paragraph, + paragraph_text, + base_unified_metadata, + text_depth, + para_idx, + ) + + if (extract_charts or extract_images or extract_tables) and paragraph_images: + self._prev_para_images = paragraph_images + self._prev_para_image_idx = para_idx + self._pending_images += [(image, para_idx, "", base_unified_metadata) for image in paragraph_images] + self.images += paragraph_images + + elif isinstance(child, CT_Tbl): + if extract_tables or extract_charts: + self._extract_table_data(child, base_unified_metadata) para_idx += 1 - # We treat the document as a single page + # If there's leftover text at the doc’s end if ( extract_text and text_depth in (TextTypeEnum.DOCUMENT, TextTypeEnum.PAGE) and len(self._accumulated_text) > 0 ): text_extraction = self._construct_text_metadata( - self._accumulated_text, -1, text_depth, base_unified_metadata + self._accumulated_text, + -1, + text_depth, + base_unified_metadata, ) - if len(text_extraction) > 0: + + if text_extraction: self._extracted_data.append(text_extraction) - if self._prev_para_images: - # if we got here it means that image was at the end of the document and there - # was no caption for the image - self._extract_para_images( - self._prev_para_images, - self._prev_para_image_idx, - "", - base_unified_metadata, - self._extracted_data, + # Final pass: Decide if images are just images or contain tables/charts + if extract_images or extract_tables or extract_charts: + self._finalize_images( + extract_tables=extract_tables, + extract_charts=extract_charts, + trace_info=None, ) return self._extracted_data diff --git a/src/nv_ingest/extraction_workflows/image/image_handlers.py b/src/nv_ingest/extraction_workflows/image/image_handlers.py index 6181d78c..f7b12982 100644 --- a/src/nv_ingest/extraction_workflows/image/image_handlers.py +++ b/src/nv_ingest/extraction_workflows/image/image_handlers.py @@ -27,11 +27,12 @@ import numpy as np from PIL import Image +from math import log from wand.image import Image as WandImage import nv_ingest.util.nim.yolox as yolox_utils from nv_ingest.extraction_workflows.pdf.doughnut_utils import crop_image -from nv_ingest.schemas.image_extractor_schema import ImageExtractorSchema +from nv_ingest.schemas.image_extractor_schema import ImageConfigSchema from nv_ingest.schemas.metadata_schema import AccessLevelEnum from nv_ingest.util.image_processing.transforms import numpy_to_base64 from nv_ingest.util.nim.helpers import create_inference_client @@ -107,79 +108,6 @@ def convert_svg_to_bitmap(image_stream: io.BytesIO) -> np.ndarray: return image_array -# TODO(Devin): Move to common file -def process_inference_results( - output_array: np.ndarray, - original_image_shapes: List[Tuple[int, int]], - num_classes: int, - conf_thresh: float, - iou_thresh: float, - min_score: float, - final_thresh: float, -): - """ - Process the model output to generate detection results and expand bounding boxes. - - Parameters - ---------- - output_array : np.ndarray - The raw output from the model inference. - original_image_shapes : List[Tuple[int, int]] - The shapes of the original images before resizing, used for scaling bounding boxes. - num_classes : int - The number of classes the model can detect. - conf_thresh : float - The confidence threshold for detecting objects. - iou_thresh : float - The Intersection Over Union (IoU) threshold for non-maximum suppression. - min_score : float - The minimum score for keeping a detection. - final_thresh: float - Threshold for keeping a bounding box applied after postprocessing. - - - Returns - ------- - List[dict] - A list of dictionaries, each containing processed detection results including expanded bounding boxes. - - Notes - ----- - This function applies non-maximum suppression to the model's output and scales the bounding boxes back to the - original image size. - - Examples - -------- - >>> output_array = np.random.rand(2, 100, 85) - >>> original_image_shapes = [(1536, 1536), (1536, 1536)] - >>> results = process_inference_results(output_array, original_image_shapes, 80, 0.5, 0.5, 0.1) - >>> len(results) - 2 - """ - pred = yolox_utils.postprocess_model_prediction( - output_array, num_classes, conf_thresh, iou_thresh, class_agnostic=True - ) - results = yolox_utils.postprocess_results(pred, original_image_shapes, min_score=min_score) - logger.debug(f"Number of results: {len(results)}") - logger.debug(f"Results: {results}") - - annotation_dicts = [yolox_utils.expand_chart_bboxes(annotation_dict) for annotation_dict in results] - inference_results = [] - - # Filter out bounding boxes below the final threshold - for annotation_dict in annotation_dicts: - new_dict = {} - if "table" in annotation_dict: - new_dict["table"] = [bb for bb in annotation_dict["table"] if bb[4] >= final_thresh] - if "chart" in annotation_dict: - new_dict["chart"] = [bb for bb in annotation_dict["chart"] if bb[4] >= final_thresh] - if "title" in annotation_dict: - new_dict["title"] = annotation_dict["title"] - inference_results.append(new_dict) - - return inference_results - - def extract_table_and_chart_images( annotation_dict: Dict[str, List[List[float]]], original_image: np.ndarray, @@ -246,79 +174,85 @@ def extract_table_and_chart_images( tables_and_charts.append((page_idx, table_data)) -def extract_tables_and_charts_from_image( - image: np.ndarray, - config: ImageExtractorSchema, - num_classes: int = YOLOX_NUM_CLASSES, - conf_thresh: float = YOLOX_CONF_THRESHOLD, - iou_thresh: float = YOLOX_IOU_THRESHOLD, - min_score: float = YOLOX_MIN_SCORE, - final_thresh: float = YOLOX_FINAL_SCORE, +def extract_tables_and_charts_from_images( + images: List[np.ndarray], + config: ImageConfigSchema, trace_info: Optional[List] = None, -) -> List[CroppedImageWithContent]: +) -> List[Tuple[int, object]]: """ - Extract tables and charts from a single image using an ensemble of image-based models. - - This function processes a single image to detect and extract tables and charts. - It uses a sequence of models hosted on different inference servers to achieve this. + Detect and extract tables/charts from a list of NumPy images using YOLOX. Parameters ---------- - image : np.ndarray - A preprocessed image array for table and chart detection. - config : ImageExtractorSchema - Configuration for the inference client, including endpoint URLs and authentication. - num_classes : int, optional - The number of classes the model is trained to detect (default is 3). - conf_thresh : float, optional - The confidence threshold for detection (default is 0.01). - iou_thresh : float, optional - The Intersection Over Union (IoU) threshold for non-maximum suppression (default is 0.5). - min_score : float, optional - The minimum score threshold for considering a detection valid (default is 0.1). - final_thresh: float, optional - Threshold for keeping a bounding box applied after postprocessing (default is 0.48). + images : List[np.ndarray] + List of images in NumPy array format. + config : PDFiumConfigSchema + Configuration object containing YOLOX endpoints, auth token, etc. trace_info : Optional[List], optional - Tracing information for logging or debugging purposes. + Optional tracing data for debugging/performance profiling. Returns ------- - List[CroppedImageWithContent] - A list of `CroppedImageWithContent` objects representing detected tables or charts, - each containing metadata about the detected region. + List[Tuple[int, object]] + A list of (image_index, CroppedImageWithContent) + representing extracted table/chart data from each image. """ tables_and_charts = [] - yolox_client = None + try: - model_interface = yolox_utils.YoloxModelInterface() + model_interface = yolox_utils.YoloxPageElementsModelInterface() yolox_client = create_inference_client( - config.yolox_endpoints, model_interface, config.auth_token, config.yolox_infer_protocol + config.yolox_endpoints, + model_interface, + config.auth_token, + config.yolox_infer_protocol, ) - data = {"images": [image]} + max_batch_size = YOLOX_MAX_BATCH_SIZE + batches = [] + i = 0 + while i < len(images): + batch_size = min(2 ** int(log(len(images) - i, 2)), max_batch_size) + batches.append(images[i : i + batch_size]) # noqa: E203 + i += batch_size + + img_index = 0 + for batch in batches: + data = {"images": batch} + + # NimClient inference + inference_results = yolox_client.infer( + data, + model_name="yolox", + num_classes=YOLOX_NUM_CLASSES, + conf_thresh=YOLOX_CONF_THRESHOLD, + iou_thresh=YOLOX_IOU_THRESHOLD, + min_score=YOLOX_MIN_SCORE, + final_thresh=YOLOX_FINAL_SCORE, + trace_info=trace_info, # traceable_func arg + stage_name="pdf_content_extractor", # traceable_func arg + ) - inference_results = yolox_client.infer( - data, - model_name="yolox", - num_classes=YOLOX_NUM_CLASSES, - conf_thresh=YOLOX_CONF_THRESHOLD, - iou_thresh=YOLOX_IOU_THRESHOLD, - min_score=YOLOX_MIN_SCORE, - final_thresh=YOLOX_FINAL_SCORE, - ) + # 5) Extract table/chart info from each image's annotations + for annotation_dict, original_image in zip(inference_results, batch): + extract_table_and_chart_images( + annotation_dict, + original_image, + img_index, + tables_and_charts, + ) + img_index += 1 - extract_table_and_chart_images( - inference_results, - image, - page_idx=0, # Single image treated as one page - tables_and_charts=tables_and_charts, - ) + except TimeoutError: + logger.error("Timeout error during table/chart extraction.") + raise except Exception as e: - logger.error(f"Error during table/chart extraction from image: {str(e)}") + logger.error(f"Unhandled error during table/chart extraction: {str(e)}") traceback.print_exc() raise e + finally: if yolox_client: yolox_client.close() @@ -355,6 +289,8 @@ def image_data_extractor( Specifies whether to extract tables. extract_charts : bool Specifies whether to extract charts. + trace_info : dict, optional + Tracing information for logging or debugging purposes. **kwargs Additional extraction parameters. @@ -425,13 +361,13 @@ def image_data_extractor( # Table and chart extraction if extract_tables or extract_charts: try: - tables_and_charts = extract_tables_and_charts_from_image( - image_array, + tables_and_charts = extract_tables_and_charts_from_images( + [image_array], config=kwargs.get("image_extraction_config"), trace_info=trace_info, ) logger.debug("Extracted table/chart data from image") - for _, table_chart_data in tables_and_charts: + for _, table_chart_data in tables_and_charts[0]: extracted_data.append( construct_table_and_chart_metadata( table_chart_data, @@ -443,6 +379,7 @@ def image_data_extractor( ) except Exception as e: logger.error(f"Error extracting tables/charts from image: {e}") + raise logger.debug(f"Extracted {len(extracted_data)} items from the image.") diff --git a/src/nv_ingest/extraction_workflows/pdf/pdfium_helper.py b/src/nv_ingest/extraction_workflows/pdf/pdfium_helper.py index 1f4dbff7..ad4de2d6 100644 --- a/src/nv_ingest/extraction_workflows/pdf/pdfium_helper.py +++ b/src/nv_ingest/extraction_workflows/pdf/pdfium_helper.py @@ -34,7 +34,6 @@ from nv_ingest.util.image_processing.transforms import crop_image from nv_ingest.util.image_processing.transforms import numpy_to_base64 from nv_ingest.util.nim.helpers import create_inference_client -from nv_ingest.util.nim.helpers import get_version from nv_ingest.util.pdf.metadata_aggregators import Base64Image from nv_ingest.util.pdf.metadata_aggregators import CroppedImageWithContent from nv_ingest.util.pdf.metadata_aggregators import construct_image_metadata_from_pdf_image @@ -64,22 +63,8 @@ def extract_tables_and_charts_using_image_ensemble( ) -> List[Tuple[int, object]]: # List[Tuple[int, CroppedImageWithContent]] tables_and_charts = [] - # Obtain yolox_version - # Assuming that the grpc endpoint is at index 0 - yolox_http_endpoint = config.yolox_endpoints[1] try: - yolox_version = get_version(yolox_http_endpoint) - if not yolox_version: - logger.warning( - "Failed to obtain yolox-page-elements version from the endpoint. Falling back to the latest version." - ) - yolox_version = None # Default to the latest version - except Exception: - logger.waring("Failed to get yolox-page-elements version after 30 seconds. Falling back to the latest version.") - yolox_version = None # Default to the latest version - - try: - model_interface = yolox_utils.YoloxPageElementsModelInterface(yolox_version=yolox_version) + model_interface = yolox_utils.YoloxPageElementsModelInterface() yolox_client = create_inference_client( config.yolox_endpoints, model_interface, config.auth_token, config.yolox_infer_protocol ) @@ -142,76 +127,6 @@ def extract_tables_and_charts_using_image_ensemble( return tables_and_charts -def process_inference_results( - output_array: np.ndarray, - original_image_shapes: List[Tuple[int, int]], - num_classes: int, - conf_thresh: float, - iou_thresh: float, - min_score: float, - final_thresh: float, -): - """ - Process the model output to generate detection results and expand bounding boxes. - - Parameters - ---------- - output_array : np.ndarray - The raw output from the model inference. - original_image_shapes : List[Tuple[int, int]] - The shapes of the original images before resizing, used for scaling bounding boxes. - num_classes : int - The number of classes the model can detect. - conf_thresh : float - The confidence threshold for detecting objects. - iou_thresh : float - The Intersection Over Union (IoU) threshold for non-maximum suppression. - min_score : float - The minimum score for keeping a detection. - final_thresh: float - Threshold for keeping a bounding box applied after postprocessing. - - - Returns - ------- - List[dict] - A list of dictionaries, each containing processed detection results including expanded bounding boxes. - - Notes - ----- - This function applies non-maximum suppression to the model's output and scales the bounding boxes back to the - original image size. - - Examples - -------- - >>> output_array = np.random.rand(2, 100, 85) - >>> original_image_shapes = [(1536, 1536), (1536, 1536)] - >>> results = process_inference_results(output_array, original_image_shapes, 80, 0.5, 0.5, 0.1) - >>> len(results) - 2 - """ - pred = yolox_utils.postprocess_model_prediction( - output_array, num_classes, conf_thresh, iou_thresh, class_agnostic=True - ) - results = yolox_utils.postprocess_results(pred, original_image_shapes, min_score=min_score) - - annotation_dicts = [yolox_utils.expand_chart_bboxes(annotation_dict) for annotation_dict in results] - inference_results = [] - - # Filter out bounding boxes below the final threshold - for annotation_dict in annotation_dicts: - new_dict = {} - if "table" in annotation_dict: - new_dict["table"] = [bb for bb in annotation_dict["table"] if bb[4] >= final_thresh] - if "chart" in annotation_dict: - new_dict["chart"] = [bb for bb in annotation_dict["chart"] if bb[4] >= final_thresh] - if "title" in annotation_dict: - new_dict["title"] = annotation_dict["title"] - inference_results.append(new_dict) - - return inference_results - - # Handle individual table/chart extraction and model inference def extract_table_and_chart_images( annotation_dict, diff --git a/src/nv_ingest/extraction_workflows/pptx/pptx_helper.py b/src/nv_ingest/extraction_workflows/pptx/pptx_helper.py index 7e6b6d89..df930f1c 100644 --- a/src/nv_ingest/extraction_workflows/pptx/pptx_helper.py +++ b/src/nv_ingest/extraction_workflows/pptx/pptx_helper.py @@ -1,7 +1,7 @@ # SPDX-FileCopyrightText: Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. # All rights reserved. # SPDX-License-Identifier: Apache-2.0 - +import io # Copyright (c) 2024, NVIDIA CORPORATION. # @@ -21,8 +21,9 @@ import operator import re import uuid +from collections import defaultdict from datetime import datetime -from typing import Dict +from typing import Dict, List, Tuple from typing import Optional import pandas as pd @@ -31,8 +32,14 @@ from pptx.enum.dml import MSO_THEME_COLOR from pptx.enum.shapes import MSO_SHAPE_TYPE from pptx.enum.shapes import PP_PLACEHOLDER +from pptx.shapes.autoshape import Shape from pptx.slide import Slide +from nv_ingest.extraction_workflows.image.image_handlers import ( + load_and_preprocess_image, + extract_tables_and_charts_from_images, +) +from nv_ingest.schemas.image_extractor_schema import ImageConfigSchema from nv_ingest.schemas.metadata_schema import AccessLevelEnum from nv_ingest.schemas.metadata_schema import ContentTypeEnum from nv_ingest.schemas.metadata_schema import ImageTypeEnum @@ -41,70 +48,144 @@ from nv_ingest.schemas.metadata_schema import TableFormatEnum from nv_ingest.schemas.metadata_schema import TextTypeEnum from nv_ingest.schemas.metadata_schema import validate_metadata +from nv_ingest.schemas.pptx_extractor_schema import PPTXConfigSchema from nv_ingest.util.converters import bytetools from nv_ingest.util.detectors.language import detect_language +from nv_ingest.util.pdf.metadata_aggregators import construct_table_and_chart_metadata logger = logging.getLogger(__name__) -# Define a helper function to use python-pptx to extract text from a base64 -# encoded bytestram PPTX -def python_pptx(pptx_stream, extract_text: bool, extract_images: bool, extract_tables: bool, **kwargs): +def _finalize_images( + pending_images: List[Tuple[Shape, int, int, int, dict, dict, dict]], + extracted_data: List, + pptx_extraction_config: PPTXConfigSchema, + extract_tables: bool = False, + extract_charts: bool = False, + trace_info: Optional[Dict] = None, +): + """ + Post-process all pending images. + - Convert shape image -> NumPy or base64 + - If `extract_tables` or `extract_charts`, do detection (table/chart) + - Build the appropriate metadata, either table/chart or image. + + This mimics the docx approach, but adapted for python-pptx shapes. + """ + if not pending_images: + return + + # Convert each shape to image data (base64 or ndarray). + # We'll store them for a single call to your model if you'd like (batching). + image_arrays = [] + image_contexts = [] + for ( + shape, + shape_idx, + slide_idx, + slide_count, + page_nearby_blocks, + source_metadata, + base_unified_metadata, + ) in pending_images: + try: + image_bytes = shape.image.blob + image_array = load_and_preprocess_image(io.BytesIO(image_bytes)) + base64_img = bytetools.base64frombytes(image_bytes) + + image_arrays.append(image_array) + image_contexts.append( + ( + shape_idx, + slide_idx, + slide_count, + page_nearby_blocks, + source_metadata, + base_unified_metadata, + base64_img, + ) + ) + except Exception as e: + logger.warning(f"Unable to process shape image: {e}") + + # If you want table/chart detection for these images, do it now + # (similar to docx approach). This might use your YOLO or other method: + detection_map = defaultdict(list) # image_idx -> list of CroppedImageWithContent + if extract_tables or extract_charts: + try: + # For example, a call to your function that checks for tables/charts + detection_results = extract_tables_and_charts_from_images( + images=image_arrays, + config=ImageConfigSchema(**(pptx_extraction_config.model_dump())), + trace_info=trace_info, + ) + # detection_results is something like [(image_idx, CroppedImageWithContent), ...] + for img_idx, cropped_obj in detection_results: + detection_map[img_idx].append(cropped_obj) + except Exception as e: + logger.error(f"Error while running table/chart detection on PPTX images: {e}") + detection_map = {} + + # Now build the final metadata objects + for i, context in enumerate(image_contexts): + (shape_idx, slide_idx, slide_count, page_nearby_blocks, source_metadata, base_unified_metadata, base64_img) = ( + context + ) + + # If there's a detection result for this image, handle it + if i in detection_map and detection_map[i]: + # We found table(s)/chart(s) in the image + for cropped_item in detection_map[i]: + structured_entry = construct_table_and_chart_metadata( + structured_image=cropped_item, + page_idx=slide_idx, + page_count=slide_count, + source_metadata=source_metadata, + base_unified_metadata=base_unified_metadata, + ) + extracted_data.append(structured_entry) + else: + # No table detected => build normal image metadata + image_entry = _construct_image_metadata( + shape_idx=shape_idx, + slide_idx=slide_idx, + slide_count=slide_count, + page_nearby_blocks=page_nearby_blocks, + base64_img=base64_img, + source_metadata=source_metadata, + base_unified_metadata=base_unified_metadata, + ) + extracted_data.append(image_entry) + + +def python_pptx( + pptx_stream, extract_text: bool, extract_images: bool, extract_tables: bool, extract_charts: bool, **kwargs +): """ - Helper function to use python-pptx to extract text from a bytestream PPTX. - - A document has five levels - presentation, slides, shapes, paragraphs, and runs. - To align with the pdf extraction, we map the levels as follows: - - Document -> Presention - - Pages -> Slides - - Blocks -> Shapes - - Lines -> Paragraphs - - Spans -> Runs - - Parameters - ---------- - pptx_stream : io.BytesIO - A bytestream PPTX. - extract_text : bool - Specifies whether to extract text. - extract_images : bool - Specifies whether to extract images. - extract_tables : bool - Specifies whether to extract tables. - **kwargs - The keyword arguments are used for additional extraction parameters. - - Returns - ------- - str - A string of extracted text. + Helper function to use python-pptx to extract text from a bytestream PPTX, + while deferring image classification into tables/charts if requested. """ logger.debug("Extracting PPTX with python-pptx backend.") row_data = kwargs.get("row_data") - # get source_id source_id = row_data["source_id"] - # get text_depth + text_depth = kwargs.get("text_depth", "page") text_depth = TextTypeEnum[text_depth.upper()] - # Not configurable anywhere at the moment paragraph_format = kwargs.get("paragraph_format", "markdown") identify_nearby_objects = kwargs.get("identify_nearby_objects", True) - # get base metadata metadata_col = kwargs.get("metadata_column", "metadata") + pptx_extractor_config = kwargs.get("pptx_extraction_config", {}) + trace_info = kwargs.get("trace_info", {}) + base_unified_metadata = row_data[metadata_col] if metadata_col in row_data.index else {} - # get base source_metadata base_source_metadata = base_unified_metadata.get("source_metadata", {}) - # get source_location source_location = base_source_metadata.get("source_location", "") - # get collection_id (assuming coming in from source_metadata...) collection_id = base_source_metadata.get("collection_id", "") - # get partition_id (assuming coming in from source_metadata...) partition_id = base_source_metadata.get("partition_id", -1) - # get access_level (assuming coming in from source_metadata...) access_level = base_source_metadata.get("access_level", AccessLevelEnum.LEVEL_1) presentation = Presentation(pptx_stream) @@ -140,6 +221,10 @@ def python_pptx(pptx_stream, extract_text: bool, extract_images: bool, extract_t accumulated_text = [] extracted_data = [] + # Hold images here for final classification + # Each item is (shape, shape_idx, slide_idx, page_nearby_blocks, base_unified_metadata) + pending_images = [] + for slide_idx, slide in enumerate(presentation.slides): shapes = sorted(ungroup_shapes(slide.shapes), key=operator.attrgetter("top", "left")) @@ -153,6 +238,9 @@ def python_pptx(pptx_stream, extract_text: bool, extract_images: bool, extract_t block_text = [] added_title = added_subtitle = False + # --------------------------------------------- + # 1) Text Extraction + # --------------------------------------------- if extract_text and shape.has_text_frame: for paragraph_idx, paragraph in enumerate(shape.text_frame.paragraphs): if not paragraph.text.strip(): @@ -162,21 +250,22 @@ def python_pptx(pptx_stream, extract_text: bool, extract_images: bool, extract_t text = run.text if not text: continue + text = escape_text(text) if paragraph_format == "markdown": - # For titles/subtitles, process them on the block/shape level, and - # skip formatting. if is_title(shape): - if added_title: + if not added_title: + text = process_title(shape) # format a heading or something + added_title = True + else: continue - text = process_title(shape) - added_title = True elif is_subtitle(shape): - if added_subtitle: + if not added_subtitle: + text = process_subtitle(shape) + added_subtitle = True + else: continue - text = process_subtitle(shape) - added_subtitle = True else: if run.hyperlink.address: text = get_hyperlink(text, run.hyperlink.address) @@ -193,9 +282,11 @@ def python_pptx(pptx_stream, extract_text: bool, extract_images: bool, extract_t accumulated_text.append(text) + # For "nearby objects", store block text if extract_images and identify_nearby_objects: block_text.append(text) + # If we only want text at SPAN level, flush after each run if text_depth == TextTypeEnum.SPAN: text_extraction = _construct_text_metadata( presentation, @@ -211,17 +302,15 @@ def python_pptx(pptx_stream, extract_text: bool, extract_images: bool, extract_t source_metadata, base_unified_metadata, ) - if len(text_extraction) > 0: extracted_data.append(text_extraction) - accumulated_text = [] - # Avoid excessive newline characters and add them only at - # the line/paragraph level or higher. + # Add newlines for separation at line/paragraph level if accumulated_text and not accumulated_text[-1].endswith("\n\n"): accumulated_text.append("\n\n") + # If text_depth is LINE, flush after each paragraph if text_depth == TextTypeEnum.LINE: text_extraction = _construct_text_metadata( presentation, @@ -237,12 +326,11 @@ def python_pptx(pptx_stream, extract_text: bool, extract_images: bool, extract_t source_metadata, base_unified_metadata, ) - if len(text_extraction) > 0: extracted_data.append(text_extraction) - accumulated_text = [] + # If text_depth is BLOCK, flush after we've read the entire shape if text_depth == TextTypeEnum.BLOCK: text_extraction = _construct_text_metadata( presentation, @@ -258,54 +346,60 @@ def python_pptx(pptx_stream, extract_text: bool, extract_images: bool, extract_t source_metadata, base_unified_metadata, ) - if len(text_extraction) > 0: extracted_data.append(text_extraction) - accumulated_text = [] - if extract_images and identify_nearby_objects and (len(block_text) > 0): + # If we have text in this shape and the user wants "nearby objects" references: + if extract_images and identify_nearby_objects and block_text: page_nearby_blocks["text"]["content"].append("".join(block_text)) page_nearby_blocks["text"]["bbox"].append(get_bbox(shape_object=shape)) + # --------------------------------------------- + # 2) Image Handling (DEFERRED) + # --------------------------------------------- + # If shape is a picture (or a placeholder that is an embedded image) + # Instead of building metadata now, we'll store it in pending_images. if extract_images and ( shape.shape_type == MSO_SHAPE_TYPE.PICTURE or ( shape.is_placeholder and shape.placeholder_format.type == PP_PLACEHOLDER.OBJECT and hasattr(shape, "image") - and getattr(shape, "image") ) ): try: - image_extraction = _construct_image_metadata( - shape, - shape_idx, - slide_idx, - slide_count, - source_metadata, - base_unified_metadata, - page_nearby_blocks, + # Just accumulate the shape + context; don't build the final item yet. + pending_images.append( + ( + shape, # so we can later pull shape.image.blob + shape_idx, + slide_idx, + slide_count, + page_nearby_blocks, + source_metadata, + base_unified_metadata, + ) ) - extracted_data.append(image_extraction) except ValueError as e: - # Handle the specific case where no embedded image is found logger.warning(f"No embedded image found for shape {shape_idx} on slide {slide_idx}: {e}") except Exception as e: - # Handle any other exceptions that might occur - logger.warning(f"An error occurred while processing shape {shape_idx} on slide {slide_idx}: {e}") + logger.warning(f"Error processing shape {shape_idx} on slide {slide_idx}: {e}") + # --------------------------------------------- + # 3) Table Handling + # --------------------------------------------- if extract_tables and shape.has_table: table_extraction = _construct_table_metadata( shape, slide_idx, slide_count, source_metadata, base_unified_metadata ) extracted_data.append(table_extraction) - # Extract text - slide (b) + # If text_depth is PAGE, flush once per slide if (extract_text) and (text_depth == TextTypeEnum.PAGE) and (len(accumulated_text) > 0): text_extraction = _construct_text_metadata( presentation, - shape, + shape, # might pass None if you prefer accumulated_text, keywords, slide_idx, @@ -317,17 +411,15 @@ def python_pptx(pptx_stream, extract_text: bool, extract_images: bool, extract_t source_metadata, base_unified_metadata, ) - if len(text_extraction) > 0: extracted_data.append(text_extraction) - accumulated_text = [] - # Extract text - presentation (c) + # If text_depth is DOCUMENT, flush once at the end if (extract_text) and (text_depth == TextTypeEnum.DOCUMENT) and (len(accumulated_text) > 0): text_extraction = _construct_text_metadata( presentation, - shape, + shape, # might pass None accumulated_text, keywords, -1, @@ -339,12 +431,23 @@ def python_pptx(pptx_stream, extract_text: bool, extract_images: bool, extract_t source_metadata, base_unified_metadata, ) - if len(text_extraction) > 0: extracted_data.append(text_extraction) - accumulated_text = [] + # --------------------------------------------- + # FINAL STEP: Finalize images + # --------------------------------------------- + if extract_images or extract_tables or extract_charts: + _finalize_images( + pending_images, + extracted_data, + pptx_extractor_config, + extract_tables=extract_tables, + extract_charts=extract_charts, + trace_info=trace_info, + ) + return extracted_data @@ -410,17 +513,19 @@ def _construct_text_metadata( # need to add block text to hierarchy/nearby_objects, including bbox def _construct_image_metadata( - shape, shape_idx, slide_idx, slide_count, source_metadata, base_unified_metadata, page_nearby_blocks + shape_idx: int, + slide_idx: int, + slide_count: int, + page_nearby_blocks: Dict, + base64_img: str, + source_metadata: Dict, + base_unified_metadata: Dict, ): - image_type = shape.image.ext - if ImageTypeEnum.has_value(image_type): - image_type = ImageTypeEnum[image_type.upper()] - - base64_img = bytetools.base64frombytes(shape.image.blob) - - bbox = get_bbox(shape_object=shape) - width = shape.width - height = shape.height + """ + Build standard PPTX image metadata. + """ + # Example bounding box + bbox = (0, 0, 0, 0) # or extract from shape.left, shape.top, shape.width, shape.height if desired content_metadata = { "type": ContentTypeEnum.IMAGE, @@ -437,17 +542,14 @@ def _construct_image_metadata( } image_metadata = { - "image_type": image_type, + "image_type": ImageTypeEnum.image_type_1, "structured_image_type": ImageTypeEnum.image_type_1, - "caption": "", + "caption": "", # could attempt to guess a caption from nearby text "text": "", "image_location": bbox, - "width": width, - "height": height, } - unified_metadata = base_unified_metadata.copy() - + unified_metadata = base_unified_metadata.copy() if base_unified_metadata else {} unified_metadata.update( { "content": base64_img, @@ -459,7 +561,11 @@ def _construct_image_metadata( validated_unified_metadata = validate_metadata(unified_metadata) - return [ContentTypeEnum.IMAGE, validated_unified_metadata.model_dump(), str(uuid.uuid4())] + return [ + ContentTypeEnum.IMAGE.value, + validated_unified_metadata.model_dump(), + str(uuid.uuid4()), + ] def _construct_table_metadata( @@ -492,12 +598,13 @@ def _construct_table_metadata( "caption": "", "table_format": TableFormatEnum.MARKDOWN, "table_location": bbox, + "table_content": df.to_markdown(index=False), } ext_unified_metadata = base_unified_metadata.copy() ext_unified_metadata.update( { - "content": df.to_markdown(index=False), + "content": "", "source_metadata": source_metadata, "content_metadata": content_metadata, "table_metadata": table_metadata, diff --git a/src/nv_ingest/modules/transforms/embed_extractions.py b/src/nv_ingest/modules/transforms/embed_extractions.py index 2b80c3be..cc6dae72 100644 --- a/src/nv_ingest/modules/transforms/embed_extractions.py +++ b/src/nv_ingest/modules/transforms/embed_extractions.py @@ -281,7 +281,7 @@ def _add_embeddings(row, embeddings, info_msgs): return row -def _get_text_content(row): +def _get_pandas_text_content(row): """ A pandas UDF used to select extracted text content to be used to create embeddings. """ @@ -289,7 +289,7 @@ def _get_text_content(row): return row["content"] -def _get_table_content(row): +def _get_pandas_table_content(row): """ A pandas UDF used to select extracted table/chart content to be used to create embeddings. """ @@ -297,6 +297,38 @@ def _get_table_content(row): return row["table_metadata"]["table_content"] +def _get_pandas_image_content(row): + """ + A pandas UDF used to select extracted image captions to be used to create embeddings. + """ + + return row["image_metadata"]["caption"] + + +def _get_cudf_text_content(df: cudf.DataFrame): + """ + A cuDF UDF used to select extracted text content to be used to create embeddings. + """ + + return df.struct.field("content") + + +def _get_cudf_table_content(df: cudf.DataFrame): + """ + A cuDF UDF used to select extracted table/chart content to be used to create embeddings. + """ + + return df.struct.field("table_metadata").struct.field("table_content") + + +def _get_cudf_image_content(df: cudf.DataFrame): + """ + A cuDF UDF used to select extracted image captions to be used to create embeddings. + """ + + return df.struct.field("image_metadata").struct.field("caption") + + def _batch_generator(iterable: Iterable, batch_size=10): """ A generator to yield batches of size `batch_size` from an interable. @@ -349,7 +381,6 @@ def _generate_batches(prompts: List[str], batch_size: int = 100): def _generate_embeddings( ctrl_msg: ControlMessage, - content_type: ContentTypeEnum, event_loop: asyncio.SelectorEventLoop, batch_size: int, api_key: str, @@ -361,8 +392,10 @@ def _generate_embeddings( filter_errors: bool, ): """ - A function to generate embeddings for the supplied `ContentTypeEnum`. The `ContentTypeEnum` will - drive filtering criteria used to select rows of data to enrich with embeddings. + A function to generate text embeddings for supported content types (TEXT, STRUCTURED, IMAGE). + + This function dynamically selects the appropriate metadata field based on content type and + calculates embeddings using the NIM embedding service. AUDIO and VIDEO types are stubbed and skipped. Parameters ---------- @@ -403,53 +436,71 @@ def _generate_embeddings( content_mask : cudf.Series A boolean mask representing rows filtered to calculate embeddings. """ + cudf_content_extractor = { + ContentTypeEnum.TEXT: _get_cudf_text_content, + ContentTypeEnum.STRUCTURED: _get_cudf_table_content, + ContentTypeEnum.IMAGE: _get_cudf_image_content, + ContentTypeEnum.AUDIO: lambda _: None, # Not supported yet. + ContentTypeEnum.VIDEO: lambda _: None, # Not supported yet. + } + pandas_content_extractor = { + ContentTypeEnum.TEXT: _get_pandas_text_content, + ContentTypeEnum.STRUCTURED: _get_pandas_table_content, + ContentTypeEnum.IMAGE: _get_pandas_image_content, + ContentTypeEnum.AUDIO: lambda _: None, # Not supported yet. + ContentTypeEnum.VIDEO: lambda _: None, # Not supported yet. + } + + logger.debug("Generating text embeddings for supported content types: TEXT, STRUCTURED, IMAGE.") + + embedding_dataframes = [] + content_masks = [] with ctrl_msg.payload().mutable_dataframe() as mdf: if mdf.empty: - return None, None - - # generate table text mask - if content_type == ContentTypeEnum.TEXT: - content_mask = (mdf["document_type"] == content_type.value) & ( - mdf["metadata"].struct.field("content") != "" - ).fillna(False) - content_getter = _get_text_content - elif content_type == ContentTypeEnum.STRUCTURED: - table_mask = mdf["document_type"] == content_type.value - if not table_mask.any(): - return None, None - content_mask = table_mask & ( - mdf["metadata"].struct.field("table_metadata").struct.field("table_content") != "" - ).fillna(False) - content_getter = _get_table_content - - # exit if matches found - if not content_mask.any(): - return None, None - - df_text = mdf.loc[content_mask].to_pandas().reset_index(drop=True) - # get text list - filtered_text = df_text["metadata"].apply(content_getter) - # calculate embeddings - filtered_text_batches = _generate_batches(filtered_text.tolist(), batch_size) - text_embeddings = _async_runner( - filtered_text_batches, - api_key, - embedding_nim_endpoint, - embedding_model, - encoding_format, - input_type, - truncate, - event_loop, - filter_errors, - ) - # update embeddings in metadata - df_text[["metadata", "document_type", "_contains_embeddings"]] = df_text.apply( - _add_embeddings, **text_embeddings, axis=1 - )[["metadata", "document_type", "_contains_embeddings"]] - df_text["_content"] = filtered_text + return ctrl_msg + + for content_type, content_getter in pandas_content_extractor.items(): + if not content_getter: + logger.debug(f"Skipping unsupported content type: {content_type}") + continue + + content_mask = mdf["document_type"] == content_type.value + if not content_mask.any(): + continue + + cudf_content_getter = cudf_content_extractor[content_type] + content_mask = (content_mask & (cudf_content_getter(mdf["metadata"]) != "")).fillna(False) + if not content_mask.any(): + continue + + df_content = mdf.loc[content_mask].to_pandas().reset_index(drop=True) + filtered_content = df_content["metadata"].apply(content_getter) + # calculate embeddings + filtered_content_batches = _generate_batches(filtered_content.tolist(), batch_size) + content_embeddings = _async_runner( + filtered_content_batches, + api_key, + embedding_nim_endpoint, + embedding_model, + encoding_format, + input_type, + truncate, + event_loop, + filter_errors, + ) + # update embeddings in metadata + df_content[["metadata", "document_type", "_contains_embeddings"]] = df_content.apply( + _add_embeddings, **content_embeddings, axis=1 + )[["metadata", "document_type", "_contains_embeddings"]] + df_content["_content"] = filtered_content + + embedding_dataframes.append(df_content) + content_masks.append(content_mask) + + message = _concatenate_extractions(ctrl_msg, embedding_dataframes, content_masks) - return df_text, content_mask + return message def _concatenate_extractions(ctrl_msg: ControlMessage, dataframes: List[pd.DataFrame], masks: List[cudf.Series]): @@ -493,8 +544,8 @@ def _concatenate_extractions(ctrl_msg: ControlMessage, dataframes: List[pd.DataF @register_module(MODULE_NAME, MODULE_NAMESPACE) def _embed_extractions(builder: mrc.Builder): """ - A pipeline module that receives incoming messages in ControlMessage format and calculates embeddings for - supported document types. + A pipeline module that receives incoming messages in ControlMessage format + and calculates text embeddings for all supported content types. Parameters ---------- @@ -519,56 +570,20 @@ def embed_extractions_fn(message: ControlMessage): try: task_props = message.remove_task("embed") model_dump = task_props.model_dump() - embed_text = model_dump.get("text") - embed_tables = model_dump.get("tables") filter_errors = model_dump.get("filter_errors", False) - logger.debug(f"Generating embeddings: text={embed_text}, tables={embed_tables}") - embedding_dataframes = [] - content_masks = [] - - if embed_text: - df_text, content_mask = _generate_embeddings( - message, - ContentTypeEnum.TEXT, - event_loop, - validated_config.batch_size, - validated_config.api_key, - validated_config.embedding_nim_endpoint, - validated_config.embedding_model, - validated_config.encoding_format, - validated_config.input_type, - validated_config.truncate, - filter_errors, - ) - if df_text is not None: - embedding_dataframes.append(df_text) - content_masks.append(content_mask) - - if embed_tables: - df_tables, table_mask = _generate_embeddings( - message, - ContentTypeEnum.STRUCTURED, - event_loop, - validated_config.batch_size, - validated_config.api_key, - validated_config.embedding_nim_endpoint, - validated_config.embedding_model, - validated_config.encoding_format, - validated_config.input_type, - validated_config.truncate, - filter_errors, - ) - if df_tables is not None: - embedding_dataframes.append(df_tables) - content_masks.append(table_mask) - - if len(content_masks) == 0: - return message - - message = _concatenate_extractions(message, embedding_dataframes, content_masks) - - return message + return _generate_embeddings( + message, + event_loop, + validated_config.batch_size, + validated_config.api_key, + validated_config.embedding_nim_endpoint, + validated_config.embedding_model, + validated_config.encoding_format, + validated_config.input_type, + validated_config.truncate, + filter_errors, + ) except Exception as e: traceback.print_exc() diff --git a/src/nv_ingest/schemas/docx_extractor_schema.py b/src/nv_ingest/schemas/docx_extractor_schema.py new file mode 100644 index 00000000..5204674e --- /dev/null +++ b/src/nv_ingest/schemas/docx_extractor_schema.py @@ -0,0 +1,124 @@ +# SPDX-FileCopyrightText: Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. +# All rights reserved. +# SPDX-License-Identifier: Apache-2.0 + + +import logging +from typing import Optional +from typing import Tuple + +from pydantic import model_validator, ConfigDict, BaseModel + +logger = logging.getLogger(__name__) + + +class DocxConfigSchema(BaseModel): + """ + Configuration schema for docx extraction endpoints and options. + + Parameters + ---------- + auth_token : Optional[str], default=None + Authentication token required for secure services. + + yolox_endpoints : Tuple[str, str] + A tuple containing the gRPC and HTTP services for the yolox endpoint. + Either the gRPC or HTTP service can be empty, but not both. + + Methods + ------- + validate_endpoints(values) + Validates that at least one of the gRPC or HTTP services is provided for each endpoint. + + Raises + ------ + ValueError + If both gRPC and HTTP services are empty for any endpoint. + + Config + ------ + extra : str + Pydantic config option to forbid extra fields. + """ + + auth_token: Optional[str] = None + + yolox_endpoints: Tuple[Optional[str], Optional[str]] = (None, None) + yolox_infer_protocol: str = "" + + @model_validator(mode="before") + @classmethod + def validate_endpoints(cls, values): + """ + Validates the gRPC and HTTP services for all endpoints. + + Parameters + ---------- + values : dict + Dictionary containing the values of the attributes for the class. + + Returns + ------- + dict + The validated dictionary of values. + + Raises + ------ + ValueError + If both gRPC and HTTP services are empty for any endpoint. + """ + + def clean_service(service): + """Set service to None if it's an empty string or contains only spaces or quotes.""" + if service is None or not service.strip() or service.strip(" \"'") == "": + return None + return service + + for model_name in ["yolox"]: + endpoint_name = f"{model_name}_endpoints" + grpc_service, http_service = values.get(endpoint_name) + grpc_service = clean_service(grpc_service) + http_service = clean_service(http_service) + + if not grpc_service and not http_service: + raise ValueError(f"Both gRPC and HTTP services cannot be empty for {endpoint_name}.") + + values[endpoint_name] = (grpc_service, http_service) + + protocol_name = f"{model_name}_infer_protocol" + protocol_value = values.get(protocol_name) + if not protocol_value: + protocol_value = "http" if http_service else "grpc" if grpc_service else "" + protocol_value = protocol_value.lower() + values[protocol_name] = protocol_value + + return values + + model_config = ConfigDict(extra="forbid") + + +class DocxExtractorSchema(BaseModel): + """ + Configuration schema for the PDF extractor settings. + + Parameters + ---------- + max_queue_size : int, default=1 + The maximum number of items allowed in the processing queue. + + n_workers : int, default=16 + The number of worker threads to use for processing. + + raise_on_failure : bool, default=False + A flag indicating whether to raise an exception on processing failure. + + image_extraction_config: Optional[ImageConfigSchema], default=None + Configuration schema for the image extraction stage. + """ + + max_queue_size: int = 1 + n_workers: int = 16 + raise_on_failure: bool = False + + docx_extraction_config: Optional[DocxConfigSchema] = None + model_config = ConfigDict(extra="forbid") diff --git a/src/nv_ingest/schemas/ingest_job_schema.py b/src/nv_ingest/schemas/ingest_job_schema.py index 09975228..7672fec7 100644 --- a/src/nv_ingest/schemas/ingest_job_schema.py +++ b/src/nv_ingest/schemas/ingest_job_schema.py @@ -36,6 +36,7 @@ class DocumentTypeEnum(str, Enum): mp3 = "mp3" wav = "wav" + class TaskTypeEnum(str, Enum): caption = "caption" dedup = "dedup" @@ -131,8 +132,6 @@ class IngestTaskDedupSchema(BaseModelNoExt): class IngestTaskEmbedSchema(BaseModelNoExt): - text: bool = True - tables: bool = True filter_errors: bool = False diff --git a/src/nv_ingest/schemas/ingest_pipeline_config_schema.py b/src/nv_ingest/schemas/ingest_pipeline_config_schema.py index 1471a338..fe5debd6 100644 --- a/src/nv_ingest/schemas/ingest_pipeline_config_schema.py +++ b/src/nv_ingest/schemas/ingest_pipeline_config_schema.py @@ -22,7 +22,7 @@ from nv_ingest.schemas.otel_meter_schema import OpenTelemetryMeterSchema from nv_ingest.schemas.otel_tracer_schema import OpenTelemetryTracerSchema from nv_ingest.schemas.pdf_extractor_schema import PDFExtractorSchema -from nv_ingest.schemas.pptx_extractor_schema import PPTXExctractorSchema +from nv_ingest.schemas.pptx_extractor_schema import PPTXExtractorSchema from nv_ingest.schemas.table_extractor_schema import TableExtractorSchema logger = logging.getLogger(__name__) @@ -42,7 +42,7 @@ class PipelineConfigSchema(BaseModel): otel_meter_module: OpenTelemetryMeterSchema = OpenTelemetryMeterSchema() otel_tracer_module: OpenTelemetryTracerSchema = OpenTelemetryTracerSchema() pdf_extractor_module: PDFExtractorSchema = PDFExtractorSchema() - pptx_extractor_module: PPTXExctractorSchema = PPTXExctractorSchema() + pptx_extractor_module: PPTXExtractorSchema = PPTXExtractorSchema() redis_task_sink: MessageBrokerTaskSinkSchema = MessageBrokerTaskSinkSchema() redis_task_source: MessageBrokerTaskSourceSchema = MessageBrokerTaskSourceSchema() table_extractor_module: TableExtractorSchema = TableExtractorSchema() diff --git a/src/nv_ingest/schemas/metadata_schema.py b/src/nv_ingest/schemas/metadata_schema.py index e5e1a459..9de9aba9 100644 --- a/src/nv_ingest/schemas/metadata_schema.py +++ b/src/nv_ingest/schemas/metadata_schema.py @@ -36,12 +36,14 @@ class AccessLevelEnum(int, Enum): class ContentTypeEnum(str, Enum): - TEXT = "text" + AUDIO = "audio" + EMBEDDING = "embedding" IMAGE = "image" + INFO_MSG = "info_message" STRUCTURED = "structured" + TEXT = "text" UNSTRUCTURED = "unstructured" - INFO_MSG = "info_message" - EMBEDDING = "embedding" + VIDEO = "video" class StdContentDescEnum(str, Enum): diff --git a/src/nv_ingest/schemas/pptx_extractor_schema.py b/src/nv_ingest/schemas/pptx_extractor_schema.py index 987ac671..d3897075 100644 --- a/src/nv_ingest/schemas/pptx_extractor_schema.py +++ b/src/nv_ingest/schemas/pptx_extractor_schema.py @@ -3,8 +3,122 @@ # SPDX-License-Identifier: Apache-2.0 -from nv_ingest.schemas.pdf_extractor_schema import PDFExtractorSchema +import logging +from typing import Optional +from typing import Tuple +from pydantic import model_validator, ConfigDict, BaseModel -class PPTXExctractorSchema(PDFExtractorSchema): - pass +logger = logging.getLogger(__name__) + + +class PPTXConfigSchema(BaseModel): + """ + Configuration schema for docx extraction endpoints and options. + + Parameters + ---------- + auth_token : Optional[str], default=None + Authentication token required for secure services. + + yolox_endpoints : Tuple[str, str] + A tuple containing the gRPC and HTTP services for the yolox endpoint. + Either the gRPC or HTTP service can be empty, but not both. + + Methods + ------- + validate_endpoints(values) + Validates that at least one of the gRPC or HTTP services is provided for each endpoint. + + Raises + ------ + ValueError + If both gRPC and HTTP services are empty for any endpoint. + + Config + ------ + extra : str + Pydantic config option to forbid extra fields. + """ + + auth_token: Optional[str] = None + + yolox_endpoints: Tuple[Optional[str], Optional[str]] = (None, None) + yolox_infer_protocol: str = "" + + @model_validator(mode="before") + @classmethod + def validate_endpoints(cls, values): + """ + Validates the gRPC and HTTP services for all endpoints. + + Parameters + ---------- + values : dict + Dictionary containing the values of the attributes for the class. + + Returns + ------- + dict + The validated dictionary of values. + + Raises + ------ + ValueError + If both gRPC and HTTP services are empty for any endpoint. + """ + + def clean_service(service): + """Set service to None if it's an empty string or contains only spaces or quotes.""" + if service is None or not service.strip() or service.strip(" \"'") == "": + return None + return service + + for model_name in ["yolox"]: + endpoint_name = f"{model_name}_endpoints" + grpc_service, http_service = values.get(endpoint_name) + grpc_service = clean_service(grpc_service) + http_service = clean_service(http_service) + + if not grpc_service and not http_service: + raise ValueError(f"Both gRPC and HTTP services cannot be empty for {endpoint_name}.") + + values[endpoint_name] = (grpc_service, http_service) + + protocol_name = f"{model_name}_infer_protocol" + protocol_value = values.get(protocol_name) + if not protocol_value: + protocol_value = "http" if http_service else "grpc" if grpc_service else "" + protocol_value = protocol_value.lower() + values[protocol_name] = protocol_value + + return values + + model_config = ConfigDict(extra="forbid") + + +class PPTXExtractorSchema(BaseModel): + """ + Configuration schema for the PDF extractor settings. + + Parameters + ---------- + max_queue_size : int, default=1 + The maximum number of items allowed in the processing queue. + + n_workers : int, default=16 + The number of worker threads to use for processing. + + raise_on_failure : bool, default=False + A flag indicating whether to raise an exception on processing failure. + + image_extraction_config: Optional[ImageConfigSchema], default=None + Configuration schema for the image extraction stage. + """ + + max_queue_size: int = 1 + n_workers: int = 16 + raise_on_failure: bool = False + + pptx_extraction_config: Optional[PPTXConfigSchema] = None + model_config = ConfigDict(extra="forbid") diff --git a/src/nv_ingest/schemas/processing_job_schema.py b/src/nv_ingest/schemas/processing_job_schema.py new file mode 100644 index 00000000..731ec986 --- /dev/null +++ b/src/nv_ingest/schemas/processing_job_schema.py @@ -0,0 +1,31 @@ +# SPDX-FileCopyrightText: Copyright (c) 2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-License-Identifier: LicenseRef-NvidiaProprietary +# +# NVIDIA CORPORATION, its affiliates and licensors retain all intellectual +# property and proprietary rights in and to this material, related +# documentation and any modifications thereto. Any use, reproduction, +# disclosure or distribution of this material and related documentation +# without an express license agreement from NVIDIA CORPORATION or +# its affiliates is strictly prohibited. + +from pydantic import BaseModel, ConfigDict +from enum import Enum + + +class ConversionStatus(str, Enum): + IN_PROGRESS = "in_progress" + SUCCESS = "success" + FAILED = "failed" + + model_config = ConfigDict(extra="forbid") + + +class ProcessingJob(BaseModel): + submitted_job_id: str + filename: str + raw_result: str = "" + content: str = "" + status: ConversionStatus + error: str | None = None + + model_config = ConfigDict(extra="forbid") diff --git a/src/nv_ingest/service/impl/ingest/redis_ingest_service.py b/src/nv_ingest/service/impl/ingest/redis_ingest_service.py index 2fb9b6c0..737231f5 100644 --- a/src/nv_ingest/service/impl/ingest/redis_ingest_service.py +++ b/src/nv_ingest/service/impl/ingest/redis_ingest_service.py @@ -14,10 +14,12 @@ from json import JSONDecodeError from typing import Any +from typing import List from nv_ingest.schemas import validate_ingest_job from nv_ingest.schemas.message_wrapper_schema import MessageWrapper from nv_ingest.service.meta.ingest.ingest_service_meta import IngestServiceMeta from nv_ingest.util.message_brokers.redis.redis_client import RedisClient +from nv_ingest.schemas.processing_job_schema import ProcessingJob logger = logging.getLogger("uvicorn") @@ -46,6 +48,8 @@ def __init__(self, redis_hostname: str, redis_port: int, redis_task_queue: str): self._redis_hostname = redis_hostname self._redis_port = redis_port self._redis_task_queue = redis_task_queue + self._cache_prefix = "processing_cache:" + self._bulk_vdb_cache_prefix = "vdb_bulk_upload_cache:" self._ingest_client = RedisClient( host=self._redis_hostname, port=self._redis_port, max_pool_size=self._concurrency_level @@ -89,3 +93,24 @@ async def fetch_job(self, job_id: str) -> Any: raise TimeoutError() return message + + async def set_processing_cache(self, job_id: str, jobs_data: List[ProcessingJob]) -> None: + """Store processing jobs data using simple key-value""" + cache_key = f"{self._cache_prefix}{job_id}" + try: + self._ingest_client.get_client().set(cache_key, json.dumps([job.dict() for job in jobs_data]), ex=3600) + except Exception as err: + logger.error(f"Error setting cache for {cache_key}: {err}") + raise + + async def get_processing_cache(self, job_id: str) -> List[ProcessingJob]: + """Retrieve processing jobs data using simple key-value""" + cache_key = f"{self._cache_prefix}{job_id}" + try: + data = self._ingest_client.get_client().get(cache_key) + if data is None: + return [] + return [ProcessingJob(**job) for job in json.loads(data)] + except Exception as err: + logger.error(f"Error getting cache for {cache_key}: {err}") + raise diff --git a/src/nv_ingest/service/meta/ingest/ingest_service_meta.py b/src/nv_ingest/service/meta/ingest/ingest_service_meta.py index 3bc5b7a6..b94f739a 100644 --- a/src/nv_ingest/service/meta/ingest/ingest_service_meta.py +++ b/src/nv_ingest/service/meta/ingest/ingest_service_meta.py @@ -10,8 +10,10 @@ from abc import ABC from abc import abstractmethod +from typing import List from nv_ingest.schemas.message_wrapper_schema import MessageWrapper +from nv_ingest.schemas.processing_job_schema import ProcessingJob class IngestServiceMeta(ABC): @@ -22,3 +24,11 @@ async def submit_job(self, job_spec: MessageWrapper, trace_id: str) -> str: @abstractmethod async def fetch_job(self, job_id: str): """Abstract method for fetching job from ingestion service based on job_id""" + + @abstractmethod + async def set_processing_cache(self, job_id: str, jobs_data: List[ProcessingJob]) -> None: + """Abstract method for setting processing cache""" + + @abstractmethod + async def get_processing_cache(self, job_id: str) -> List[ProcessingJob]: + """Abstract method for getting processing cache""" diff --git a/src/nv_ingest/stages/docx_extractor_stage.py b/src/nv_ingest/stages/docx_extractor_stage.py index 7fcc434c..953eefc1 100644 --- a/src/nv_ingest/stages/docx_extractor_stage.py +++ b/src/nv_ingest/stages/docx_extractor_stage.py @@ -8,19 +8,70 @@ import io import logging import traceback +from typing import Optional, Dict, Any import pandas as pd from pydantic import BaseModel from morpheus.config import Config from nv_ingest.extraction_workflows import docx +from nv_ingest.schemas.docx_extractor_schema import DocxExtractorSchema from nv_ingest.stages.multiprocessing_stage import MultiProcessingBaseStage from nv_ingest.util.exception_handlers.pdf import create_exception_tag logger = logging.getLogger(f"morpheus.{__name__}") -def _process_docx_bytes(df, task_props): +def decode_and_extract(base64_row, task_props, validated_config: Any, trace_info: Dict, default="python_docx"): + if isinstance(task_props, BaseModel): + task_props = task_props.model_dump() + + # Base64 content to extract + base64_content = base64_row["content"] + # Row data to include in extraction + bool_index = base64_row.index.isin(("content",)) + row_data = base64_row[~bool_index] + task_props["params"]["row_data"] = row_data + # Get source_id + source_id = base64_row["source_id"] if "source_id" in base64_row.index else None + # Decode the base64 content + doc_bytes = base64.b64decode(base64_content) + + # Load the document + doc_stream = io.BytesIO(doc_bytes) + + # Type of extraction method to use + extract_method = task_props.get("method", "python_docx") + extract_params = task_props.get("params", {}) + try: + if validated_config.docx_extraction_config is not None: + extract_params["docx_extraction_config"] = validated_config.docx_extraction_config + + if trace_info is not None: + extract_params["trace_info"] = trace_info + + if not hasattr(docx, extract_method): + extract_method = default + + func = getattr(docx, extract_method, default) + logger.debug("Running extraction method: %s", extract_method) + extracted_data = func(doc_stream, **extract_params) + + return extracted_data + + except Exception as error: + traceback.print_exc() + log_error_message = f"Error loading extractor:{error}" + logger.error(log_error_message) + logger.error(f"Failed on file:{source_id}") + + # Propagate error back and tag message as failed. + exception_tag = create_exception_tag(error_message=log_error_message, source_id=source_id) + + return exception_tag + + +def _process_docx_bytes(df, task_props, validated_config: Any, trace_info: Optional[Dict[str, Any]] = None): """ Processes a cuDF DataFrame containing docx files in base64 encoding. Each document's content is replaced with its extracted text. @@ -33,51 +84,11 @@ def _process_docx_bytes(df, task_props): - A pandas DataFrame with the docx content replaced by the extracted text. """ - def decode_and_extract(base64_row, task_props, default="python_docx"): - if isinstance(task_props, BaseModel): - task_props = task_props.model_dump() - - # Base64 content to extract - base64_content = base64_row["content"] - # Row data to include in extraction - bool_index = base64_row.index.isin(("content",)) - row_data = base64_row[~bool_index] - task_props["params"]["row_data"] = row_data - # Get source_id - source_id = base64_row["source_id"] if "source_id" in base64_row.index else None - # Decode the base64 content - doc_bytes = base64.b64decode(base64_content) - - # Load the document - doc_stream = io.BytesIO(doc_bytes) - - # Type of extraction method to use - extract_method = task_props.get("method", "python_docx") - extract_params = task_props.get("params", {}) - if not hasattr(docx, extract_method): - extract_method = default - try: - func = getattr(docx, extract_method, default) - logger.debug("Running extraction method: %s", extract_method) - extracted_data = func(doc_stream, **extract_params) - - return extracted_data - - except Exception as e: - traceback.print_exc() - log_error_message = f"Error loading extractor:{e}" - logger.error(log_error_message) - logger.error(f"Failed on file:{source_id}") - - # Propagate error back and tag message as failed. - exception_tag = create_exception_tag(error_message=log_error_message, source_id=source_id) - - return exception_tag - try: # Apply the helper function to each row in the 'content' column - _decode_and_extract = functools.partial(decode_and_extract, task_props=task_props) - logger.debug(f"processing ({task_props.get('method', None)})") + _decode_and_extract = functools.partial( + decode_and_extract, task_props=task_props, validated_config=validated_config, trace_info=trace_info + ) sr_extraction = df.apply(_decode_and_extract, axis=1) sr_extraction = sr_extraction.explode().dropna() @@ -92,12 +103,14 @@ def decode_and_extract(base64_row, task_props, default="python_docx"): except Exception as e: traceback.print_exc() logger.error(f"Failed to extract text from document: {e}") + raise return df def generate_docx_extractor_stage( c: Config, + extractor_config: dict, task: str = "docx-extract", task_desc: str = "docx_content_extractor", pe_count: int = 24, @@ -109,6 +122,8 @@ def generate_docx_extractor_stage( ---------- c : Config Morpheus global configuration object + extractor_config : dict + Configuration parameters for document content extractor. task : str The task name to match for the stage worker function. task_desc : str @@ -121,7 +136,9 @@ def generate_docx_extractor_stage( MultiProcessingBaseStage A Morpheus stage with applied worker function. """ + validated_config = DocxExtractorSchema(**extractor_config) + _wrapped_process_fn = functools.partial(_process_docx_bytes, validated_config=validated_config) return MultiProcessingBaseStage( - c=c, pe_count=pe_count, task=task, task_desc=task_desc, process_fn=_process_docx_bytes, document_type="docx" + c=c, pe_count=pe_count, task=task, task_desc=task_desc, process_fn=_wrapped_process_fn, document_type="docx" ) diff --git a/src/nv_ingest/stages/extractors/image_extractor_stage.py b/src/nv_ingest/stages/extractors/image_extractor_stage.py index 9bf97029..c0e90c28 100644 --- a/src/nv_ingest/stages/extractors/image_extractor_stage.py +++ b/src/nv_ingest/stages/extractors/image_extractor_stage.py @@ -81,8 +81,6 @@ def decode_and_extract( source_id = base64_row["source_id"] if "source_id" in base64_row.index else None # Decode the base64 content image_bytes = base64.b64decode(base64_content) - - # Load the PDF image_stream = io.BytesIO(image_bytes) # Type of extraction method to use diff --git a/src/nv_ingest/stages/nim/chart_extraction.py b/src/nv_ingest/stages/nim/chart_extraction.py index 11d6ffa4..3890c62e 100644 --- a/src/nv_ingest/stages/nim/chart_extraction.py +++ b/src/nv_ingest/stages/nim/chart_extraction.py @@ -10,7 +10,6 @@ from typing import Tuple import pandas as pd -import tritonclient.grpc as grpcclient from morpheus.config import Config from nv_ingest.schemas.chart_extractor_schema import ChartExtractorSchema @@ -67,6 +66,7 @@ def _update_metadata(row: pd.Series, cached_client: NimClient, deplot_client: Ni (content_metadata.get("type") != "structured") or (content_metadata.get("subtype") != "chart") or (chart_metadata is None) + or (base64_image in [None, ""]) ): return metadata @@ -190,10 +190,8 @@ def _extract_chart_data( logger.error("Error occurred while extracting chart data.", exc_info=True) raise finally: - if isinstance(cached_client, grpcclient.InferenceServerClient): - cached_client.close() - if isinstance(deplot_client, grpcclient.InferenceServerClient): - deplot_client.close() + cached_client.close() + deplot_client.close() def generate_chart_extractor_stage( diff --git a/src/nv_ingest/stages/nim/table_extraction.py b/src/nv_ingest/stages/nim/table_extraction.py index 95bcf9cd..dd803af1 100644 --- a/src/nv_ingest/stages/nim/table_extraction.py +++ b/src/nv_ingest/stages/nim/table_extraction.py @@ -67,6 +67,7 @@ def _update_metadata(row: pd.Series, paddle_client: NimClient, trace_info: Dict) (content_metadata.get("type") != "structured") or (content_metadata.get("subtype") != "table") or (table_metadata is None) + or (base64_image in [None, ""]) ): return metadata @@ -172,8 +173,7 @@ def _extract_table_data( logger.error("Error occurred while extracting table data.", exc_info=True) raise finally: - if isinstance(paddle_client, NimClient): - paddle_client.close() + paddle_client.close() def generate_table_extractor_stage( diff --git a/src/nv_ingest/stages/pptx_extractor_stage.py b/src/nv_ingest/stages/pptx_extractor_stage.py index 9512a2f4..efbf848b 100644 --- a/src/nv_ingest/stages/pptx_extractor_stage.py +++ b/src/nv_ingest/stages/pptx_extractor_stage.py @@ -8,6 +8,7 @@ import io import logging import traceback +from typing import Any, Optional, Dict import pandas as pd from pydantic import BaseModel @@ -15,12 +16,61 @@ from nv_ingest.extraction_workflows import pptx from nv_ingest.stages.multiprocessing_stage import MultiProcessingBaseStage +from nv_ingest.schemas.pptx_extractor_schema import PPTXExtractorSchema from nv_ingest.util.exception_handlers.pdf import create_exception_tag logger = logging.getLogger(f"morpheus.{__name__}") -def _process_pptx_bytes(df, task_props): +def decode_and_extract(base64_row, task_props, validated_config: Any, trace_info: Dict, default="python_pptx"): + if isinstance(task_props, BaseModel): + task_props = task_props.model_dump() + + # Base64 content to extract + base64_content = base64_row["content"] + # Row data to include in extraction + bool_index = base64_row.index.isin(("content",)) + row_data = base64_row[~bool_index] + task_props["params"]["row_data"] = row_data + # Get source_id + source_id = base64_row["source_id"] if "source_id" in base64_row.index else None + # Decode the base64 content + pptx_bytes = base64.b64decode(base64_content) + + # Load the PPTX + pptx_stream = io.BytesIO(pptx_bytes) + + # Type of extraction method to use + extract_method = task_props.get("method", "python_pptx") + extract_params = task_props.get("params", {}) + if not hasattr(pptx, extract_method): + extract_method = default + try: + if validated_config.pptx_extraction_config is not None: + extract_params["pptx_extraction_config"] = validated_config.pptx_extraction_config + + if trace_info is not None: + extract_params["trace_info"] = trace_info + + func = getattr(pptx, extract_method, default) + logger.debug("Running extraction method: %s", extract_method) + extracted_data = func(pptx_stream, **extract_params) + + return extracted_data + + except Exception as e: + traceback.print_exc() + log_error_message = f"Error loading extractor:{e}" + logger.error(log_error_message) + logger.error(f"Failed on file:{source_id}") + + # Propagate error back and tag message as failed. + exception_tag = create_exception_tag(error_message=log_error_message, source_id=source_id) + + return exception_tag + + +def _process_pptx_bytes(df, task_props: dict, validated_config: Any, trace_info: Optional[Dict[str, Any]] = None): """ Processes a cuDF DataFrame containing PPTX files in base64 encoding. Each PPTX's content is replaced with its extracted text. @@ -32,52 +82,13 @@ def _process_pptx_bytes(df, task_props): Returns: - A pandas DataFrame with the PPTX content replaced by the extracted text. """ - - def decode_and_extract(base64_row, task_props, default="python_pptx"): - if isinstance(task_props, BaseModel): - task_props = task_props.model_dump() - - # Base64 content to extract - base64_content = base64_row["content"] - # Row data to include in extraction - bool_index = base64_row.index.isin(("content",)) - row_data = base64_row[~bool_index] - task_props["params"]["row_data"] = row_data - # Get source_id - source_id = base64_row["source_id"] if "source_id" in base64_row.index else None - # Decode the base64 content - pptx_bytes = base64.b64decode(base64_content) - - # Load the PPTX - pptx_stream = io.BytesIO(pptx_bytes) - - # Type of extraction method to use - extract_method = task_props.get("method", "python_pptx") - extract_params = task_props.get("params", {}) - if not hasattr(pptx, extract_method): - extract_method = default - try: - func = getattr(pptx, extract_method, default) - logger.debug("Running extraction method: %s", extract_method) - extracted_data = func(pptx_stream, **extract_params) - - return extracted_data - - except Exception as e: - traceback.print_exc() - log_error_message = f"Error loading extractor:{e}" - logger.error(log_error_message) - logger.error(f"Failed on file:{source_id}") - - # Propagate error back and tag message as failed. - exception_tag = create_exception_tag(error_message=log_error_message, source_id=source_id) - - return exception_tag - try: # Apply the helper function to each row in the 'content' column - _decode_and_extract = functools.partial(decode_and_extract, task_props=task_props) - logger.debug(f"processing ({task_props.get('method', None)})") + _decode_and_extract = functools.partial( + decode_and_extract, task_props=task_props, validated_config=validated_config, trace_info=trace_info + ) + + # logger.debug(f"processing ({task_props.get('method', None)})") sr_extraction = df.apply(_decode_and_extract, axis=1) sr_extraction = sr_extraction.explode().dropna() @@ -91,12 +102,14 @@ def decode_and_extract(base64_row, task_props, default="python_pptx"): except Exception as e: traceback.print_exc() logger.error(f"Failed to extract text from PPTX: {e}") + raise return df def generate_pptx_extractor_stage( c: Config, + extractor_config: dict, task: str = "pptx-extract", task_desc: str = "pptx_content_extractor", pe_count: int = 24, @@ -108,6 +121,8 @@ def generate_pptx_extractor_stage( ---------- c : Config Morpheus global configuration object + extractor_config : dict + Configuration parameters for document content extractor. task : str The task name to match for the stage worker function. task_desc : str @@ -121,6 +136,9 @@ def generate_pptx_extractor_stage( A Morpheus stage with applied worker function. """ + validated_config = PPTXExtractorSchema(**extractor_config) + _wrapped_process_fn = functools.partial(_process_pptx_bytes, validated_config=validated_config) + return MultiProcessingBaseStage( - c=c, pe_count=pe_count, task=task, task_desc=task_desc, process_fn=_process_pptx_bytes, document_type="pptx" + c=c, pe_count=pe_count, task=task, task_desc=task_desc, process_fn=_wrapped_process_fn, document_type="pptx" ) diff --git a/src/nv_ingest/util/converters/formats.py b/src/nv_ingest/util/converters/formats.py new file mode 100644 index 00000000..cfbe5dd8 --- /dev/null +++ b/src/nv_ingest/util/converters/formats.py @@ -0,0 +1,70 @@ +# SPDX-FileCopyrightText: Copyright (c) 2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-License-Identifier: LicenseRef-NvidiaProprietary +# +# NVIDIA CORPORATION, its affiliates and licensors retain all intellectual +# property and proprietary rights in and to this material, related +# documentation and any modifications thereto. Any use, reproduction, +# disclosure or distribution of this material and related documentation +# without an express license agreement from NVIDIA CORPORATION or +# its affiliates is strictly prohibited. + +# pylint: skip-file + +import json + + +def ingest_json_results_to_blob(result_content): + """ + Parse a JSON string or BytesIO object, combine and sort entries, and create a blob string. + + Returns: + str: The generated blob string. + """ + try: + # Load the JSON data + data = json.loads(result_content) if isinstance(result_content, str) else json.loads(result_content) + data = data["data"] + + # Smarter sorting: by page, then structured objects by x0, y0 + def sorting_key(entry): + page = entry["metadata"]["content_metadata"]["page_number"] + if entry["document_type"] == "structured": + # Use table location's x0 and y0 as secondary keys + x0 = entry["metadata"]["table_metadata"]["table_location"][0] + y0 = entry["metadata"]["table_metadata"]["table_location"][1] + else: + # Non-structured objects are sorted after structured ones + x0 = float("inf") + y0 = float("inf") + return page, x0, y0 + + data.sort(key=sorting_key) + + # Initialize the blob string + blob = [] + + for entry in data: + document_type = entry.get("document_type", "") + + if document_type == "structured": + # Add table content to the blob + blob.append(entry["metadata"]["table_metadata"]["table_content"]) + blob.append("\n") + + elif document_type == "text": + # Add content to the blob + blob.append(entry["metadata"]["content"]) + blob.append("\n") + + elif document_type == "image": + # Add image caption to the blob + caption = entry["metadata"]["image_metadata"].get("caption", "") + blob.append(f"image_caption:[{caption}]") + blob.append("\n") + + # Join all parts of the blob into a single string + return "".join(blob) + + except Exception as e: + print(f"[ERROR] An error occurred while processing JSON content: {e}") + return "" diff --git a/src/nv_ingest/util/nim/cached.py b/src/nv_ingest/util/nim/cached.py index 56513d08..1a7bf0c9 100644 --- a/src/nv_ingest/util/nim/cached.py +++ b/src/nv_ingest/util/nim/cached.py @@ -119,7 +119,7 @@ def parse_output(self, response: Any, protocol: str, data: Optional[Dict[str, An else: raise ValueError("Invalid protocol specified. Must be 'grpc' or 'http'.") - def process_inference_results(self, output: Any, **kwargs) -> Any: + def process_inference_results(self, output: Any, protocol: str, **kwargs) -> Any: """ Process inference results for the Cached model. diff --git a/src/nv_ingest/util/nim/deplot.py b/src/nv_ingest/util/nim/deplot.py index 9cf6175d..63f16a3b 100644 --- a/src/nv_ingest/util/nim/deplot.py +++ b/src/nv_ingest/util/nim/deplot.py @@ -133,7 +133,7 @@ def parse_output(self, response: Any, protocol: str, data: Optional[Dict[str, An else: raise ValueError("Invalid protocol specified. Must be 'grpc' or 'http'.") - def process_inference_results(self, output: Any, **kwargs) -> Any: + def process_inference_results(self, output: Any, protocol: str, **kwargs) -> Any: """ Process inference results for the Deplot model. diff --git a/src/nv_ingest/util/nim/helpers.py b/src/nv_ingest/util/nim/helpers.py index 61a41634..a692265f 100644 --- a/src/nv_ingest/util/nim/helpers.py +++ b/src/nv_ingest/util/nim/helpers.py @@ -75,7 +75,7 @@ def prepare_data_for_inference(self, data: dict): """ raise NotImplementedError("Subclasses should implement this method") - def process_inference_results(self, output_array, **kwargs): + def process_inference_results(self, output_array, protocol: str, **kwargs): """ Process the inference results from the model. @@ -206,7 +206,7 @@ def infer(self, data: dict, model_name: str, **kwargs) -> Any: response, protocol=self.protocol, data=prepared_data, **kwargs ) results = self.model_interface.process_inference_results( - parsed_output, original_image_shapes=data.get("original_image_shapes"), **kwargs + parsed_output, original_image_shapes=data.get("original_image_shapes"), protocol=self.protocol, **kwargs ) return results @@ -624,8 +624,8 @@ def call_audio_inference_model(client, audio_content: str, audio_id: str, trace_ headers = client["headers"] payload = {"audio_content": audio_content, "audio_id": audio_id} - response = requests.post(url, json=payload, headers=headers) - + response = requests.post(url, json=payload, headers=headers) + response.raise_for_status() # Raise an exception for HTTP errors # Parse the JSON response @@ -639,8 +639,3 @@ def call_audio_inference_model(client, audio_content: str, audio_id: str, trace_ raise RuntimeError(f"An error occurred during inference: {e}") return json_response - - - - - diff --git a/src/nv_ingest/util/nim/yolox.py b/src/nv_ingest/util/nim/yolox.py index d07f184e..831c4e62 100644 --- a/src/nv_ingest/util/nim/yolox.py +++ b/src/nv_ingest/util/nim/yolox.py @@ -16,7 +16,6 @@ import numpy as np import torch import torchvision -from packaging import version as pkgversion from PIL import Image from nv_ingest.util.image_processing.transforms import scale_image_to_encoding_size @@ -44,20 +43,6 @@ class YoloxPageElementsModelInterface(ModelInterface): An interface for handling inference with a Yolox object detection model, supporting both gRPC and HTTP protocols. """ - def __init__( - self, - yolox_version: Optional[str] = None, - ): - """ - Initialize the YOLOX model interface. - - Parameters - ---------- - yolox_version : str, optional - The version of the YOLOX model (default: None). - """ - self.yolox_version = yolox_version - def name( self, ) -> str: @@ -70,7 +55,7 @@ def name( The name of the model interface. """ - return f"yolox-page-elements (version {self.yolox_version})" + return "yolox-page-elements" def prepare_data_for_inference(self, data: Dict[str, Any]) -> Dict[str, Any]: """ @@ -86,16 +71,16 @@ def prepare_data_for_inference(self, data: Dict[str, Any]) -> Dict[str, Any]: dict The updated data dictionary with resized images and original image shapes. """ + if (not isinstance(data, dict)) or ("images" not in data): + raise KeyError("Input data must be a dictionary containing an 'images' key with a list of images.") + + if not all(isinstance(x, np.ndarray) for x in data["images"]): + raise ValueError("All elements in the 'images' list must be numpy.ndarray objects.") original_images = data["images"] - # Our yolox model expects images to be resized to 1024x1024 - resized_images = [ - resize_image(image, (YOLOX_IMAGE_PREPROC_WIDTH, YOLOX_IMAGE_PREPROC_HEIGHT)) for image in original_images - ] data["original_image_shapes"] = [image.shape for image in original_images] - data["resized_images"] = resized_images - return data # Return data with added 'resized_images' key + return data def format_input(self, data: Dict[str, Any], protocol: str) -> Any: """ @@ -121,16 +106,18 @@ def format_input(self, data: Dict[str, Any], protocol: str) -> Any: if protocol == "grpc": logger.debug("Formatting input for gRPC Yolox model") + # Our yolox-page-elements model (grPC) expects images to be resized to 1024x1024 + resized_images = [ + resize_image(image, (YOLOX_IMAGE_PREPROC_WIDTH, YOLOX_IMAGE_PREPROC_HEIGHT)) for image in data["images"] + ] # Reorder axes to match model input (batch, channels, height, width) - input_array = np.einsum("bijk->bkij", data["resized_images"]).astype(np.float32) + input_array = np.einsum("bijk->bkij", resized_images).astype(np.float32) return input_array elif protocol == "http": logger.debug("Formatting input for HTTP Yolox model") - # Additional lists to keep track of scaling factors and new sizes - scaling_factors = [] content_list = [] - for image in data["resized_images"]: + for image in data["images"]: # Convert numpy array to PIL Image image_pil = Image.fromarray((image * 255).astype(np.uint8)) original_size = image_pil.size # Should be (1024, 1024) @@ -148,26 +135,12 @@ def format_input(self, data: Dict[str, Any], protocol: str) -> Any: if new_size != original_size: logger.warning(f"Image was scaled from {original_size} to {new_size} to meet size constraints.") - # Compute scaling factor - scaling_factor_x = new_size[0] / YOLOX_IMAGE_PREPROC_WIDTH - scaling_factor_y = new_size[1] / YOLOX_IMAGE_PREPROC_HEIGHT - scaling_factors.append((scaling_factor_x, scaling_factor_y)) - # Add to content_list - if self._is_version_early_access_legacy_api(): - content = {"type": "image_url", "image_url": {"url": f"data:image/png;base64,{scaled_image_b64}"}} - else: - content = {"type": "image_url", "url": f"data:image/png;base64,{scaled_image_b64}"} + content = {"type": "image_url", "url": f"data:image/png;base64,{scaled_image_b64}"} content_list.append(content) - # Store scaling factors in data - data["scaling_factors"] = scaling_factors - - if self._is_version_early_access_legacy_api(): - payload = {"messages": [{"content": content_list}]} - else: - payload = {"input": content_list} + payload = {"input": content_list} return payload else: @@ -203,108 +176,30 @@ def parse_output(self, response: Any, protocol: str, data: Optional[Dict[str, An elif protocol == "http": logger.debug("Parsing output from HTTP Yolox model") - is_legacy_version = self._is_version_early_access_legacy_api() - - # Convert JSON response to numpy array similar to gRPC response - if is_legacy_version: - # Convert response data to GA API format. - response_data = response.get("data", []) - batch_results = [] - for idx, detections in enumerate(response_data): - curr_batch = {"index": idx, "bounding_boxes": {}} - for obj in detections: - obj_type = obj.get("type", "") - bboxes = obj.get("bboxes", []) - if not obj_type: - continue - if obj_type not in curr_batch: - curr_batch["bounding_boxes"][obj_type] = [] - curr_batch["bounding_boxes"][obj_type].extend(bboxes) - batch_results.append(curr_batch) - else: - batch_results = response.get("data", []) - - batch_size = len(batch_results) processed_outputs = [] - scaling_factors = data.get("scaling_factors", [(1.0, 1.0)] * batch_size) - - x_min_label = "xmin" if is_legacy_version else "x_min" - y_min_label = "ymin" if is_legacy_version else "y_min" - x_max_label = "xmax" if is_legacy_version else "x_max" - y_max_label = "ymax" if is_legacy_version else "y_max" - confidence_label = "confidence" - + batch_results = response.get("data", []) for detections in batch_results: - idx = int(detections["index"]) - scale_factor_x, scale_factor_y = scaling_factors[idx] - image_width = YOLOX_IMAGE_PREPROC_WIDTH - image_height = YOLOX_IMAGE_PREPROC_HEIGHT + new_bounding_boxes = {"table": [], "chart": [], "title": []} - # Initialize an empty tensor for detections - max_detections = 100 - detection_tensor = np.zeros((max_detections, 85), dtype=np.float32) - - index = 0 bounding_boxes = detections.get("bounding_boxes", []) for obj_type, bboxes in bounding_boxes.items(): for bbox in bboxes: - if index >= max_detections: - break - xmin_norm = bbox[x_min_label] - ymin_norm = bbox[y_min_label] - xmax_norm = bbox[x_max_label] - ymax_norm = bbox[y_max_label] - confidence = bbox[confidence_label] - - # Convert normalized coordinates to absolute pixel values in scaled image - xmin_scaled = xmin_norm * image_width * scale_factor_x - ymin_scaled = ymin_norm * image_height * scale_factor_y - xmax_scaled = xmax_norm * image_width * scale_factor_x - ymax_scaled = ymax_norm * image_height * scale_factor_y - - # Adjust coordinates back to 1024x1024 image space - xmin = xmin_scaled / scale_factor_x - ymin = ymin_scaled / scale_factor_y - xmax = xmax_scaled / scale_factor_x - ymax = ymax_scaled / scale_factor_y - - # YOLOX expects bbox format: center_x, center_y, width, height - center_x = (xmin + xmax) / 2 - center_y = (ymin + ymax) / 2 - width = xmax - xmin - height = ymax - ymin - - # Set the bbox coordinates - detection_tensor[index, 0] = center_x - detection_tensor[index, 1] = center_y - detection_tensor[index, 2] = width - detection_tensor[index, 3] = height - - # Objectness score - detection_tensor[index, 4] = confidence - - class_index = {"table": 0, "chart": 1, "title": 2}.get(obj_type, -1) - if class_index >= 0: - detection_tensor[index, 5 + class_index] = 1.0 - - index += 1 - - # Trim the detection tensor to the actual number of detections - detection_tensor = detection_tensor[:index, :] - processed_outputs.append(detection_tensor) - - # Pad batch if necessary - max_detections_in_batch = max([output.shape[0] for output in processed_outputs]) if processed_outputs else 0 - batch_output_array = np.zeros((batch_size, max_detections_in_batch, 85), dtype=np.float32) - for i, output in enumerate(processed_outputs): - batch_output_array[i, : output.shape[0], :] = output - - return batch_output_array + xmin = bbox["x_min"] + ymin = bbox["y_min"] + xmax = bbox["x_max"] + ymax = bbox["y_max"] + confidence = bbox["confidence"] + + new_bounding_boxes[obj_type].append([xmin, ymin, xmax, ymax, confidence]) + + processed_outputs.append(new_bounding_boxes) + + return processed_outputs else: raise ValueError("Invalid protocol specified. Must be 'grpc' or 'http'.") - def process_inference_results(self, output_array: np.ndarray, **kwargs) -> List[Dict[str, Any]]: + def process_inference_results(self, output: Any, protocol: str, **kwargs) -> List[Dict[str, Any]]: """ Process the results of the Yolox model inference and return the final annotations. @@ -320,7 +215,6 @@ def process_inference_results(self, output_array: np.ndarray, **kwargs) -> List[ list[dict] A list of annotation dictionaries for each image in the batch. """ - original_image_shapes = kwargs.get("original_image_shapes", []) num_classes = kwargs.get("num_classes", YOLOX_NUM_CLASSES) conf_thresh = kwargs.get("conf_thresh", YOLOX_CONF_THRESHOLD) @@ -328,14 +222,22 @@ def process_inference_results(self, output_array: np.ndarray, **kwargs) -> List[ min_score = kwargs.get("min_score", YOLOX_MIN_SCORE) final_thresh = kwargs.get("final_thresh", YOLOX_FINAL_SCORE) - pred = postprocess_model_prediction(output_array, num_classes, conf_thresh, iou_thresh, class_agnostic=True) + if protocol == "http": + # For http, the output already has postprocessing applied. Skip to table/chart expansion. + results = output - results = postprocess_results(pred, original_image_shapes, min_score=min_score) + elif protocol == "grpc": + # For grpc, apply the same NIM postprocessing. + pred = postprocess_model_prediction(output, num_classes, conf_thresh, iou_thresh, class_agnostic=True) + results = postprocess_results(pred, original_image_shapes, min_score=min_score) - annotation_dicts = [expand_chart_bboxes(annotation_dict) for annotation_dict in results] + # Table/chart expansion is "business logic" specific to nv-ingest + annotation_dicts = [expand_table_bboxes(annotation_dict) for annotation_dict in results] + annotation_dicts = [expand_chart_bboxes(annotation_dict) for annotation_dict in annotation_dicts] inference_results = [] # Filter out bounding boxes below the final threshold + # This final thresholding is "business logic" specific to nv-ingest for annotation_dict in annotation_dicts: new_dict = {} if "table" in annotation_dict: @@ -348,9 +250,6 @@ def process_inference_results(self, output_array: np.ndarray, **kwargs) -> List[ return inference_results - def _is_version_early_access_legacy_api(self): - return self.yolox_version and (pkgversion.parse(self.yolox_version) < pkgversion.parse("1.0.0-rc0")) - def postprocess_model_prediction(prediction, num_classes, conf_thre=0.7, nms_thre=0.45, class_agnostic=False): # Convert numpy array to torch tensor @@ -423,12 +322,14 @@ def postprocess_results(results, original_image_shapes, min_score=0.0): Keep only bboxes with high enough confidence. """ - labels = ["table", "chart", "title"] + class_labels = ["table", "chart", "title"] out = [] for original_image_shape, result in zip(original_image_shapes, results): + annotation_dict = {label: [] for label in class_labels} + if result is None: - out.append({}) + out.append(annotation_dict) continue try: @@ -447,29 +348,17 @@ def postprocess_results(results, original_image_shapes, min_score=0.0): bboxes[:, [1, 3]] /= original_image_shape[0] bboxes = np.clip(bboxes, 0.0, 1.0) - label_idxs = result[:, 6] + labels = result[:, 6] scores = scores[scores > min_score] except Exception as e: raise ValueError(f"Error in postprocessing {result.shape} and {original_image_shape}: {e}") - annotation_dict = {label: [] for label in labels} - - # bboxes are in format [x_min, y_min, x_max, y_max] - for j in range(len(bboxes)): - label = labels[int(label_idxs[j])] - bbox = bboxes[j] - score = scores[j] - - # additional preprocessing for tables: extend the upper bounds to capture titles if any. - if label == "table": - height = bbox[3] - bbox[1] - bbox[1] = (bbox[1] - height * 0.2).clip(0.0, 1.0) - - annotation_dict[label].append([round(float(x), 4) for x in np.concatenate((bbox, [score]))]) + for box, score, label in zip(bboxes, scores, labels): + class_name = class_labels[int(label)] + annotation_dict[class_name].append([round(float(x), 4) for x in np.concatenate((box, [score]))]) out.append(annotation_dict) - # {label: [[x1, y1, x2, y2, confidence], ...], ...} return out @@ -493,6 +382,37 @@ def resize_image(image, target_img_size): return image +def expand_table_bboxes(annotation_dict, labels=None): + """ + Additional preprocessing for tables: extend the upper bounds to capture titles if any. + Args: + annotation_dict: output of postprocess_results, a dictionary with keys "table", "figure", "title" + + Returns: + annotation_dict: same as input, with expanded bboxes for charts + + """ + if not labels: + labels = ["table", "chart", "title"] + + if not annotation_dict or len(annotation_dict["table"]) == 0: + return annotation_dict + + new_annotation_dict = {label: [] for label in labels} + + for label, bboxes in annotation_dict.items(): + for bbox_and_score in bboxes: + bbox, score = bbox_and_score[:4], bbox_and_score[4] + + if label == "table": + height = bbox[3] - bbox[1] + bbox[1] = max(0.0, min(1.0, bbox[1] - height * 0.2)) + + new_annotation_dict[label].append([round(float(x), 4) for x in bbox + [score]]) + + return new_annotation_dict + + def expand_chart_bboxes(annotation_dict, labels=None): """ Expand bounding boxes of charts and titles based on the bounding boxes of the other class. diff --git a/src/nv_ingest/util/pdf/metadata_aggregators.py b/src/nv_ingest/util/pdf/metadata_aggregators.py index 8c6237f7..3fac696e 100644 --- a/src/nv_ingest/util/pdf/metadata_aggregators.py +++ b/src/nv_ingest/util/pdf/metadata_aggregators.py @@ -29,7 +29,6 @@ from nv_ingest.util.exception_handlers.pdf import pdfium_exception_handler -# TODO(Devin): Shift to this, since there is no difference between ImageTable and ImageChart @dataclass class CroppedImageWithContent: content: str diff --git a/src/nv_ingest/util/pipeline/pipeline_builders.py b/src/nv_ingest/util/pipeline/pipeline_builders.py index 5a1c25cb..4d2d519a 100644 --- a/src/nv_ingest/util/pipeline/pipeline_builders.py +++ b/src/nv_ingest/util/pipeline/pipeline_builders.py @@ -30,9 +30,8 @@ def setup_ingestion_pipeline( ######################################################################################################## pdf_extractor_stage = add_pdf_extractor_stage(pipe, morpheus_pipeline_config, ingest_config, default_cpu_count) image_extractor_stage = add_image_extractor_stage(pipe, morpheus_pipeline_config, ingest_config, default_cpu_count) - docx_extractor_stage = add_docx_extractor_stage(pipe, morpheus_pipeline_config, default_cpu_count) - pptx_extractor_stage = add_pptx_extractor_stage(pipe, morpheus_pipeline_config, default_cpu_count) - ## audio extraction + docx_extractor_stage = add_docx_extractor_stage(pipe, morpheus_pipeline_config, ingest_config, default_cpu_count) + pptx_extractor_stage = add_pptx_extractor_stage(pipe, morpheus_pipeline_config, ingest_config, default_cpu_count) audio_extractor_stage = add_audio_extractor_stage(pipe, morpheus_pipeline_config, ingest_config, default_cpu_count) ######################################################################################################## @@ -80,7 +79,6 @@ def setup_ingestion_pipeline( pipe.add_edge(docx_extractor_stage, pptx_extractor_stage) pipe.add_edge(pptx_extractor_stage, audio_extractor_stage) pipe.add_edge(audio_extractor_stage, image_dedup_stage) - pipe.add_edge(image_dedup_stage, image_filter_stage) pipe.add_edge(image_filter_stage, table_extraction_stage) pipe.add_edge(table_extraction_stage, chart_extraction_stage) diff --git a/src/nv_ingest/util/pipeline/stage_builders.py b/src/nv_ingest/util/pipeline/stage_builders.py index b5153cbb..8780e757 100644 --- a/src/nv_ingest/util/pipeline/stage_builders.py +++ b/src/nv_ingest/util/pipeline/stage_builders.py @@ -274,16 +274,28 @@ def add_image_extractor_stage(pipe, morpheus_pipeline_config, ingest_config, def extractor_config=image_extractor_config, pe_count=8, task="extract", - task_desc="docx_content_extractor", + task_desc="image_content_extractor", ) ) return image_extractor_stage -def add_docx_extractor_stage(pipe, morpheus_pipeline_config, default_cpu_count): +def add_docx_extractor_stage(pipe, morpheus_pipeline_config, ingest_config, default_cpu_count): + yolox_grpc, yolox_http, yolox_auth, yolox_protocol = get_table_detection_service("yolox") + docx_extractor_config = ingest_config.get( + "docx_extraction_module", + { + "docx_extraction_config": { + "yolox_endpoints": (yolox_grpc, yolox_http), + "yolox_infer_protocol": yolox_protocol, + "auth_token": yolox_auth, + } + }, + ) docx_extractor_stage = pipe.add_stage( generate_docx_extractor_stage( morpheus_pipeline_config, + extractor_config=docx_extractor_config, pe_count=1, task="extract", task_desc="docx_content_extractor", @@ -292,10 +304,22 @@ def add_docx_extractor_stage(pipe, morpheus_pipeline_config, default_cpu_count): return docx_extractor_stage -def add_pptx_extractor_stage(pipe, morpheus_pipeline_config, default_cpu_count): +def add_pptx_extractor_stage(pipe, morpheus_pipeline_config, ingest_config, default_cpu_count): + yolox_grpc, yolox_http, yolox_auth, yolox_protocol = get_table_detection_service("yolox") + pptx_extractor_config = ingest_config.get( + "pptx_extraction_module", + { + "pptx_extraction_config": { + "yolox_endpoints": (yolox_grpc, yolox_http), + "yolox_infer_protocol": yolox_protocol, + "auth_token": yolox_auth, + } + }, + ) pptx_extractor_stage = pipe.add_stage( generate_pptx_extractor_stage( morpheus_pipeline_config, + extractor_config=pptx_extractor_config, pe_count=1, task="extract", task_desc="pptx_content_extractor", @@ -332,17 +356,20 @@ def get_audio_retrieval_service(env_var_prefix): return grpc_endpoint, http_endpoint, auth_token, infer_protocol + def add_audio_extractor_stage(pipe, morpheus_pipeline_config, ingest_config, default_cpu_count): audio_grpc, audio_http, audio_auth, audio_infer_protocol = get_audio_retrieval_service("audio") - audio_extractor_config = ingest_config.get("audio_extraction_module", - { - "audio_extraction_config": { - "audio_endpoints": (audio_grpc, audio_http), - "audio_infer_protocol": audio_infer_protocol, - "auth_token": audio_auth, - # All auth tokens are the same for the moment - } - }) + audio_extractor_config = ingest_config.get( + "audio_extraction_module", + { + "audio_extraction_config": { + "audio_endpoints": (audio_grpc, audio_http), + "audio_infer_protocol": audio_infer_protocol, + "auth_token": audio_auth, + # All auth tokens are the same for the moment + } + }, + ) audio_extractor_stage = pipe.add_stage( generate_audio_extractor_stage( morpheus_pipeline_config, diff --git a/src/util/image_viewer.py b/src/util/image_viewer.py index b47ccbdd..cebac902 100644 --- a/src/util/image_viewer.py +++ b/src/util/image_viewer.py @@ -31,12 +31,33 @@ def load_images_from_json(json_file_path): with open(json_file_path, "r") as file: data = json.load(file) + def create_default_image(): + """Create a solid black 300×300 image.""" + width, height = 300, 300 + default_img = Image.new("RGB", (width, height), color="black") + return default_img + images = [] for item in data: # Assuming the JSON is a list of objects if item["document_type"] in ("image", "structured"): - image_data = base64.b64decode(item["metadata"]["content"]) - image = Image.open(BytesIO(image_data)) - images.append(image) + content = item.get("metadata", {}).get("content", "") + # Check if content is missing or empty + if not content: + images.append(create_default_image()) + continue + + # Attempt to decode and open the image + try: + image_data = base64.b64decode(content) + temp_image = Image.open(BytesIO(image_data)) + # Verify & re-open to ensure no corruption or errors + temp_image.verify() + temp_image = Image.open(BytesIO(image_data)) + images.append(temp_image) + except Exception: + # If there's any error decoding/reading the image, use the default + images.append(create_default_image()) + return images diff --git a/tests/nv_ingest/extraction_workflows/docx/test_docx_helper.py b/tests/nv_ingest/extraction_workflows/docx/test_docx_helper.py index e56d003d..341ea68c 100644 --- a/tests/nv_ingest/extraction_workflows/docx/test_docx_helper.py +++ b/tests/nv_ingest/extraction_workflows/docx/test_docx_helper.py @@ -9,6 +9,7 @@ import pytest from nv_ingest.extraction_workflows.docx.docx_helper import python_docx +from nv_ingest.schemas.metadata_schema import ImageTypeEnum @pytest.fixture @@ -37,6 +38,7 @@ def test_docx_all_text(doc_stream, document_df): extract_text=True, extract_images=False, extract_tables=False, + extract_charts=False, row_data=document_df.iloc[0], ) @@ -64,6 +66,7 @@ def test_docx_all_text(doc_stream, document_df): assert extracted_data[0][1]["source_metadata"]["source_id"] == "woods_frost" +@pytest.mark.xfail(reason="Table extract requires yolox, disabling for now") def test_docx_table(doc_stream, document_df): """ Validate text and table extraction. Table content is converted into markdown text. @@ -73,6 +76,7 @@ def test_docx_table(doc_stream, document_df): extract_text=True, extract_images=False, extract_tables=True, + extract_charts=False, row_data=document_df.iloc[0], ) @@ -108,11 +112,11 @@ def test_docx_image(doc_stream, document_df): doc_stream, extract_text=True, extract_images=True, - extract_tables=True, + extract_tables=False, + extract_charts=False, row_data=document_df.iloc[0], ) - expected_captions = ["*Figure 1: Snowy Woods*", "*Figure 2: Robert Frost*"] expected_text_cnt = 1 expected_image_cnt = 2 expected_entry_cnt = expected_image_cnt + expected_text_cnt @@ -133,11 +137,4 @@ def test_docx_image(doc_stream, document_df): assert extracted_data[idx][0] == "image" # validate image type - assert extracted_data[idx][1]["image_metadata"]["image_type"] == "jpeg" - - # validate captions - expected_caption = expected_captions[idx] - extracted_caption = extracted_data[idx][1]["image_metadata"]["caption"] - assert extracted_caption == expected_caption - - assert image_cnt == expected_image_cnt + assert extracted_data[idx][1]["image_metadata"]["image_type"] == ImageTypeEnum.image_type_1 diff --git a/tests/nv_ingest/extraction_workflows/image/test_image_handlers.py b/tests/nv_ingest/extraction_workflows/image/test_image_handlers.py index e4f41c5d..da0c358e 100644 --- a/tests/nv_ingest/extraction_workflows/image/test_image_handlers.py +++ b/tests/nv_ingest/extraction_workflows/image/test_image_handlers.py @@ -7,7 +7,6 @@ from nv_ingest.extraction_workflows.image.image_handlers import convert_svg_to_bitmap from nv_ingest.extraction_workflows.image.image_handlers import extract_table_and_chart_images from nv_ingest.extraction_workflows.image.image_handlers import load_and_preprocess_image -from nv_ingest.extraction_workflows.image.image_handlers import process_inference_results from nv_ingest.util.pdf.metadata_aggregators import CroppedImageWithContent @@ -119,142 +118,6 @@ def test_convert_svg_to_bitmap_large_svg(): assert np.all(result[:, :, 2] == 255) # Blue channel fully on -def test_process_inference_results_basic_case(): - """Test process_inference_results with a typical valid input.""" - - # Simulated model output array for a single image with several detections. - # Array format is (batch_size, num_detections, 85) - 80 classes + 5 box coordinates - # For simplicity, use random values for the boxes and class predictions. - output_array = np.zeros((1, 3, 85), dtype=np.float32) - - # Mock bounding box coordinates - output_array[0, 0, :4] = [0.5, 0.5, 0.2, 0.2] # x_center, y_center, width, height - output_array[0, 1, :4] = [0.6, 0.6, 0.2, 0.2] - output_array[0, 2, :4] = [0.7, 0.7, 0.2, 0.2] - - # Mock object confidence scores - output_array[0, :, 4] = [0.8, 0.9, 0.85] - - # Mock class scores (set class 1 with highest confidence for simplicity) - output_array[0, 0, 5 + 1] = 0.7 - output_array[0, 1, 5 + 1] = 0.75 - output_array[0, 2, 5 + 1] = 0.72 - - original_image_shapes = [(640, 640)] # Original shape of the image before resizing - - # Process inference results with thresholds that should retain all mock detections - results = process_inference_results( - output_array, - original_image_shapes, - num_classes=80, - conf_thresh=0.5, - iou_thresh=0.5, - min_score=0.1, - final_thresh=0.3, - ) - - # Check output structure - assert isinstance(results, list) - assert len(results) == 1 - assert isinstance(results[0], dict) - - # Validate bounding box scaling and structure - assert "chart" in results[0] or "table" in results[0] - if "chart" in results[0]: - assert isinstance(results[0]["chart"], list) - assert len(results[0]["chart"]) > 0 - # Check bounding box format for each detected "chart" item (5 values per box) - for bbox in results[0]["chart"]: - assert len(bbox) == 5 # [x1, y1, x2, y2, score] - assert bbox[4] >= 0.3 # score meets final threshold - - print("Processed inference results:", results) - - -def test_process_inference_results_multiple_images(): - """Test with multiple images to verify batch processing.""" - # Simulate model output with 2 images and 3 detections each - output_array = np.zeros((2, 3, 85), dtype=np.float32) - # Set bounding boxes and confidence for the mock detections - output_array[0, 0, :5] = [0.5, 0.5, 0.2, 0.2, 0.8] - output_array[0, 1, :5] = [0.6, 0.6, 0.2, 0.2, 0.7] - output_array[1, 0, :5] = [0.4, 0.4, 0.1, 0.1, 0.9] - # Assign class confidences for classes 0 and 1 - output_array[0, 0, 5 + 1] = 0.75 - output_array[0, 1, 5 + 1] = 0.65 - output_array[1, 0, 5 + 0] = 0.8 - - original_image_shapes = [(640, 640), (800, 800)] - - results = process_inference_results( - output_array, - original_image_shapes, - num_classes=80, - conf_thresh=0.5, - iou_thresh=0.5, - min_score=0.1, - final_thresh=0.3, - ) - - assert isinstance(results, list) - assert len(results) == 2 - for result in results: - assert isinstance(result, dict) - if "chart" in result: - assert all(len(bbox) == 5 and bbox[4] >= 0.3 for bbox in result["chart"]) - - -def test_process_inference_results_high_confidence_threshold(): - """Test with a high confidence threshold to verify filtering.""" - output_array = np.zeros((1, 5, 85), dtype=np.float32) - # Set low confidence scores below the threshold - output_array[0, :, 4] = [0.2, 0.3, 0.4, 0.4, 0.2] - output_array[0, :, 5] = [0.5] * 5 # Class confidence - - original_image_shapes = [(640, 640)] - - results = process_inference_results( - output_array, - original_image_shapes, - num_classes=80, - conf_thresh=0.9, # High confidence threshold - iou_thresh=0.5, - min_score=0.1, - final_thresh=0.3, - ) - - assert isinstance(results, list) - assert len(results) == 1 - assert results[0] == {} # No detections should pass the high confidence threshold - - -def test_process_inference_results_varied_num_classes(): - """Test compatibility with different model class counts.""" - output_array = np.zeros((1, 3, 25), dtype=np.float32) # 20 classes + 5 box coords - # Assign box, object confidence, and class scores - output_array[0, 0, :5] = [0.5, 0.5, 0.2, 0.2, 0.8] - output_array[0, 1, :5] = [0.6, 0.6, 0.3, 0.3, 0.7] - output_array[0, 0, 5 + 1] = 0.9 # Assign highest confidence to class 1 - - original_image_shapes = [(640, 640)] - - results = process_inference_results( - output_array, - original_image_shapes, - num_classes=20, # Different class count - conf_thresh=0.5, - iou_thresh=0.5, - min_score=0.1, - final_thresh=0.3, - ) - - assert isinstance(results, list) - assert len(results) == 1 - assert isinstance(results[0], dict) - assert "chart" in results[0] - assert len(results[0]["chart"]) > 0 # Verify detections processed correctly with 20 classes - - def crop_image(image: np.ndarray, bbox: Tuple[int, int, int, int]) -> np.ndarray: """Mock function to simulate cropping an image.""" h1, w1, h2, w2 = bbox diff --git a/tests/nv_ingest/extraction_workflows/pptx/test_pptx_helper.py b/tests/nv_ingest/extraction_workflows/pptx/test_pptx_helper.py index 1a85c95c..43e799d9 100644 --- a/tests/nv_ingest/extraction_workflows/pptx/test_pptx_helper.py +++ b/tests/nv_ingest/extraction_workflows/pptx/test_pptx_helper.py @@ -1,8 +1,7 @@ # SPDX-FileCopyrightText: Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. # All rights reserved. # SPDX-License-Identifier: Apache-2.0 - - +import json from io import BytesIO from textwrap import dedent @@ -220,6 +219,7 @@ def test_pptx(pptx_stream_with_text, document_df): extract_text=True, extract_images=False, extract_tables=False, + extract_charts=False, row_data=document_df.iloc[0], ) @@ -267,6 +267,7 @@ def test_pptx_with_multiple_runs_in_title(pptx_stream_with_multiple_runs_in_titl extract_text=True, extract_images=False, extract_tables=False, + extract_charts=False, row_data=document_df.iloc[0], ) @@ -299,6 +300,7 @@ def test_pptx_text_depth_presentation(pptx_stream_with_text, document_df): extract_text=True, extract_images=False, extract_tables=False, + extract_charts=False, row_data=document_df.iloc[0], text_depth="document", ) @@ -343,6 +345,7 @@ def test_pptx_text_depth_shape(pptx_stream_with_text, document_df): extract_text=True, extract_images=False, extract_tables=False, + extract_charts=False, row_data=document_df.iloc[0], text_depth="block", ) @@ -397,6 +400,7 @@ def test_pptx_text_depth_para_run(pptx_stream_with_text, document_df, text_depth extract_text=True, extract_images=False, extract_tables=False, + extract_charts=False, row_data=document_df.iloc[0], text_depth=text_depth, ) @@ -441,6 +445,7 @@ def test_pptx_bullet(pptx_stream_with_bullet, document_df): extract_text=True, extract_images=False, extract_tables=False, + extract_charts=False, row_data=document_df.iloc[0], ) @@ -473,6 +478,7 @@ def test_pptx_group(pptx_stream_with_group, document_df): extract_text=True, extract_images=False, extract_tables=False, + extract_charts=False, row_data=document_df.iloc[0], ) @@ -502,6 +508,7 @@ def test_pptx_table(pptx_stream_with_table, document_df): extract_text=True, extract_images=False, extract_tables=True, + extract_charts=False, row_data=document_df.iloc[0], ) @@ -524,7 +531,7 @@ def test_pptx_table(pptx_stream_with_table, document_df): | Baz | Qux | """ ) - assert extracted_data[0][1]["content"].rstrip() == expected_content.rstrip() + assert extracted_data[0][1]["table_metadata"]["table_content"].rstrip() == expected_content.rstrip() def test_pptx_image(pptx_stream_with_image, document_df): @@ -533,14 +540,17 @@ def test_pptx_image(pptx_stream_with_image, document_df): extract_text=True, extract_images=True, extract_tables=False, + extract_charts=False, row_data=document_df.iloc[0], ) assert isinstance(extracted_data, list) assert len(extracted_data) == 2 assert len(extracted_data[0]) == 3 - assert extracted_data[0][0] == "image" + + assert extracted_data[0][0] == "text" assert extracted_data[0][1]["source_metadata"]["source_id"] == "source1" assert isinstance(extracted_data[0][2], str) - assert extracted_data[0][1]["content"][:10] == "iVBORw0KGg" # PNG format header + assert extracted_data[1][0] == "image" + assert extracted_data[1][1]["content"][:10] == "iVBORw0KGg" # PNG format header diff --git a/tests/nv_ingest/modules/sources/test_message_broker_task_source.py b/tests/nv_ingest/modules/sources/test_message_broker_task_source.py index a824f60d..cdb389e2 100644 --- a/tests/nv_ingest/modules/sources/test_message_broker_task_source.py +++ b/tests/nv_ingest/modules/sources/test_message_broker_task_source.py @@ -52,7 +52,7 @@ def job_payload(): "params": {}, }, }, - {"type": "embed", "task_properties": {"text": True, "tables": True}}, + {"type": "embed", "task_properties": {}}, ], } ) diff --git a/tests/nv_ingest/schemas/test_ingest_job_schema.py b/tests/nv_ingest/schemas/test_ingest_job_schema.py index 97045fbb..d0338666 100644 --- a/tests/nv_ingest/schemas/test_ingest_job_schema.py +++ b/tests/nv_ingest/schemas/test_ingest_job_schema.py @@ -37,10 +37,7 @@ def valid_task_properties(task_type): elif task_type == TaskTypeEnum.store: return {"images": True, "structured": True, "method": "minio", "params": {"endpoint": "minio:9000"}} elif task_type == TaskTypeEnum.embed: - return { - "text": True, - "tables": True, - } + return {} elif task_type == TaskTypeEnum.filter: return { "content_type": "image", @@ -179,10 +176,7 @@ def test_multiple_task_types(): }, { "type": "embed", - "task_properties": { - "text": True, - "tables": True, - }, + "task_properties": {}, }, { "type": "filter", diff --git a/tests/nv_ingest/util/converters/multimodal_test_raw_results.json b/tests/nv_ingest/util/converters/multimodal_test_raw_results.json new file mode 100644 index 00000000..ff3b09ac --- /dev/null +++ b/tests/nv_ingest/util/converters/multimodal_test_raw_results.json @@ -0,0 +1 @@ +{"status": "success", "description": "Successfully processed the message.", "data": [{"document_type": "text", "metadata": {"chart_metadata": null, "content": "TestingDocument\r\nA sample document with headings and placeholder text\r\nIntroduction\r\nThis is a placeholder document that can be used for any purpose. It contains some \r\nheadings and some placeholder text to fill the space. The text is not important and contains \r\nno real value, but it is useful for testing. Below, we will have some simple tables and charts \r\nthat we can use to confirm Ingest is working as expected.\r\nTable 1\r\nThis table describes some animals, and some activities they might be doing in specific \r\nlocations.\r\nAnimal Activity Place\r\nGira@e Driving a car At the beach\r\nLion Putting on sunscreen At the park\r\nCat Jumping onto a laptop In a home o@ice\r\nDog Chasing a squirrel In the front yard\r\nChart 1\r\nThis chart shows some gadgets, and some very fictitious costs. Section One\r\nThis is the first section of the document. It has some more placeholder text to show how \r\nthe document looks like. The text is not meant to be meaningful or informative, but rather to \r\ndemonstrate the layout and formatting of the document.\r\n\u2022 This is the first bullet point\r\n\u2022 This is the second bullet point\r\n\u2022 This is the third bullet point\r\nSection Two\r\nThis is the second section of the document. It is more of the same as we\u2019ve seen in the rest \r\nof the document. The content is meaningless, but the intent is to create a very simple \r\nsmoke test to ensure extraction is working as intended. This will be used in CI as time goes \r\non to ensure that changes we make to the library do not negatively impact our accuracy.\r\nTable 2\r\nThis table shows some popular colors that cars might come in.\r\nCar Color1 Color2 Color3\r\nCoupe White Silver Flat Gray\r\nSedan White Metallic Gray Matte Gray\r\nMinivan Gray Beige Black\r\nTruck Dark Gray Titanium Gray Charcoal\r\nConvertible Light Gray Graphite Slate Gray\r\nPicture\r\nBelow, is a high-quality picture of some shapes. Chart 2\r\nThis chart shows some average frequency ranges for speaker drivers.\r\nConclusion\r\nThis is the conclusion of the document. It has some more placeholder text, but the most \r\nimportant thing is that this is the conclusion. As we end this document, we should have \r\nbeen able to extract 2 tables, 2 charts, and some text including 3 bullet points.", "content_metadata": {"description": "Unstructured text from PDF document.", "hierarchy": {"block": -1, "line": -1, "nearby_objects": {"images": {"bbox": [], "content": []}, "structured": {"bbox": [], "content": []}, "text": {"bbox": [], "content": []}}, "page": -1, "page_count": 3, "span": -1}, "page_number": -1, "subtype": "", "type": "text"}, "content_url": "", "debug_metadata": null, "embedding": null, "error_metadata": null, "image_metadata": null, "info_message_metadata": null, "raise_on_failure": false, "source_metadata": {"access_level": 1, "collection_id": "", "date_created": "2025-01-16T21:56:47.531787", "last_modified": "2025-01-16T21:56:47.531632", "partition_id": -1, "source_id": "/home/jeremy/Development/nv-ingest/data/multimodal_test.pdf", "source_location": "", "source_name": "/home/jeremy/Development/nv-ingest/data/multimodal_test.pdf", "source_type": "PDF", "summary": ""}, "table_metadata": null, "text_metadata": {"keywords": "", "language": "en", "summary": "", "text_location": [-1, -1, -1, -1], "text_type": "document"}}}, {"document_type": "structured", "metadata": {"chart_metadata": null, "content": "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du3aNSQkpF+/fgEBARERETExMSKRCI9DmNcJWGElSZIkSdiAY7FYq1atMjY2vnLlSlxcHJ/PFwqFhw4dCg0NPXfu3K5duzp37tyrV6/09HShULhr167u3bsfP35cJBItXrw4NDT00qVLIpEI9hbBwu+7774LCQk5evQo/N29e/czZ86IRCLQjPv37z9u3LiCgoKJEyd26tSpT58+4eHhffr0iY2NFQqFjFl8bW3thx9+2KlTJx8fH3d398DAwJ07dy5btuz06dNOTk6wVANvBLuucrl84cKFnTt3Dg8Pj46ODgoKCgkJ+eWXX/h8fqsLojCP/fXXX7t37x4YGNi3b9+goKCgoKDvvvsOlpCZLcv169f7+/uHhIRER0eHhYX5+vp++eWXsJgE77tr167Q0NDr169//fXXXl5eERERJSUlsA70PL8s5lWBzWbX1tYePnxYJBJFRUUNGDAAIXT48GG4T4jNZtfV1XXt2rVfv34URcXFxbm4uLi6um7cuFEsFjMGPDwe75133nF3d8/MzJTJZL169bK3t584cSIzCQRbhUWLFrm6ukZGRkZGRvbo0ePQoUMgZZCIQCD4/fffg4ODg4KCoqOjAwMDu3fvDpLbjq0Oj8crLi4eNmyYt7d33759AwMDBw0adO/ePSZl0E1Pnz7ds2fPgICAfv36hYSEdOvW7eeff2bkEeJcu3atf//+nTt37tu3b+/evTt37jx+/PiioiIejwcyyGaz1Wr1Z5991qVLlx49ekRFRXl7ew8YMODKlSu6Cj1FUQKBID8/f8iQIV5eXlFRUWFhYf369Xvw4AFTKsx/madehYWl0Dt37hAEMWDAAFhShZ9gwkQQRENDQ9++ffv3709RlFKphJGjurq6rq6OMbKRyWR1dXW//fZbSkqKr6/vyJEjPTw8qqurp0yZkpKSMmbMmPHjx3O53HPnzu3cuXPSpEl//fWXi4uLt7f3ggULbty4ER8f379/fx8fH1NTU39/fyawX79+fn5+sA8iFAq3bt06f/58Nze3TZs22djYZGZmbtq0aezYsceOHRs0aBD4LviHaxSDedHAjr9MJnN3dw8PD4+Jibl7925ERARCqLm5ua6u7ttvv01KSgoLCwsKCrKyskIIgTzCQk7fvn2PHTt29OjRESNGwFYGjM379u1rbm7u1asXE18ulzPbmvX19WVlZX37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iPZDkiTE3q+urlYoFCYmJo8fP7558yZqqtEdmMG0bdu2a9euubm5EDoDtPJGRkaXL18uLi729vZ2cHD4N2OnVCr9/PwQQkePHq2qqhIKhWq1GsJIT58+ffr06aWlpVQRFqyovb292Wz2xYsXi4uLxWIxi8WCkBcnTpwgSbJ///7UmMRv2mqFQmFnZ9erV6/y8vILFy7AdAW+iImJOXXq1LNnzwwMDMzMzAICAmpra6OjozkcDiQxEQgESUlJERER27dvB2Vt07qFwbvGG4uwcFoyMDDYtm1bhw4dLl++7OPjs2nTpitXriQnJ589e3bGjBljxoypqalZtmyZl5dXTU2NlZWVs7NzUVHRzp07a2tr5XL5pUuXvL29U1JS8JwGx4727duDA8T169fv37/f5EMhAwb/bcA99aNHj+Li4n766adt27ZpNJp+/fqx2WwQYXv27KnRaH744YfTp0/fv3//6NGj3t7e//vf/2g3wuAJQeMy7OhDfYh9m/AT0EF+9913s2bNys7OLi8vP3HixNy5czUazcyZM83NzUGko1WhWw6NElgQQkNDSZJs06aNUCh0cHBwcXHp1q1bRETE5cuXaTsK3NcHBQW5uLjs379/xYoVL168qK6uvnnz5sSJE4uKiubOnduhQwe4xKR6gehtGuysbdq04XA4CQkJUVFRycnJr169ksvlY8aM8fDwOH78+IIFCwoKCmpqam7duhUcHJybmxsREUGLdgTlODs7s1is8+fPx8bGIoTKy8u3bt06dOhQuVyuV68G4riLi4tGo4mMjLx69WppaalEImnbtu3Tp09//fVXuVwul8vPnTvn4+OTlpZGHU24H1OpVMHBwVu2bCkqKnrx4sWGDRs2btzI4/HCw8OxlQUss/v374+Li7tz5051dbWpqemCBQtA2jh//nx1dXVpaemuXbsWL14sFAq/+eYbpJWfQO26dOnSr7/++unTp2VlZUeOHJk/fz5JkjNmzBCJRJ/yok11PEKNmOrUhyByOTg4iMXimzdvHj58GHLpHThwICAgQK8KE6NhhoWbCj6fP3/+fITQnDlzTp48WVVVVV5e/r///Q8iEixYsACS3+JWNIZmXC/wYI8ePSCwdHh4eHZ2tkKhKCgoWLhw4bNnz0aNGuXu7k41JIBPOnXqFBISkpOTExIScv/+fVDnA4VeXl4gSEAH6iVA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", "content_metadata": {"description": "Structured table extracted from PDF document.", "hierarchy": {"block": -1, "line": -1, "nearby_objects": {"images": {"bbox": [], "content": []}, "structured": {"bbox": [], "content": []}, "text": {"bbox": [], "content": []}}, "page": 0, "page_count": 3, "span": -1}, "page_number": 0, "subtype": "table", "type": "structured"}, "content_url": "", "debug_metadata": null, "embedding": null, "error_metadata": null, "image_metadata": null, "info_message_metadata": null, "raise_on_failure": false, "source_metadata": {"access_level": 1, "collection_id": "", "date_created": "2025-01-16T21:56:47.531787", "last_modified": "2025-01-16T21:56:47.531632", "partition_id": -1, "source_id": "/home/jeremy/Development/nv-ingest/data/multimodal_test.pdf", "source_location": "", "source_name": "/home/jeremy/Development/nv-ingest/data/multimodal_test.pdf", "source_type": "PDF", "summary": ""}, "table_metadata": {"caption": "", "table_content": "| locations. |\n| Activity | Place |\n| Animal |\n| Giraffe | Driving a car. | At the beach |\n| Lion | Putting on sunscreen | At the park |\n| Cat |\n| Jumping onto a laptop | In a home office |\n| Dog |\n| Chasing a squirrel | In the front yard |\n", "table_content_format": "pseudo_markdown", "table_format": "image", "table_location": [533.2992, 134.96189999999999, 717.7728, 1051.4446], "table_location_max_dimensions": [1536, 1187], "uploaded_image_uri": ""}, "text_metadata": null}}, {"document_type": "structured", "metadata": {"chart_metadata": null, "content": "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", "content_metadata": {"description": "Structured chart extracted from PDF document.", "hierarchy": {"block": -1, "line": -1, "nearby_objects": {"images": {"bbox": [], "content": []}, "structured": {"bbox": [], "content": []}, "text": {"bbox": [], "content": []}}, "page": 0, "page_count": 3, "span": -1}, "page_number": 0, "subtype": "chart", "type": "structured"}, "content_url": "", "debug_metadata": null, "embedding": null, "error_metadata": null, "image_metadata": null, "info_message_metadata": null, "raise_on_failure": false, "source_metadata": {"access_level": 1, "collection_id": "", "date_created": "2025-01-16T21:56:47.531787", "last_modified": "2025-01-16T21:56:47.531632", "partition_id": -1, "source_id": "/home/jeremy/Development/nv-ingest/data/multimodal_test.pdf", "source_location": "", "source_name": "/home/jeremy/Development/nv-ingest/data/multimodal_test.pdf", "source_type": "PDF", "summary": ""}, "table_metadata": {"caption": "", "table_content": "This chart shows some gadgets, and some very fictitious costs. TITLE | Chart 1 \n Cost | Gadgets and their cost \n Hammer | 20 \n Powerdrill | 120 \n Bluetooth speaker | 75 \n Minifridge | 100 \n Premium desk fan | 150 Hammer - Powerdrill - Bluetooth speaker - Minifridge - Premium desk fan Dollars $- - $20.00 - $40.00 - $60.00 - $80.00 - $100.00 - $120.00 - $140.00 - $160.00 Cost Chart 1 - Gadgets and their cost", "table_content_format": "", "table_format": "image", "table_location": [713.1033325195312, 115.5099316984415, 1244.9893798828125, 1077.069027364254], "table_location_max_dimensions": [1536, 1187], "uploaded_image_uri": ""}, "text_metadata": null}}, {"document_type": "structured", "metadata": {"chart_metadata": null, "content": "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", "content_metadata": {"description": "Structured table extracted from PDF document.", "hierarchy": {"block": -1, "line": -1, "nearby_objects": {"images": {"bbox": [], "content": []}, "structured": {"bbox": [], "content": []}, "text": {"bbox": [], "content": []}}, "page": 1, "page_count": 3, "span": -1}, "page_number": 1, "subtype": "table", "type": "structured"}, "content_url": "", "debug_metadata": null, "embedding": null, "error_metadata": null, "image_metadata": null, "info_message_metadata": null, "raise_on_failure": false, "source_metadata": {"access_level": 1, "collection_id": "", "date_created": "2025-01-16T21:56:47.531787", "last_modified": "2025-01-16T21:56:47.531632", "partition_id": -1, "source_id": "/home/jeremy/Development/nv-ingest/data/multimodal_test.pdf", "source_location": "", "source_name": "/home/jeremy/Development/nv-ingest/data/multimodal_test.pdf", "source_type": "PDF", "summary": ""}, "table_metadata": {"caption": "", "table_content": "| This table shows some popular colors that cars might come in. |\n| Car | Color1 | Color2 | Color3 |\n| Coupe | White | Silver | Flat Gray |\n| Sedan | White | Metallic Gray | Matte Gray |\n| Minivan | Gray | Beige | Black |\n| Truck | Dark Gray | Titanium Gray | Charcoal |\n| Convertible | Light Gray | Graphite | Slate Gray |\n", "table_content_format": "pseudo_markdown", "table_format": "image", "table_location": [640.512, 134.96189999999999, 870.6048, 1051.4446], "table_location_max_dimensions": [1536, 1187], "uploaded_image_uri": ""}, "text_metadata": null}}, {"document_type": "structured", "metadata": {"chart_metadata": null, "content": "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k0/CZF988QWl9I477oAappTeeeedhBD4tfK/Ij399NNk+M0lSunq1av9jzFCyF133QXVCHvtL3/5CyHk7bffvvXWW2GC0tJSSunmzZuhb6cUTJ933nlVVVX0TH1VCaEzx2kIrx9++GFCiNlsDg4OfuaZZ3bv3v3pp59CnPT6669TSt1uN6X0xhtvZBgGLl6U0sOHDxNC4uPjv/zyy7q6uqNHj/7qV78ihNx666101KkO/4TL0MyZMz///PNDhw599tlncIV94okn6PCv16xZs2QymVqtzsnJWbt27d69e59//nmFQqFQKCoqKujwbyFc4i+++OKDBw82NTUdPHgQwvG//vWvdPj34Cc/+QnDMMHBwZGRkY899tiWLVsqKio2btx45ZVXEkLuuOOOjRs3fvLJJxBmSbUE0TlE5E888URZWVl1dfVbb72lUChCQ0NbWlpgyg8//BB+EjQazYMPPrhz585t27YtWrSIEAItWBCItLa2JiUlEUIefPDB/Pz8goKCl19+Wa1W8zwPYWVdXZ1Go0lMTHQ4HFAAt9s9ffp0uHRu3LhRqv8XX3yREPKvf/2LUvrqq68SQpYvX37gwIH6+vq9e/cuWLCAEPLWW2/R775e4x+OTJky5Ztvvqmvry8oKLjqqqsIIY899phUpfCbMXny5M8///zo0aNSYPSTn/zE5/PBcioqKlQqldFoVKlUN99887fffrtz586VK1fCwnU63SWXXPLVV1/t2bMHopypU6c6nU6oVYfDAUfgww8/fPTo0bKysrfeekur1ep0OvjlGP2b19HRAT/5Tz/9dGVlZX19/Zo1azQajUKhKCkpgfIoFIqUlBS73U79gralS5eyLJufn08p3bJlC/zurl+/vry8/PDhw3Cg/vznP5d+zF5++WWGYaKiotRq9R133PHee+/19/f39fVBA//jjz9eXl7e0NCwbt26oKAghmEKCgpgZ/X19UH0/9JLL5WUlBw4cODiiy/meR5uAPxj9wULFuzYsaOiomLPnj3nn3++dKxKWw3HVXV19YYNG+DO8K677tqwYcOmTZvgeJ47d65CoTAYDGlpaU8++SScyN3d3WlpaYSQZ599tri4uKSk5MUXX2RZNjo6urm5GaIZn8938cUXE0LOP//8rVu35uXlPffcc0FBQRaLhWEYCPvgjGYYZtmyZVJ5oGw33XQTwzBwKEJJ4ET+2c9+dvDgwYqKii+//BIeW3366afSIbd06VKO47RabVZW1po1a/bu3fvyyy+rVCqO4yBahcq59957CSHTpk1bv379oUOHNmzYAPczf/zjHymlfX190dHRWq22oaGB+sWmEHNAcCMd7fDHnXfeyTBMeHh4cHDwAw888Mknn7jd7qqqKr1er1AoXn311Zqamtra2pdeeolhmNDQ0Pb2dqj5vLw8lmXNZrNKpfrNb36zY8eO3bt3//SnPyWEXHLJJdKhsn//fp7n4+Pjv/jii9LS0o0bNyYmJoaFhdXW1kIZfD4fnFx33HFHQUFBeXn5Rx99FBERwbLs7t27oahHjx6FffT555/X1dUVFRVB7PurX/1q9CFx7Nix9evXn3feeYSQRx99dP369Vu3boXo+bLLLiOEXH311Xv37j169Oinn34KT/CeffZZaWf9+c9/ZhgmIiJCr9ffe++969ats1qt0sJhXY2NjXq93mAwrF27tqampqSkBG69LrroIume/5ZbbmEYJiYmJjo6+q233srPz9+wYQMc/H/605+kHVpYWKhUKs1m8/vvv19WVnbkyJHf//73hJClS5dKd5tdXV0QI65ataqioqK+vv7999/X6XRyuRwaO6hfeA2HMaUU7uj+7//+DybweDzLly8nhNx3332FhYVlZWVr1qwxmUwKhQLuLSmlg4OD8fHxer1erVbPmjXrueeey8vLo34383DFaGhogOD7zjvv3L9///79+++++261Wm0wGPR6PcT3lNJ169bBVUKpVN58881vv/02fHX33XczDAP3BgcPHmRZdsaMGXD9l+4hp02bJpfL4ScMwvSsrKwvv/yyoqLi4MGD11xzDRl+UR6q8YknnmAYJjIyUq/X33PPPR9++KHNZuvo6AgODlapVO+9915NTU1ZWdmjjz5KCFm0aJG0OooQGsdpCK/hFM3JyWlubpbmWrduHcuy11xzDR0+4aERSwqv//3vf7Ms+/TTT0uz2O324OBgnU4HjW3+lzBKKXRsSE5O7unpkWaprq7mOM5oNEJuBEEQoJC33HKL/5UCVv3mm2/CP/v6+lQqlVarhUXB8ouLi+FhsdSCe9111xFCVq5cCQG0BJpzPvroo9F1BYtqb2/XaDQpKSn+X0FA/95778E/165dSwiJjIyEGA4cPXpUJpNlZWV5PB5Y1EMPPUQI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P8/zSqWyvr7+6aef9p9y27ZtXV1dERER0dHRMHibxWKBbjDQW2PatGmTJ0/+9ttvv/32W4VCIZfLYYiTF154YcOGDdLD6+9Rn7BnYde88MILLMuqVCpYxZ49ez7++OPS0lLYxZBl8t133+3t7YWDkOf5DRs2QAIH6TF3SEiITqerqKiAUwOO1TVr1nz00UfM8HjalNIVK1YYjca1a9fW1tYqlUoYOLq/v/+55577/PPPT+QEkf5euXKlWq2Gru1QMJlMVlxcDHe28DAd/PznP+c4DkafXr58eWxsLB01WuEE64WbBAhz4Yqxf/9+eK/X/95mgmNbuvFbv369x+OJiopKSUm5/PLLzzvvPOgQDNVFCIGeXatXr+7p6ZGO87q6uueee27Hjh3SgFmvvfbarl27Rl88/StwvO2Cwlx55ZWU0tdee83pdCoUCo7jeJ4vKSlZv359SEjI8uXLpeNqghgRNr+0tHT16tWbNm3ieV6qVbgySOWBhTz99NPSUSSTyb788sv8/PyEhITp06dTSo1G41VXXTUwMPD666/DKQNPV9atW7dhwwbopA7PQ6T+JDBZZWXlc889N7r/Bsdxdrs9KCjok08+SUpKevDBB9944w3o/w35VV955RWr1SrVc3l5+csvv5ybmyv9Bk18yYK7Bair119/nWEYhUIBZ80jjzxSXV2t0WhO8AjxX6ZGo7n22mvr6+sff/zx1tbW6667TqFQQM/4lJSUxYsXHzt27LPPPoNzB65Ir7766oYNG/y7i4w+2SsrK998883169fDJRq+gjt52E0cx3355ZeLFi168sknR7QcIYROYWI+GCdlgmFl4N4dTksYxGTBggXSZPAOopSY7+DBgyqVSqfTPffcc3v37t29e/eDDz5ICJk8efLQ0NCIHJywTEgRFRER8Y9//GPDhg2rVq2C1B9vv/22NA0M2C4NfACr/uc//0n80p9By5nJZHrttde+/fbbt956KysrKyYmRq/Xx8fHQ/8TSikkRoD00v7bCzf6BoPh97///apVq/yTo0Gx3W43vPZ3ww03bN++ff/+/e+99x6MXwgvqFFKP/74Y0LIHXfcAYuF2fv6+iwWS0hISG9vL5S/r69v1qxZhJBrrrlm/fr1mzZtgiywQUFB0Ooj5ZmSMlT456ytrq6GLrypqakwvAu8gQqD0aSnp69Zs2b//v3bt2+HjR2RN1fa5M2bN7MsGxYW9u9//zs3N/fbb7+FvNRLliyBfBd0eCgWi8Xy/PPPb9q06c0338zOzibD2VhhGng/derUqaJf0lyXy5WamqpQKBobG6lfcmJI0bVz5046nAUcRqaYM2fOunXrtm/f/vLLL0MIBW/W+2dwgyXccssthJAVK1Zs2bJl69atDzzwgDSmIGTakhrtqqqq4GExy7J79uyRvhJFsby8PCwsjBDyhz/8YevWrZs3b4Zi5OTkDA4OwuyQmA/S90rLhMwGixYt+uqrr7Zt2/bwww/DoGuEkLvuugsK2draOnv27BGncHh4+E033STl6922bRuEdKtWrdq2bdvGjRuhW8U111wjDhuxv+6//34yPFAF7HFKKZy/kPBbyvDt8XhgaRdeeOGGDRu2bdv297//HTqNQEof2BzYF1OnTn3jjTc++eST66+/XqlUJiYmku+OFwOtwlOnTv3b3/725JNPLly4UKlUwuN7SGAHhyVk8o6KinrllVe2b9/+wQcfwEuHkEYdag9iemhJlU5k6G774IMPSov6+9//TggJDQ39+9//vmHDhqeffhreEXzttdf8N9Pr9Ur1DCk1/Y8W/6r77W9/Swj58MMPpY0SBAFeub722mu3b9++ZcuWO+64w2g0Qvvuv//9b5gdkjZApOW/U2C98B6kKIq7du2ShkWUJCcnP/HEEw6HA7YdrqiTJk165513tm/f/uabb0J6OLjGUkqhG5harX7mmWf27t27Z8+eRx55BM7o/v7+MS+ekBxGunpDzUC27JkzZ7777rubNm16+umnoW83HDnwWA+uupAMcXSlwcLr6upCQ0MZhnn88cd37969d+9eeG8VMnVCYeBkNJlMERERTz/99Gefffbwww9DVAcZP6G2YQxaQsitt94Kpy1c8RITE1tbWwW/ARCWL1/+9ddfb9u27aGHHpLO6/vvv58OJ+ZLTExUqVStra1QgKNHj8KxIeVUvfvuuwkh06dP/+CDD7Zv3/7aa69BAg0pbejQ0FB0dLTBYBgv3SFUY3t7O+Th/slPfvLJJ5+88847Cxcu1Gg0wcHBarVaSswH13xIBej/gwLFgJVKV4/Dhw9DmK5UKqVhiWD6w4cPw1X9T3/609atW7/++mtIf7R48WJ4BkuHE/P985//pH4/ys3NzTAOwCOPPLJr1659+/Y9++yz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", "content_metadata": {"description": "Structured chart extracted from PDF document.", "hierarchy": {"block": -1, "line": -1, "nearby_objects": {"images": {"bbox": [], "content": []}, "structured": {"bbox": [], "content": []}, "text": {"bbox": [], "content": []}}, "page": 2, "page_count": 3, "span": -1}, "page_number": 2, "subtype": "chart", "type": "structured"}, "content_url": "", "debug_metadata": null, "embedding": null, "error_metadata": null, "image_metadata": null, "info_message_metadata": null, "raise_on_failure": false, "source_metadata": {"access_level": 1, "collection_id": "", "date_created": "2025-01-16T21:56:47.531787", "last_modified": "2025-01-16T21:56:47.531632", "partition_id": -1, "source_id": "/home/jeremy/Development/nv-ingest/data/multimodal_test.pdf", "source_location": "", "source_name": "/home/jeremy/Development/nv-ingest/data/multimodal_test.pdf", "source_type": "PDF", "summary": ""}, "table_metadata": {"caption": "", "table_content": "This chart shows some average frequency ranges for speaker drivers TITLE | Chart 2 \n Frequency Range Start (Hz) | Frequency Range Start (Hz) | Frequency Range End (Hz) \n Tweeter | 13110 | 13110 \n Midrange | 1375 | 13110 \n Midwoofer | 1710 | 13110 \n Subwoofer | 9 | 130 Tweeter - Midrange - Midwoofer - Subwoofer Hertz (log scale) 10 - 100 - 1000 - 10000 - 100000 Frequency Range Start (Hz) - Frequency Range End (Hz) This chart shows some average frequency ranges for speaker drivers - Frequency Ranges of Speaker Drivers", "table_content_format": "", "table_format": "image", "table_location": [119.01695251464844, 114.26061560213566, 693.9878540039062, 1083.422370672226], "table_location_max_dimensions": [1536, 1187], "uploaded_image_uri": ""}, "text_metadata": null}}], "fragment": 0, "fragment_count": 1, "trace": {"trace::entry::broker_source_network_in": 1.737064606939735e+18, "trace::entry::caption_ext": 1.737064609844455e+18, "trace::entry::caption_ext_channel_in": 1.7370646098369871e+18, "trace::entry::chart_data_extraction": 1.737064608686222e+18, "trace::entry::chart_data_extraction::cached_0": 1.737064609051628e+18, "trace::entry::chart_data_extraction::cached_1": 1.7370646095401439e+18, "trace::entry::chart_data_extraction::deplot_0": 1.737064608866291e+18, "trace::entry::chart_data_extraction::deplot_1": 1.73706460928608e+18, "trace::entry::chart_data_extraction_channel_in": 1.737064608680754e+18, "trace::entry::dedup_images": 1.7370646083958408e+18, "trace::entry::dedup_images_channel_in": 1.737064608394093e+18, "trace::entry::docx_content_extractor": 1.737064608391644e+18, "trace::entry::docx_content_extractor_channel_in": 1.737064608389036e+18, "trace::entry::filter_images": 1.7370646083990769e+18, "trace::entry::filter_images_channel_in": 1.737064608397959e+18, "trace::entry::job_counter": 1.73706460735933e+18, "trace::entry::job_counter_channel_in": 1.737064607357017e+18, "trace::entry::message_broker_task_sink": 1.737064609848043e+18, "trace::entry::message_broker_task_sink_channel_in": 1.737064609846622e+18, "trace::entry::message_broker_task_source": 1.737064607345441e+18, "trace::entry::metadata_injection": 1.7370646073686262e+18, "trace::entry::metadata_injection_channel_in": 1.737064607368335e+18, "trace::entry::pdf_content_extractor": 1.737064607398e+18, "trace::entry::pdf_content_extractor::pdfium_pages_to_numpy_0": 1.7370646075532639e+18, "trace::entry::pdf_content_extractor::pdfium_pages_to_numpy_1": 1.7370646080538499e+18, "trace::entry::pdf_content_extractor::yolox_0": 1.7370646078989688e+18, "trace::entry::pdf_content_extractor::yolox_1": 1.737064608250277e+18, "trace::entry::pdf_content_extractor_channel_in": 1.7370646073974162e+18, "trace::entry::pptx_content_extractor": 1.737064608393168e+18, "trace::entry::pptx_content_extractor_channel_in": 1.737064608392411e+18, "trace::entry::store_embedding_minio": 1.737064609846136e+18, "trace::entry::store_embedding_minio_channel_in": 1.737064609845022e+18, "trace::entry::table_data_extraction": 1.737064608401692e+18, "trace::entry::table_data_extraction::paddle_0": 1.737064608469864e+18, "trace::entry::table_data_extraction::paddle_1": 1.737064608544877e+18, "trace::entry::table_data_extraction_channel_in": 1.737064608399796e+18, "trace::exit::broker_source_network_in": 1.737064607344929e+18, "trace::exit::caption_ext": 1.737064609845022e+18, "trace::exit::caption_ext_channel_in": 1.737064609844396e+18, "trace::exit::chart_data_extraction": 1.7370646098369871e+18, "trace::exit::chart_data_extraction::cached_0": 1.737064609285778e+18, "trace::exit::chart_data_extraction::cached_1": 1.737064609770478e+18, "trace::exit::chart_data_extraction::deplot_0": 1.737064609051506e+18, "trace::exit::chart_data_extraction::deplot_1": 1.73706460954002e+18, "trace::exit::chart_data_extraction_channel_in": 1.737064608686173e+18, "trace::exit::dedup_images": 1.737064608397959e+18, "trace::exit::dedup_images_channel_in": 1.737064608395814e+18, "trace::exit::docx_content_extractor": 1.737064608392411e+18, "trace::exit::docx_content_extractor_channel_in": 1.737064608391604e+18, "trace::exit::filter_images": 1.737064608399796e+18, "trace::exit::filter_images_channel_in": 1.737064608399043e+18, "trace::exit::job_counter": 1.737064607368335e+18, "trace::exit::job_counter_channel_in": 1.737064607359222e+18, "trace::exit::message_broker_task_sink_channel_in": 1.73706460984782e+18, "trace::exit::message_broker_task_source": 1.737064607357001e+18, "trace::exit::metadata_injection": 1.7370646073974162e+18, "trace::exit::metadata_injection_channel_in": 1.737064607368457e+18, "trace::exit::pdf_content_extractor": 1.7370646083872138e+18, "trace::exit::pdf_content_extractor::pdfium_pages_to_numpy_0": 1.737064607898837e+18, "trace::exit::pdf_content_extractor::pdfium_pages_to_numpy_1": 1.737064608250124e+18, "trace::exit::pdf_content_extractor::yolox_0": 1.737064607991429e+18, "trace::exit::pdf_content_extractor::yolox_1": 1.7370646082900168e+18, "trace::exit::pdf_content_extractor_channel_in": 1.7370646073979581e+18, "trace::exit::pptx_content_extractor": 1.737064608394093e+18, "trace::exit::pptx_content_extractor_channel_in": 1.737064608393143e+18, "trace::exit::store_embedding_minio": 1.737064609846622e+18, "trace::exit::store_embedding_minio_channel_in": 1.737064609846076e+18, "trace::exit::table_data_extraction": 1.737064608680754e+18, "trace::exit::table_data_extraction::paddle_0": 1.737064608540777e+18, "trace::exit::table_data_extraction::paddle_1": 1.737064608619194e+18, "trace::exit::table_data_extraction_channel_in": 1.737064608401629e+18}, "annotations": {"annotation::0850e5fb-3149-43f9-bdf3-3a8d80f912ae": {"source_id": "nv_ingest.util.exception_handlers.decorators", "task_id": "metadata_injection", "task_result": "SUCCESS"}, "annotation::20cbf1f6-9b0c-4ec8-a148-8789cce2bf9b": {"message": "Created", "source_id": "nv_ingest.modules.sources.message_broker_task_source"}, "annotation::28011457-406e-4750-b1fb-c91acc9c9ad5": {"source_id": "nv_ingest.util.exception_handlers.decorators", "task_id": "docx_content_extractor", "task_result": "SUCCESS"}, "annotation::35daf894-ee54-4180-bde5-1944ae215a7f": {"source_id": "nv_ingest.util.exception_handlers.decorators", "task_id": "table_data_extraction", "task_result": "SUCCESS"}, "annotation::3e6eafec-db0a-4c8c-9b2f-2bfc603d6b49": {"source_id": "nv_ingest.util.exception_handlers.decorators", "task_id": "chart_data_extraction", "task_result": "SUCCESS"}, "annotation::4bebcba2-54f2-4c4f-8e09-2ac1c2ab60a2": {"source_id": "nv_ingest.util.exception_handlers.decorators", "task_id": "caption_ext", "task_result": "SUCCESS"}, "annotation::50228775-183c-4faf-9e34-478adf1a44b6": {"source_id": "nv_ingest.util.exception_handlers.decorators", "task_id": "docx_content_extractor", "task_result": "SUCCESS"}, "annotation::55d2a8df-82dc-4904-a44e-f8cb07e95bf8": {"source_id": "nv_ingest.util.exception_handlers.decorators", "task_id": "pptx_content_extractor", "task_result": "SUCCESS"}, "annotation::5a1be681-0e21-4595-b948-a2a928fe2bef": {"source_id": "nv_ingest.util.exception_handlers.decorators", "task_id": "chart_data_extraction", "task_result": "SUCCESS"}, "annotation::5cd0c948-2382-458a-bbca-dfaec0e2837c": {"source_id": "nv_ingest.util.exception_handlers.decorators", "task_id": "dedup_images", "task_result": "SUCCESS"}, "annotation::6ec6ac21-c35c-4a02-ac70-72a1fb7d6659": {"source_id": "nv_ingest.util.exception_handlers.decorators", "task_id": "filter_images", "task_result": "SUCCESS"}, "annotation::7b253246-c4c1-47bf-867e-90164e715ca4": {"source_id": "nv_ingest.util.exception_handlers.decorators", "task_id": "caption_ext", "task_result": "SUCCESS"}, "annotation::7da4625f-a90b-427d-9243-b762050a0cba": {"source_id": "nv_ingest.util.exception_handlers.decorators", "task_id": "table_data_extraction", "task_result": "SUCCESS"}, "annotation::7e004440-31ce-4d13-8c38-331d6e1acaab": {"source_id": "nv_ingest.util.exception_handlers.decorators", "task_id": "docx_content_extractor", "task_result": "SUCCESS"}, "annotation::96866059-2f15-4d4e-abfc-0443f3349aa9": {"source_id": "nv_ingest.util.exception_handlers.decorators", "task_id": "store_embedding_minio", "task_result": "SUCCESS"}, "annotation::9ff9511a-2dc0-4854-9c17-436338708643": {"source_id": "nv_ingest.util.exception_handlers.decorators", "task_id": "pptx_content_extractor", "task_result": "SUCCESS"}, "annotation::aa859318-9372-4366-bf7d-2577036ca959": {"source_id": "nv_ingest.util.exception_handlers.decorators", "task_id": "filter_images", "task_result": "SUCCESS"}, "annotation::c0218a87-de79-4678-a03f-9c6e50ca2735": {"source_id": "nv_ingest.util.exception_handlers.decorators", "task_id": "docx_content_extractor", "task_result": "SUCCESS"}, "annotation::c2920300-2256-416b-9233-bf699fa2547e": {"source_id": "nv_ingest.util.exception_handlers.decorators", "task_id": "pdf_content_extractor", "task_result": "SUCCESS"}, "annotation::c703abcc-a807-4437-8092-1de9b5667225": {"source_id": "nv_ingest.util.exception_handlers.decorators", "task_id": "table_data_extraction", "task_result": "SUCCESS"}, "annotation::c7a88380-bb36-4ad7-8859-9ef995892393": {"source_id": "nv_ingest.util.exception_handlers.decorators", "task_id": "job_counter", "task_result": "SUCCESS"}, "annotation::d57ae40c-75bb-408f-b2af-ad750eb84387": {"source_id": "nv_ingest.util.exception_handlers.decorators", "task_id": "chart_data_extraction", "task_result": "SUCCESS"}, "annotation::d79b48dd-13fa-4a83-bad3-87e70d755d86": {"source_id": "nv_ingest.util.exception_handlers.decorators", "task_id": "store_embedding_minio", "task_result": "SUCCESS"}, "annotation::f7963fc9-7366-4d3c-846c-6621374b7390": {"source_id": "nv_ingest.util.exception_handlers.decorators", "task_id": "dedup_images", "task_result": "SUCCESS"}, "annotation::f801768a-b0b5-4cd7-96b1-ff2b6429177b": {"source_id": "nv_ingest.util.exception_handlers.decorators", "task_id": "pdf_content_extractor", "task_result": "SUCCESS"}, "annotation::fdd56e54-9306-43ee-982f-03a357f5684f": {"source_id": "nv_ingest.util.exception_handlers.decorators", "task_id": "pdf_content_extractor", "task_result": "SUCCESS"}}} diff --git a/tests/nv_ingest/util/converters/test_formats.py b/tests/nv_ingest/util/converters/test_formats.py new file mode 100644 index 00000000..17c2a0d6 --- /dev/null +++ b/tests/nv_ingest/util/converters/test_formats.py @@ -0,0 +1,101 @@ +# SPDX-FileCopyrightText: Copyright (c) 2024, NVIDIA CORPORATION & AFFILIATES. +# All rights reserved. +# SPDX-License-Identifier: Apache-2.0 + +import json +import os + +from nv_ingest.util.converters.formats import ingest_json_results_to_blob + + +sample_result_text_json = """ +[ + { + "document_type": "text", + "metadata": { + "chart_metadata": null, + "content": "TestingDocument\r\nA sample document with headings and placeholder text\r\nIntroduction\r\nThis is a placeholder document that can be used for any purpose. It contains some \r\nheadings and some placeholder text to fill the space. The text is not important and contains \r\nno real value, but it is useful for testing. Below, we will have some simple tables and charts \r\nthat we can use to confirm Ingest is working as expected.\r\nTable 1\r\nThis table describes some animals, and some activities they might be doing in specific \r\nlocations.\r\nAnimal Activity Place\r\nGira@e Driving a car At the beach\r\nLion Putting on sunscreen At the park\r\nCat Jumping onto a laptop In a home o@ice\r\nDog Chasing a squirrel In the front yard\r\nChart 1\r\nThis chart shows some gadgets, and some very fictitious costs. Section One\r\nThis is the first section of the document. It has some more placeholder text to show how \r\nthe document looks like. The text is not meant to be meaningful or informative, but rather to \r\ndemonstrate the layout and formatting of the document.\r\n\u2022 This is the first bullet point\r\n\u2022 This is the second bullet point\r\n\u2022 This is the third bullet point\r\nSection Two\r\nThis is the second section of the document. It is more of the same as we\u2019ve seen in the rest \r\nof the document. The content is meaningless, but the intent is to create a very simple \r\nsmoke test to ensure extraction is working as intended. This will be used in CI as time goes \r\non to ensure that changes we make to the library do not negatively impact our accuracy.\r\nTable 2\r\nThis table shows some popular colors that cars might come in.\r\nCar Color1 Color2 Color3\r\nCoupe White Silver Flat Gray\r\nSedan White Metallic Gray Matte Gray\r\nMinivan Gray Beige Black\r\nTruck Dark Gray Titanium Gray Charcoal\r\nConvertible Light Gray Graphite Slate Gray\r\nPicture\r\nBelow, is a high-quality picture of some shapes. Chart 2\r\nThis chart shows some average frequency ranges for speaker drivers.\r\nConclusion\r\nThis is the conclusion of the document. It has some more placeholder text, but the most \r\nimportant thing is that this is the conclusion. As we end this document, we should have \r\nbeen able to extract 2 tables, 2 charts, and some text including 3 bullet points.", + "content_metadata": { + "description": "Unstructured text from PDF document.", + "hierarchy": { + "block": -1, + "line": -1, + "nearby_objects": { + "images": { + "bbox": [], + "content": [] + }, + "structured": { + "bbox": [], + "content": [] + }, + "text": { + "bbox": [], + "content": [] + } + }, + "page": -1, + "page_count": 3, + "span": -1 + }, + "page_number": -1, + "subtype": "", + "type": "text" + }, + "content_url": "", + "debug_metadata": null, + "embedding": null, + "error_metadata": null, + "image_metadata": null, + "info_message_metadata": null, + "raise_on_failure": false, + "source_metadata": { + "access_level": 1, + "collection_id": "", + "date_created": "2025-01-16T21:31:28.929797", + "last_modified": "2025-01-16T21:31:28.929648", + "partition_id": -1, + "source_id": "/home/jeremy/Development/nv-ingest/data/multimodal_test.pdf", + "source_location": "", + "source_name": "/home/jeremy/Development/nv-ingest/data/multimodal_test.pdf", + "source_type": "PDF", + "summary": "" + }, + "table_metadata": null, + "text_metadata": { + "keywords": "", + "language": "en", + "summary": "", + "text_location": [ + -1, + -1, + -1, + -1 + ], + "text_type": "document" + } + } + } +] +""" # noqa: E501 + + +def test_json_results_to_blob_text_failure(): + # there must be a "data" element in the json otherwise empty is returned + blob_response = ingest_json_results_to_blob(sample_result_text_json) + assert blob_response == "" + + +def test_json_results_to_blob(): + current_directory = os.path.dirname(__file__) + + # Construct the full path to the target file + file_name = "multimodal_test_raw_results.json" + file_path = os.path.join(current_directory, file_name) + + with open(file_path, "r") as file: + json_result_raw_data = json.load(file) + blob_response = ingest_json_results_to_blob(json.dumps(json_result_raw_data)) + + # The actual output is quite large. So we just check for key pieces being present + assert "Tweeter - Midrange - Midwoofer - Subwoofer Hertz" in blob_response diff --git a/tests/nv_ingest/util/nim/test_cached.py b/tests/nv_ingest/util/nim/test_cached.py index 8463f25d..c3871926 100644 --- a/tests/nv_ingest/util/nim/test_cached.py +++ b/tests/nv_ingest/util/nim/test_cached.py @@ -216,7 +216,7 @@ def test_process_inference_results(model_interface): """ output = "Processed Output" - result = model_interface.process_inference_results(output) + result = model_interface.process_inference_results(output, "http") assert result == output diff --git a/tests/nv_ingest/util/nim/test_yolox.py b/tests/nv_ingest/util/nim/test_yolox.py index f42bf778..b3a84fd3 100644 --- a/tests/nv_ingest/util/nim/test_yolox.py +++ b/tests/nv_ingest/util/nim/test_yolox.py @@ -1,25 +1,17 @@ -import pytest -import numpy as np -from io import BytesIO import base64 +import random +from io import BytesIO + +import numpy as np +import pytest from PIL import Image from nv_ingest.util.nim.yolox import YoloxPageElementsModelInterface -@pytest.fixture(params=["0.2.0", "1.0.0"]) -def model_interface(request): - return YoloxPageElementsModelInterface(yolox_version=request.param) - - -@pytest.fixture -def legacy_model_interface(): - return YoloxPageElementsModelInterface(yolox_version="0.2.0") - - @pytest.fixture -def ga_model_interface(): - return YoloxPageElementsModelInterface(yolox_version="1.0.0") +def model_interface(): + return YoloxPageElementsModelInterface() def create_test_image(width=800, height=600, color=(255, 0, 0)): @@ -68,25 +60,18 @@ def create_base64_image(width=1024, height=1024, color=(255, 0, 0)): return base64.b64encode(buffer.getvalue()).decode("utf-8") -def test_name_returns_yolox_legacy(legacy_model_interface): - assert legacy_model_interface.name() == "yolox-page-elements (version 0.2.0)" - - -def test_name_returns_yolox(ga_model_interface): - ga_model_interface = YoloxPageElementsModelInterface(yolox_version="1.0.0") - assert ga_model_interface.name() == "yolox-page-elements (version 1.0.0)" +def test_name_returns_yolox(model_interface): + model_interface = YoloxPageElementsModelInterface() + assert model_interface.name() == "yolox-page-elements" def test_prepare_data_for_inference_valid(model_interface): images = [create_test_image(), create_test_image(width=640, height=480)] input_data = {"images": images} result = model_interface.prepare_data_for_inference(input_data) - assert "resized_images" in result assert "original_image_shapes" in result - assert len(result["resized_images"]) == len(images) assert len(result["original_image_shapes"]) == len(images) - for original_shape, resized_image, image in zip(result["original_image_shapes"], result["resized_images"], images): - assert resized_image.shape == (1024, 1024, 3) + for original_shape, image in zip(result["original_image_shapes"], images): assert original_shape[:2] == image.shape[:2] @@ -118,28 +103,11 @@ def test_format_input_grpc(model_interface): assert formatted_input.shape[1:] == (3, 1024, 1024) -def test_format_input_legacy(legacy_model_interface): - images = [create_test_image(), create_test_image()] - input_data = {"images": images} - prepared_data = legacy_model_interface.prepare_data_for_inference(input_data) - formatted_input = legacy_model_interface.format_input(prepared_data, "http") - assert "messages" in formatted_input - assert isinstance(formatted_input["messages"], list) - for message in formatted_input["messages"]: - assert "content" in message - for content in message["content"]: - assert "type" in content - assert content["type"] == "image_url" - assert "image_url" in content - assert "url" in content["image_url"] - assert content["image_url"]["url"].startswith("data:image/png;base64,") - - -def test_format_input(ga_model_interface): +def test_format_input_http(model_interface): images = [create_test_image(), create_test_image()] input_data = {"images": images} - prepared_data = ga_model_interface.prepare_data_for_inference(input_data) - formatted_input = ga_model_interface.format_input(prepared_data, "http") + prepared_data = model_interface.prepare_data_for_inference(input_data) + formatted_input = model_interface.format_input(prepared_data, "http") assert "input" in formatted_input assert isinstance(formatted_input["input"], list) for content in formatted_input["input"]: @@ -165,45 +133,7 @@ def test_parse_output_grpc(model_interface): assert parsed_output.dtype == np.float32 -def test_parse_output_http_valid_legacy(legacy_model_interface): - response = { - "data": [ - [ - { - "type": "table", - "bboxes": [{"xmin": 0.1, "ymin": 0.1, "xmax": 0.2, "ymax": 0.2, "confidence": 0.9}], - }, - { - "type": "chart", - "bboxes": [{"xmin": 0.3, "ymin": 0.3, "xmax": 0.4, "ymax": 0.4, "confidence": 0.8}], - }, - {"type": "title", "bboxes": [{"xmin": 0.5, "ymin": 0.5, "xmax": 0.6, "ymax": 0.6, "confidence": 0.95}]}, - ], - [ - { - "type": "table", - "bboxes": [{"xmin": 0.15, "ymin": 0.15, "xmax": 0.25, "ymax": 0.25, "confidence": 0.85}], - }, - { - "type": "chart", - "bboxes": [{"xmin": 0.35, "ymin": 0.35, "xmax": 0.45, "ymax": 0.45, "confidence": 0.75}], - }, - { - "type": "title", - "bboxes": [{"xmin": 0.55, "ymin": 0.55, "xmax": 0.65, "ymax": 0.65, "confidence": 0.92}], - }, - ], - ] - } - scaling_factors = [(1.0, 1.0), (1.0, 1.0)] - data = {"scaling_factors": scaling_factors} - parsed_output = legacy_model_interface.parse_output(response, "http", data) - assert isinstance(parsed_output, np.ndarray) - assert parsed_output.shape == (2, 3, 85) - assert parsed_output.dtype == np.float32 - - -def test_parse_output_http_valid(ga_model_interface): +def test_parse_output_http_valid(model_interface): response = { "data": [ { @@ -224,12 +154,19 @@ def test_parse_output_http_valid(ga_model_interface): }, ] } - scaling_factors = [(1.0, 1.0), (1.0, 1.0)] - data = {"scaling_factors": scaling_factors} - parsed_output = ga_model_interface.parse_output(response, "http", data) - assert isinstance(parsed_output, np.ndarray) - assert parsed_output.shape == (2, 3, 85) - assert parsed_output.dtype == np.float32 + parsed_output = model_interface.parse_output(response, "http") + assert parsed_output == [ + { + "table": [[0.1, 0.1, 0.2, 0.2, 0.9]], + "chart": [[0.3, 0.3, 0.4, 0.4, 0.8]], + "title": [[0.5, 0.5, 0.6, 0.6, 0.95]], + }, + { + "table": [[0.15, 0.15, 0.25, 0.25, 0.85]], + "chart": [[0.35, 0.35, 0.45, 0.45, 0.75]], + "title": [[0.55, 0.55, 0.65, 0.65, 0.92]], + }, + ] def test_parse_output_invalid_protocol(model_interface): @@ -238,11 +175,12 @@ def test_parse_output_invalid_protocol(model_interface): model_interface.parse_output(response, "invalid_protocol") -def test_process_inference_results(model_interface): +def test_process_inference_results_grpc(model_interface): output_array = np.random.rand(2, 100, 85).astype(np.float32) original_image_shapes = [(800, 600, 3), (640, 480, 3)] inference_results = model_interface.process_inference_results( output_array, + "grpc", original_image_shapes=original_image_shapes, num_classes=3, conf_thresh=0.5, @@ -262,3 +200,35 @@ def test_process_inference_results(model_interface): assert bbox[4] >= 0.6 if "title" in result: assert isinstance(result["title"], list) + + +def test_process_inference_results_http(model_interface): + output = [ + { + "table": [[random.random() for _ in range(5)] for _ in range(10)], + "chart": [[random.random() for _ in range(5)] for _ in range(10)], + "title": [[random.random() for _ in range(5)] for _ in range(10)], + } + for _ in range(10) + ] + inference_results = model_interface.process_inference_results( + output, + "http", + num_classes=3, + conf_thresh=0.5, + iou_thresh=0.4, + min_score=0.3, + final_thresh=0.6, + ) + assert isinstance(inference_results, list) + assert len(inference_results) == 10 + for result in inference_results: + assert isinstance(result, dict) + if "table" in result: + for bbox in result["table"]: + assert bbox[4] >= 0.6 + if "chart" in result: + for bbox in result["chart"]: + assert bbox[4] >= 0.6 + if "title" in result: + assert isinstance(result["title"], list) diff --git a/tests/nv_ingest_client/client/test_interface.py b/tests/nv_ingest_client/client/test_interface.py index 41c72bc2..83a556b8 100644 --- a/tests/nv_ingest_client/client/test_interface.py +++ b/tests/nv_ingest_client/client/test_interface.py @@ -4,6 +4,7 @@ # SPDX-FileCopyrightText: Copyright (c) 2024 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +import logging import tempfile from concurrent.futures import Future from unittest.mock import MagicMock @@ -24,7 +25,7 @@ from nv_ingest_client.primitives.tasks import StoreEmbedTask from nv_ingest_client.primitives.tasks import StoreTask from nv_ingest_client.primitives.tasks import TableExtractionTask -from nv_ingest_client.primitives.tasks import VdbUploadTask +from nv_ingest_client.util.milvus import MilvusOperator MODULE_UNDER_TEST = "nv_ingest_client.client.interface" @@ -72,13 +73,13 @@ def test_embed_task_no_args(ingestor): assert isinstance(ingestor._job_specs.job_specs["pdf"][0]._tasks[0], EmbedTask) -def test_embed_task_some_args(ingestor): - ingestor.embed(text=False, tables=False) +def test_embed_task_some_args(ingestor, caplog): + # `text` and `table` arguments were deprecated before GA. + with caplog.at_level(logging.WARNING): + ingestor.embed(text=False, tables=False) - task = ingestor._job_specs.job_specs["pdf"][0]._tasks[0] - assert isinstance(task, EmbedTask) - assert task._text is False - assert task._tables is False + assert "'text' parameter is deprecated" in caplog.records[0].message + assert "'tables' parameter is deprecated" in caplog.records[1].message def test_extract_task_no_args(ingestor): @@ -193,15 +194,13 @@ def test_store_task_some_args_extra_param(ingestor): def test_vdb_upload_task_no_args(ingestor): ingestor.vdb_upload() - assert isinstance(ingestor._job_specs.job_specs["pdf"][0]._tasks[0], VdbUploadTask) + assert isinstance(ingestor._vdb_bulk_upload, MilvusOperator) def test_vdb_upload_task_some_args(ingestor): ingestor.vdb_upload(filter_errors=True) - task = ingestor._job_specs.job_specs["pdf"][0]._tasks[0] - assert isinstance(task, VdbUploadTask) - assert task._filter_errors is True + assert isinstance(ingestor._vdb_bulk_upload, MilvusOperator) def test_caption_task_no_args(ingestor): @@ -228,8 +227,8 @@ def test_chain(ingestor): assert isinstance(ingestor._job_specs.job_specs["pdf"][0]._tasks[5], FilterTask) assert isinstance(ingestor._job_specs.job_specs["pdf"][0]._tasks[6], SplitTask) assert isinstance(ingestor._job_specs.job_specs["pdf"][0]._tasks[7], StoreTask) - assert isinstance(ingestor._job_specs.job_specs["pdf"][0]._tasks[8], VdbUploadTask) - assert len(ingestor._job_specs.job_specs["pdf"][0]._tasks) == 9 + assert isinstance(ingestor._vdb_bulk_upload, MilvusOperator) + assert len(ingestor._job_specs.job_specs["pdf"][0]._tasks) == 8 def test_ingest(ingestor, mock_client): diff --git a/tests/nv_ingest_client/util/test_milvus_util.py b/tests/nv_ingest_client/util/test_milvus_util.py new file mode 100644 index 00000000..525ca288 --- /dev/null +++ b/tests/nv_ingest_client/util/test_milvus_util.py @@ -0,0 +1,67 @@ +import pytest +from nv_ingest_client.util.milvus import MilvusOperator, _dict_to_params + + +@pytest.fixture +def milvus_test_dict(): + mil_op = MilvusOperator() + kwargs = mil_op.milvus_kwargs + kwargs["collection_name"] = mil_op.collection_name + return kwargs + + +def test_extra_kwargs(milvus_test_dict): + mil_op = MilvusOperator(filter_errors=True) + milvus_test_dict.pop("collection_name") + assert mil_op.milvus_kwargs == milvus_test_dict + + +@pytest.mark.parametrize("collection_name", [None, "name"]) +def test_op_collection_name(collection_name): + if collection_name: + mo = MilvusOperator(collection_name=collection_name) + else: + # default + collection_name = "nv_ingest_collection" + mo = MilvusOperator() + cr_collection_name, conn_params = mo.get_connection_params() + wr_collection_name, write_params = mo.get_write_params() + assert cr_collection_name == wr_collection_name == collection_name + + +def test_op_connection_params(milvus_test_dict): + mo = MilvusOperator() + cr_collection_name, conn_params = mo.get_connection_params() + assert cr_collection_name == milvus_test_dict["collection_name"] + for k, v in conn_params.items(): + assert milvus_test_dict[k] == v + + +def test_op_write_params(milvus_test_dict): + mo = MilvusOperator() + collection_name, wr_params = mo.get_write_params() + assert collection_name == milvus_test_dict["collection_name"] + for k, v in wr_params.items(): + assert milvus_test_dict[k] == v + + +@pytest.mark.parametrize( + "collection_name, expected_results", + [ + ({"text": ["text", "charts", "tables"]}, {"enable_text": True, "enable_charts": True, "enable_tables": True}), + ({"text": ["text", "tables"]}, {"enable_text": True, "enable_charts": False, "enable_tables": True}), + ({"text": ["text", "charts"]}, {"enable_text": True, "enable_charts": True, "enable_tables": False}), + ({"text": ["text"]}, {"enable_text": True, "enable_charts": False, "enable_tables": False}), + ], +) +def test_op_dict_to_params(collection_name, expected_results): + mo = MilvusOperator() + _, wr_params = mo.get_write_params() + response = _dict_to_params(collection_name, wr_params) + if isinstance(collection_name, str): + collection_name = {collection_name: None} + for res in response: + coll_name, write_params = res + for k, v in expected_results.items(): + assert write_params[k] == v + coll_name in collection_name.keys() From a60ffdf94f86f79309dafaf3b079c91c4554fcaf Mon Sep 17 00:00:00 2001 From: Devin Robison Date: Fri, 17 Jan 2025 13:24:34 -0700 Subject: [PATCH 4/7] Workflow is functioning but failing on transcription call --- .../modules/injectors/metadata_injector.py | 11 +- .../schemas/audio_extractor_schema.py | 6 +- src/nv_ingest/schemas/metadata_schema.py | 12 +- src/nv_ingest/stages/nim/audio_extraction.py | 109 +++++++----------- .../util/converters/type_mappings.py | 2 + 5 files changed, 67 insertions(+), 73 deletions(-) diff --git a/src/nv_ingest/modules/injectors/metadata_injector.py b/src/nv_ingest/modules/injectors/metadata_injector.py index 41ffadc6..72133b0a 100644 --- a/src/nv_ingest/modules/injectors/metadata_injector.py +++ b/src/nv_ingest/modules/injectors/metadata_injector.py @@ -4,6 +4,7 @@ import logging +import traceback import mrc import pandas as pd @@ -46,6 +47,9 @@ def on_data(message: ControlMessage): "type": content_type.name.lower(), }, "error_metadata": None, + "audio_metadata": ( + None if content_type != ContentTypeEnum.AUDIO else {"audio_type": row["document_type"]} + ), "image_metadata": ( None if content_type != ContentTypeEnum.IMAGE else {"image_type": row["document_type"]} ), @@ -78,7 +82,12 @@ def _metadata_injection(builder: mrc.Builder): raise_on_failure=validated_config.raise_on_failure, ) def _on_data(message: ControlMessage): - return on_data(message) + try: + return on_data(message) + except Exception as e: + logger.error(f"Unhandled exception in metadata_injector: {e}") + traceback.print_exc() + raise node = builder.make_node("metadata_injector", _on_data) diff --git a/src/nv_ingest/schemas/audio_extractor_schema.py b/src/nv_ingest/schemas/audio_extractor_schema.py index 49a3dc5d..6b00b4e2 100755 --- a/src/nv_ingest/schemas/audio_extractor_schema.py +++ b/src/nv_ingest/schemas/audio_extractor_schema.py @@ -73,12 +73,8 @@ def clean_service(service): return None return service - - print ('===> audio extractor schema values:', values) endpoint_name = "audio_endpoints" grpc_service, http_service = values.get(endpoint_name) - print ("grpc_service:", grpc_service) - print ("http_service:", http_service) grpc_service = clean_service(grpc_service) http_service = clean_service(http_service) @@ -90,9 +86,9 @@ def clean_service(service): protocol_name = "audio_infer_protocol" protocol_value = values.get(protocol_name) - print("protocol_value:", protocol_value) if not protocol_value: protocol_value = "http" if http_service else "grpc" if grpc_service else "" + protocol_value = protocol_value.lower() values[protocol_name] = protocol_value diff --git a/src/nv_ingest/schemas/metadata_schema.py b/src/nv_ingest/schemas/metadata_schema.py index 9de9aba9..3183b57d 100644 --- a/src/nv_ingest/schemas/metadata_schema.py +++ b/src/nv_ingest/schemas/metadata_schema.py @@ -299,6 +299,11 @@ class ChartMetadataSchema(BaseModelNoExt): uploaded_image_uri: str = "" +class AudioMetadataSchema(BaseModelNoExt): + audio_transcript: str = "" + audio_type: str = "" + + # TODO consider deprecating this in favor of info msg... class ErrorMetadataSchema(BaseModelNoExt): task: TaskTypeEnum @@ -321,6 +326,7 @@ class MetadataSchema(BaseModelNoExt): embedding: Optional[List[float]] = None source_metadata: Optional[SourceMetadataSchema] = None content_metadata: Optional[ContentMetadataSchema] = None + audio_metadata: Optional[AudioMetadataSchema] = None text_metadata: Optional[TextMetadataSchema] = None image_metadata: Optional[ImageMetadataSchema] = None table_metadata: Optional[TableMetadataSchema] = None @@ -334,10 +340,12 @@ class MetadataSchema(BaseModelNoExt): @classmethod def check_metadata_type(cls, values): content_type = values.get("content_metadata", {}).get("type", None) - if content_type != ContentTypeEnum.TEXT: - values["text_metadata"] = None + if content_type != ContentTypeEnum.AUDIO: + values["audio_metadata"] = None if content_type != ContentTypeEnum.IMAGE: values["image_metadata"] = None + if content_type != ContentTypeEnum.TEXT: + values["text_metadata"] = None if content_type != ContentTypeEnum.STRUCTURED: values["table_metadata"] = None return values diff --git a/src/nv_ingest/stages/nim/audio_extraction.py b/src/nv_ingest/stages/nim/audio_extraction.py index 55556936..d554eb3e 100755 --- a/src/nv_ingest/stages/nim/audio_extraction.py +++ b/src/nv_ingest/stages/nim/audio_extraction.py @@ -4,27 +4,24 @@ import logging import functools +import traceback + import pandas as pd from typing import Any from typing import Dict from typing import Optional from typing import Tuple -import tritonclient.grpc as grpcclient from morpheus.config import Config from nv_ingest.schemas.audio_extractor_schema import AudioExtractorSchema from nv_ingest.stages.multiprocessing_stage import MultiProcessingBaseStage -import sys -sys.path.append('../../..') - from nv_ingest.util.nim.helpers import call_audio_inference_model, create_inference_client -from nv_ingest.util.nim.helpers import get_version logger = logging.getLogger(f"morpheus.{__name__}") -def _update_metadata(row: pd.Series, audio_client: Any, audio_version: Any, trace_info: Dict) -> Dict: +def _update_metadata(row: pd.Series, audio_client: Any, trace_info: Dict) -> Dict: """ Modifies the metadata of a row if the conditions for table extraction are met. @@ -50,9 +47,8 @@ def _update_metadata(row: pd.Series, audio_client: Any, audio_version: Any, trac If critical information (such as metadata) is missing from the row. """ - metadata = row.get("metadata") - + if metadata is None: logger.error("Row does not contain 'metadata'.") raise ValueError("Row does not contain 'metadata'.") @@ -60,31 +56,30 @@ def _update_metadata(row: pd.Series, audio_client: Any, audio_version: Any, trac content_metadata = metadata.get("content_metadata", {}) # Only modify if content type is audio - if content_metadata.get("type") != "audio" : + # TODO(Devin): Double check dtypes (metadata_schema.py:39) + if content_metadata.get("type") != "audio": return metadata source_metadata = metadata.get("source_metadata") - audio_id = source_metadata['source_id'] - - content_metadata = metadata.get("content_metadata") - content_metadata = content_metadata['content'] - audio_content = content_metadata['content'] - + audio_id = source_metadata["source_id"] + + audio_content = metadata.get("content") # Modify audio metadata with the result from the inference model try: audio_result = call_audio_inference_model(audio_client, audio_content, audio_id, trace_info=trace_info) print(audio_result) - metadata['audio_metadata'] = {'content': audio_result} + metadata["audio_metadata"] = {"audio_transcript": audio_result} except Exception as e: logger.error(f"Unhandled error calling audio inference model: {e}", exc_info=True) raise - + return metadata -def _transcribe_audio(df: pd.DataFrame, task_props: Dict[str, Any], - validated_config: Any, trace_info: Optional[Dict] = None) -> Tuple[pd.DataFrame, Dict]: +def _transcribe_audio( + df: pd.DataFrame, task_props: Dict[str, Any], validated_config: Any, trace_info: Optional[Dict] = None +) -> Tuple[pd.DataFrame, Dict]: """ Extracts audio data from a DataFrame. @@ -113,19 +108,18 @@ def _transcribe_audio(df: pd.DataFrame, task_props: Dict[str, Any], If any error occurs during the audio data extraction process. """ - #port = 32783 - #audio_client = create_inference_client( + # port = 32783 + # audio_client = create_inference_client( # (None, f'http://0.0.0.0:{port}/v1/transcribe'), # None, # "http" - #) + # ) + + logger.debug(f"Entering audio extraction stage with {len(df)} rows.") + + _ = task_props - - audio_client = create_inference_client( - validated_config.stage_config.audio_endpoints, - None, - "http" - ) + audio_client = create_inference_client(validated_config.stage_config.audio_endpoints, None, "http") if trace_info is None: trace_info = {} @@ -133,23 +127,24 @@ def _transcribe_audio(df: pd.DataFrame, task_props: Dict[str, Any], try: # Apply the _update_metadata function to each row in the DataFrame - #audio_version = get_version(validated_config.stage_config.audio_endpoints[1]) - audio_version = get_version(f'http://audio:{port}') - df["metadata"] = df.apply(_update_metadata, axis=1, args=(audio_client, audio_version, trace_info)) - + # audio_version = get_version(validated_config.stage_config.audio_endpoints[1]) + # audio_version = get_version(f'http://audio:{port}') + df["metadata"] = df.apply(_update_metadata, axis=1, args=(audio_client, trace_info)) + return df, trace_info except Exception as e: - logger.error("Error occurred while extracting audio data.", exc_info=True) + traceback.print_exc() + logger.error(f"Error occurred while extracting audio data: {e}", exc_info=True) raise def generate_audio_extractor_stage( - c: Config, - stage_config: Dict[str, Any], - task: str = "audio_data_extract", - task_desc: str = "audio_data_extraction", - pe_count: int = 1, + c: Config, + stage_config: Dict[str, Any], + task: str = "audio_data_extract", + task_desc: str = "audio_data_extraction", + pe_count: int = 1, ): """ Generates a multiprocessing stage to perform audio data extraction. @@ -186,16 +181,15 @@ def generate_audio_extractor_stage( _wrapped_process_fn = functools.partial(_transcribe_audio, validated_config=validated_config) return MultiProcessingBaseStage( - c=c, - pe_count=pe_count, - task=task, - task_desc=task_desc, + c=c, + pe_count=pe_count, + task=task, + task_desc=task_desc, process_fn=_wrapped_process_fn, document_type="regex:^(mp3|wav)$", ) - if __name__ == "__main__": metadata = { "source_metadata": { @@ -207,17 +201,11 @@ def generate_audio_extractor_stage( "source_id": "https://audio.listennotes.com/e/p/3946bc3aba1f425f8b2e146f0b3f72fc/", "source_location": "", "source_type": "wav", - "summary": "" + "summary": "", }, - - "content_metadata": { - "description": "Audio wav file", - "type": "audio", - "content": '' - } + "content_metadata": {"description": "Audio wav file", "type": "audio", "content": ""}, } - metadata = { "source_metadata": { "access_level": 1, @@ -228,27 +216,18 @@ def generate_audio_extractor_stage( "source_id": "test.mp3", "source_location": "", "source_type": "mp3", - "summary": "" + "summary": "", }, - - "content_metadata": { - "description": "Audio wav file", - "type": "audio", - "content": 'some base64 string' - } + "content_metadata": {"description": "Audio wav file", "type": "audio", "content": "some base64 string"}, } - - data = [{"metadata": metadata}] df = pd.DataFrame(data) - df.to_csv('test.csv', index=False) - - df_result, _ = _transcribe_audio(df) + df.to_csv("test.csv", index=False) - df_result.to_csv('result.csv', index=False) + df_result, _ = _transcribe_audio(df) + df_result.to_csv("result.csv", index=False) - print("Done!") diff --git a/src/nv_ingest/util/converters/type_mappings.py b/src/nv_ingest/util/converters/type_mappings.py index 4fbfb0a9..2a8a8626 100644 --- a/src/nv_ingest/util/converters/type_mappings.py +++ b/src/nv_ingest/util/converters/type_mappings.py @@ -11,12 +11,14 @@ DocumentTypeEnum.docx: ContentTypeEnum.STRUCTURED, DocumentTypeEnum.html: ContentTypeEnum.STRUCTURED, DocumentTypeEnum.jpeg: ContentTypeEnum.IMAGE, + DocumentTypeEnum.mp3: ContentTypeEnum.AUDIO, DocumentTypeEnum.pdf: ContentTypeEnum.STRUCTURED, DocumentTypeEnum.png: ContentTypeEnum.IMAGE, DocumentTypeEnum.pptx: ContentTypeEnum.STRUCTURED, DocumentTypeEnum.svg: ContentTypeEnum.IMAGE, DocumentTypeEnum.tiff: ContentTypeEnum.IMAGE, DocumentTypeEnum.txt: ContentTypeEnum.TEXT, + DocumentTypeEnum.wav: ContentTypeEnum.AUDIO, } From 7a3c3402994a09bf658b1b3ba1f24ba0ec28fbb6 Mon Sep 17 00:00:00 2001 From: Devin Robison Date: Fri, 17 Jan 2025 15:20:39 -0700 Subject: [PATCH 5/7] Add ModelInferenceInterface for Parakeet --- .../schemas/ingest_pipeline_config_schema.py | 1 + src/nv_ingest/stages/nim/audio_extraction.py | 105 +++++++------- .../util/flow_control/filter_by_task.py | 112 +++++++++----- src/nv_ingest/util/nim/helpers.py | 26 ++-- src/nv_ingest/util/nim/parakeet.py | 137 ++++++++++++++++++ 5 files changed, 281 insertions(+), 100 deletions(-) create mode 100644 src/nv_ingest/util/nim/parakeet.py diff --git a/src/nv_ingest/schemas/ingest_pipeline_config_schema.py b/src/nv_ingest/schemas/ingest_pipeline_config_schema.py index fe5debd6..60d15a07 100644 --- a/src/nv_ingest/schemas/ingest_pipeline_config_schema.py +++ b/src/nv_ingest/schemas/ingest_pipeline_config_schema.py @@ -29,6 +29,7 @@ class PipelineConfigSchema(BaseModel): + # TODO(Devin): Audio chart_extractor_module: ChartExtractorSchema = ChartExtractorSchema() document_splitter_module: DocumentSplitterSchema = DocumentSplitterSchema() embedding_storage_module: EmbeddingStorageModuleSchema = EmbeddingStorageModuleSchema() diff --git a/src/nv_ingest/stages/nim/audio_extraction.py b/src/nv_ingest/stages/nim/audio_extraction.py index d554eb3e..3b33ee7b 100755 --- a/src/nv_ingest/stages/nim/audio_extraction.py +++ b/src/nv_ingest/stages/nim/audio_extraction.py @@ -17,6 +17,7 @@ from nv_ingest.stages.multiprocessing_stage import MultiProcessingBaseStage from nv_ingest.util.nim.helpers import call_audio_inference_model, create_inference_client +from nv_ingest.util.nim.parakeet import ParakeetModelInterface logger = logging.getLogger(f"morpheus.{__name__}") @@ -53,25 +54,23 @@ def _update_metadata(row: pd.Series, audio_client: Any, trace_info: Dict) -> Dic logger.error("Row does not contain 'metadata'.") raise ValueError("Row does not contain 'metadata'.") + base64_audio = metadata.get("content") content_metadata = metadata.get("content_metadata", {}) # Only modify if content type is audio - # TODO(Devin): Double check dtypes (metadata_schema.py:39) if content_metadata.get("type") != "audio": return metadata source_metadata = metadata.get("source_metadata") audio_id = source_metadata["source_id"] - audio_content = metadata.get("content") - # Modify audio metadata with the result from the inference model try: - audio_result = call_audio_inference_model(audio_client, audio_content, audio_id, trace_info=trace_info) - print(audio_result) + audio_result = call_audio_inference_model(audio_client, base64_audio, audio_id, trace_info=trace_info) metadata["audio_metadata"] = {"audio_transcript": audio_result} except Exception as e: logger.error(f"Unhandled error calling audio inference model: {e}", exc_info=True) + traceback.print_exc() raise return metadata @@ -119,7 +118,13 @@ def _transcribe_audio( _ = task_props - audio_client = create_inference_client(validated_config.stage_config.audio_endpoints, None, "http") + parakeet_model_interface = ParakeetModelInterface() + parakeet_client = create_inference_client( + validated_config.audio_extraction_config.audio_endpoints, + parakeet_model_interface, + auth_token=validated_config.audio_extraction_config.auth_token, + infer_protocol=validated_config.audio_extraction_config.audio_infer_protocol, + ) if trace_info is None: trace_info = {} @@ -129,7 +134,7 @@ def _transcribe_audio( # Apply the _update_metadata function to each row in the DataFrame # audio_version = get_version(validated_config.stage_config.audio_endpoints[1]) # audio_version = get_version(f'http://audio:{port}') - df["metadata"] = df.apply(_update_metadata, axis=1, args=(audio_client, trace_info)) + df["metadata"] = df.apply(_update_metadata, axis=1, args=(parakeet_client, trace_info)) return df, trace_info @@ -186,48 +191,50 @@ def generate_audio_extractor_stage( task=task, task_desc=task_desc, process_fn=_wrapped_process_fn, - document_type="regex:^(mp3|wav)$", + # document_type="regex:^(mp3|wav)$", + document_type="wav", ) -if __name__ == "__main__": - metadata = { - "source_metadata": { - "access_level": 1, - "collection_id": "", - "date_created": "2024-11-04T12:29:08", - "last_modified": "2024-11-04T12:29:08", - "partition_id": -1, - "source_id": "https://audio.listennotes.com/e/p/3946bc3aba1f425f8b2e146f0b3f72fc/", - "source_location": "", - "source_type": "wav", - "summary": "", - }, - "content_metadata": {"description": "Audio wav file", "type": "audio", "content": ""}, - } - - metadata = { - "source_metadata": { - "access_level": 1, - "collection_id": "", - "date_created": "2024-11-04T12:29:08", - "last_modified": "2024-11-04T12:29:08", - "partition_id": -1, - "source_id": "test.mp3", - "source_location": "", - "source_type": "mp3", - "summary": "", - }, - "content_metadata": {"description": "Audio wav file", "type": "audio", "content": "some base64 string"}, - } - - data = [{"metadata": metadata}] - df = pd.DataFrame(data) - - df.to_csv("test.csv", index=False) - - df_result, _ = _transcribe_audio(df) - - df_result.to_csv("result.csv", index=False) - - print("Done!") +# if __name__ == "__main__": +# metadata = { +# "source_metadata": { +# "access_level": 1, +# "collection_id": "", +# "date_created": "2024-11-04T12:29:08", +# "last_modified": "2024-11-04T12:29:08", +# "partition_id": -1, +# "source_id": "https://audio.listennotes.com/e/p/3946bc3aba1f425f8b2e146f0b3f72fc/", +# "source_location": "", +# "source_type": "wav", +# "summary": "", +# }, +# "content_metadata": {"description": "Audio wav file", "type": "audio", "content": ""}, +# } +# +# metadata = { +# "source_metadata": { +# "access_level": 1, +# "collection_id": "", +# "date_created": "2024-11-04T12:29:08", +# "last_modified": "2024-11-04T12:29:08", +# "partition_id": -1, +# "source_id": "test.mp3", +# "source_location": "", +# "source_type": "mp3", +# "summary": "", +# }, +# "content_metadata": {"description": "Audio wav file", "type": "audio", "content": "some base64 string"}, +# } +# +# data = [{"metadata": metadata}] +# df = pd.DataFrame(data) +# +# df.to_csv("test.csv", index=False) +# +# df_result, _ = _transcribe_audio(df) +# +# df_result.to_csv("result.csv", index=False) +# +# print("Done!") +# diff --git a/src/nv_ingest/util/flow_control/filter_by_task.py b/src/nv_ingest/util/flow_control/filter_by_task.py index c5be609c..586c4b16 100644 --- a/src/nv_ingest/util/flow_control/filter_by_task.py +++ b/src/nv_ingest/util/flow_control/filter_by_task.py @@ -3,13 +3,11 @@ # SPDX-License-Identifier: Apache-2.0 -import logging import re import typing from functools import wraps - from pydantic import BaseModel -from morpheus.messages import ControlMessage +import logging logger = logging.getLogger(__name__) @@ -25,10 +23,10 @@ def filter_by_task(required_tasks, forward_func=None): Parameters ---------- required_tasks : list - A list of task keys to check for in the ControlMessage. + A list of task keys (string or tuple/list of [task_name, task_property_dict(s)]) to check for in the + ControlMessage. forward_func : callable, optional - A function to be called with the ControlMessage if no required task is found. Defaults to - None. + A function to be called with the ControlMessage if no required task is found. Defaults to None. Returns ------- @@ -39,39 +37,66 @@ def filter_by_task(required_tasks, forward_func=None): def decorator(func): @wraps(func) def wrapper(*args, **kwargs): - if args and hasattr(args[0], "get_tasks"): - message = args[0] - tasks = message.get_tasks() - for required_task in required_tasks: - if isinstance(required_task, str) and (required_task in tasks): - return func(*args, **kwargs) + if not args or not hasattr(args[0], "get_tasks"): + raise ValueError("The first argument must be a ControlMessage object with task handling capabilities.") + + message = args[0] + tasks = message.get_tasks() + logger.debug(f"Tasks in message: {list(tasks.keys())}") + logger.debug(f"Required tasks: {required_tasks}") - if isinstance(required_task, tuple) or isinstance(required_task, list): - required_task_name, *required_task_props_list = required_task - if required_task_name not in tasks: - continue - - task_props_list = tasks.get(required_task_name, []) - logger.debug(f"Checking task properties for: {required_task_name}") - logger.debug(f"Required task properties: {required_task_props_list}") - for task_props in task_props_list: - if isinstance(task_props, BaseModel): - task_props = task_props.model_dump() - - if all( - _is_subset(task_props, required_task_props) - for required_task_props in required_task_props_list - ): - return func(*args, **kwargs) - - if forward_func: - # If a forward function is provided, call it with the ControlMessage - return forward_func(message) - else: - # If no forward function is provided, return the message directly - return message + for required_task in required_tasks: + # 1) If the required task is a string (simple check for existence) + if isinstance(required_task, str): + if required_task in tasks: + logger.debug(f"Found required task '{required_task}'. Executing function.") + return func(*args, **kwargs) + else: + logger.debug(f"Task '{required_task}' not found in ControlMessage. Skipping.") + + # 2) If the required task is a tuple/list: (task_name, {prop_key: prop_val}, ...) + elif isinstance(required_task, (tuple, list)): + required_task_name, *required_task_props_list = required_task + if required_task_name not in tasks: + logger.debug(f"Task '{required_task_name}' not found in ControlMessage. Skipping.") + continue + + # We have at least one task of this type. Check the properties: + task_props_list = tasks.get(required_task_name, []) + logger.debug(f"Checking task properties for '{required_task_name}': {task_props_list}") + logger.debug(f"Required task properties: {required_task_props_list}") + + # Check each set of task_props against the required subset(s) + for task_props in task_props_list: + if isinstance(task_props, BaseModel): + task_props = task_props.model_dump() + + # We need to match *all* required_task_props in `required_task_props_list` + # with the current `task_props`. + if all( + _is_subset(task_props, required_task_props) + for required_task_props in required_task_props_list + ): + logger.debug( + f"Task '{required_task_name}' with properties {task_props} " + f"matches all required properties. Executing function." + ) + return func(*args, **kwargs) + else: + logger.debug( + f"Task '{required_task_name}' with properties {task_props} " + f"does not match all required properties {required_task_props_list}. Skipping." + ) + + # If we got here, it means none of the required tasks or properties matched + logger.debug("No required tasks matched. Forwarding or returning message as configured.") + + if forward_func: + # If a forward function is provided, call it with the ControlMessage + return forward_func(message) else: - raise ValueError("The first argument must be a ControlMessage object with task handling capabilities.") + # If no forward function is provided, return the message directly + return message return wrapper @@ -81,8 +106,10 @@ def wrapper(*args, **kwargs): def _is_subset(superset, subset): if subset == "*": return True + if isinstance(superset, dict) and isinstance(subset, dict): return all(key in superset and _is_subset(superset[key], val) for key, val in subset.items()) + if isinstance(subset, str) and subset.startswith("regex:"): # The subset is a regex pattern pattern = subset[len("regex:") :] @@ -90,15 +117,19 @@ def _is_subset(superset, subset): return any(re.match(pattern, str(sup_item)) for sup_item in superset) else: return re.match(pattern, str(superset)) is not None + if isinstance(superset, list) and not isinstance(subset, list): # Check if the subset value matches any item in the superset return any(_is_subset(sup_item, subset) for sup_item in superset) - if isinstance(superset, list) or isinstance(superset, set): + + if isinstance(superset, (list, set)) and isinstance(subset, (list, set)): + # Check if each sub_item in `subset` is in `superset` (by subset matching) return all(any(_is_subset(sup_item, sub_item) for sup_item in superset) for sub_item in subset) + return superset == subset -def remove_task_subset(ctrl_msg: ControlMessage, task_type: typing.List, subset: typing.Dict): +def remove_task_subset(ctrl_msg: typing.Any, task_type: typing.List, subset: typing.Dict): """ A helper function to extract a task based on subset matching when the task might be out of order with respect to the Morpheus pipeline. For example, if a deduplication filter occurs before scale filtering in the pipeline, but @@ -127,6 +158,9 @@ def remove_task_subset(ctrl_msg: ControlMessage, task_type: typing.List, subset: for _ in ctrl_msg_tasks[task_type]: task_props = ctrl_msg.remove_task(task_type) if _is_subset(task_props, subset): + logger.debug( + f"Removed task '{task_type}' with properties {task_props} " f"matching subset {subset}." + ) break filter_tasks.append(task_props) break diff --git a/src/nv_ingest/util/nim/helpers.py b/src/nv_ingest/util/nim/helpers.py index a692265f..96a1b04e 100644 --- a/src/nv_ingest/util/nim/helpers.py +++ b/src/nv_ingest/util/nim/helpers.py @@ -603,10 +603,14 @@ def call_audio_inference_model(client, audio_content: str, audio_id: str, trace_ Parameters ---------- - client : grpcclient.InferenceServerClient or dict + client : The inference client, which is an HTTP client. - audio_source : str + audio_content: str The audio source to transcribe. + audio_id: str + The unique identifier for the audio content. + trace_info: dict + Trace information for debugging or logging. Returns ------- @@ -620,16 +624,16 @@ def call_audio_inference_model(client, audio_content: str, audio_id: str, trace_ """ try: - url = client["endpoint_url"] - headers = client["headers"] + data = {"base64_audio": audio_content, "audio_id": audio_id} - payload = {"audio_content": audio_content, "audio_id": audio_id} - response = requests.post(url, json=payload, headers=headers) - - response.raise_for_status() # Raise an exception for HTTP errors + parakeet_result = client.infer( + data, + model_name="parakeet", + trace_info=trace_info, # traceable_func arg + stage_name="audio_extraction", + ) - # Parse the JSON response - json_response = response.json() + return parakeet_result except requests.exceptions.RequestException as e: raise RuntimeError(f"HTTP request failed: {e}") @@ -637,5 +641,3 @@ def call_audio_inference_model(client, audio_content: str, audio_id: str, trace_ raise RuntimeError(f"Missing expected key in response: {e}") except Exception as e: raise RuntimeError(f"An error occurred during inference: {e}") - - return json_response diff --git a/src/nv_ingest/util/nim/parakeet.py b/src/nv_ingest/util/nim/parakeet.py new file mode 100644 index 00000000..19c5daab --- /dev/null +++ b/src/nv_ingest/util/nim/parakeet.py @@ -0,0 +1,137 @@ +import logging +import requests +from typing import Any, Dict, Optional + +from nv_ingest.util.nim.helpers import ModelInterface + +logger = logging.getLogger(__name__) + + +class ParakeetModelInterface(ModelInterface): + """ + A simple interface for handling inference with a Parakeet model (e.g., speech, audio-related). + """ + + def name(self) -> str: + """ + Get the name of the model interface. + + Returns + ------- + str + The name of the model interface ("Parakeet"). + """ + return "Parakeet" + + def prepare_data_for_inference(self, data: Dict[str, Any]) -> Dict[str, Any]: + """ + Prepare input data for inference. This can be as simple or complex as needed. + Here, we assume 'audio_content' and 'audio_id' are already in the right format. + + Parameters + ---------- + data : dict + The input data containing an audio payload. + + Returns + ------- + dict + The updated data dictionary (possibly identical if no special processing is required). + """ + + return data + + def format_input(self, data: Dict[str, Any], protocol: str, **kwargs) -> Any: + """ + Format input data for the specified protocol (e.g., HTTP). + Here, we assume a simple JSON payload containing 'audio_content' and 'audio_id'. + + Parameters + ---------- + data : dict + The input data to format. + protocol : str + The protocol to use ("http"). + **kwargs : dict + Additional parameters for HTTP payload formatting if needed. + + Returns + ------- + Any + The formatted input data. + + Raises + ------ + ValueError + If an invalid protocol is specified. + """ + if protocol == "http": + logger.debug("Formatting input for HTTP Parakeet model") + # For HTTP, we just build a simple JSON payload + payload = {"audio_content": data["base64_audio"], "audio_id": data["audio_id"]} + return payload + else: + raise ValueError("Invalid protocol specified. Must be 'http' for Parakeet.") + + def parse_output(self, response: Any, protocol: str, data: Optional[Dict[str, Any]] = None, **kwargs) -> Any: + """ + Parse the output from the model's inference response. + + Parameters + ---------- + response : requests.Response + The response from the model inference (for HTTP). + protocol : str + The protocol used ("http"). + data : dict, optional + Additional input data passed to the function (not used in this simple example). + + Returns + ------- + dict + The JSON-parsed output from the Parakeet model. + + Raises + ------ + ValueError + If an invalid protocol is specified. + RuntimeError + For any HTTP-related or unexpected errors (e.g., missing keys). + """ + if protocol == "http": + logger.debug("Parsing output from HTTP Parakeet model") + try: + response.raise_for_status() # Raise an exception for HTTP errors + json_response = response.json() + except requests.exceptions.RequestException as e: + raise RuntimeError(f"HTTP request failed: {e}") + except KeyError as e: + raise RuntimeError(f"Missing expected key in response: {e}") + except Exception as e: + raise RuntimeError(f"An error occurred during inference: {e}") + + return json_response + else: + raise ValueError("Invalid protocol specified. Must be 'http' for Parakeet.") + + def process_inference_results(self, output: Any, protocol: str, **kwargs) -> Any: + """ + Process inference results for the Parakeet model. In this simple case, + we simply return the output as-is. + + Parameters + ---------- + output : Any + The raw output from the model. + protocol : str + The protocol used ("http"). + **kwargs : dict + Additional parameters as needed. + + Returns + ------- + Any + The processed inference results. + """ + logger.debug("Processing Parakeet inference results (pass-through).") + return output From 8954924cd8e7545caf86c77993cb64e79bb2a650 Mon Sep 17 00:00:00 2001 From: Devin Robison Date: Fri, 17 Jan 2025 16:43:11 -0700 Subject: [PATCH 6/7] Update parakeet interface to generate audio_id --- src/nv_ingest/util/nim/parakeet.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/nv_ingest/util/nim/parakeet.py b/src/nv_ingest/util/nim/parakeet.py index 19c5daab..e8110856 100644 --- a/src/nv_ingest/util/nim/parakeet.py +++ b/src/nv_ingest/util/nim/parakeet.py @@ -1,4 +1,6 @@ import logging +import uuid + import requests from typing import Any, Dict, Optional @@ -68,7 +70,8 @@ def format_input(self, data: Dict[str, Any], protocol: str, **kwargs) -> Any: if protocol == "http": logger.debug("Formatting input for HTTP Parakeet model") # For HTTP, we just build a simple JSON payload - payload = {"audio_content": data["base64_audio"], "audio_id": data["audio_id"]} + # audio_id just needs to be a unique identifier + payload = {"audio_content": data["base64_audio"], "audio_id": f"{str(uuid.uuid4())}.wav"} return payload else: raise ValueError("Invalid protocol specified. Must be 'http' for Parakeet.") From 4e8ede8ebf6512b04fca17406c7769bbe2c934c9 Mon Sep 17 00:00:00 2001 From: Devin Robison Date: Tue, 21 Jan 2025 12:47:04 -0700 Subject: [PATCH 7/7] Update parakeep NimHandler --- src/nv_ingest/util/nim/parakeet.py | 16 +--------------- 1 file changed, 1 insertion(+), 15 deletions(-) diff --git a/src/nv_ingest/util/nim/parakeet.py b/src/nv_ingest/util/nim/parakeet.py index e8110856..f9183828 100644 --- a/src/nv_ingest/util/nim/parakeet.py +++ b/src/nv_ingest/util/nim/parakeet.py @@ -1,7 +1,6 @@ import logging import uuid -import requests from typing import Any, Dict, Optional from nv_ingest.util.nim.helpers import ModelInterface @@ -102,20 +101,7 @@ def parse_output(self, response: Any, protocol: str, data: Optional[Dict[str, An For any HTTP-related or unexpected errors (e.g., missing keys). """ if protocol == "http": - logger.debug("Parsing output from HTTP Parakeet model") - try: - response.raise_for_status() # Raise an exception for HTTP errors - json_response = response.json() - except requests.exceptions.RequestException as e: - raise RuntimeError(f"HTTP request failed: {e}") - except KeyError as e: - raise RuntimeError(f"Missing expected key in response: {e}") - except Exception as e: - raise RuntimeError(f"An error occurred during inference: {e}") - - return json_response - else: - raise ValueError("Invalid protocol specified. Must be 'http' for Parakeet.") + return response def process_inference_results(self, output: Any, protocol: str, **kwargs) -> Any: """