groundx 3.8.2__tar.gz → 3.8.4__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {groundx-3.8.2 → groundx-3.8.4}/PKG-INFO +1 -1
- {groundx-3.8.2 → groundx-3.8.4}/pyproject.toml +1 -1
- {groundx-3.8.2 → groundx-3.8.4}/src/groundx/core/client_wrapper.py +2 -2
- {groundx-3.8.2 → groundx-3.8.4}/src/groundx/extract/classes/document.py +11 -9
- {groundx-3.8.2 → groundx-3.8.4}/src/groundx/extract/classes/groundx.py +8 -4
- {groundx-3.8.2 → groundx-3.8.4}/src/groundx/extract/custom_outputs.py +235 -5
- {groundx-3.8.2 → groundx-3.8.4}/src/groundx/extract/prompt/utility.py +57 -1
- groundx-3.8.4/src/groundx/extract/services/http.py +217 -0
- {groundx-3.8.2 → groundx-3.8.4}/src/groundx/extract/services/logger.py +8 -7
- {groundx-3.8.2 → groundx-3.8.4}/LICENSE +0 -0
- {groundx-3.8.2 → groundx-3.8.4}/README.md +0 -0
- {groundx-3.8.2 → groundx-3.8.4}/src/groundx/__init__.py +0 -0
- {groundx-3.8.2 → groundx-3.8.4}/src/groundx/_default_clients.py +0 -0
- {groundx-3.8.2 → groundx-3.8.4}/src/groundx/api_key/__init__.py +0 -0
- {groundx-3.8.2 → groundx-3.8.4}/src/groundx/api_key/client.py +0 -0
- {groundx-3.8.2 → groundx-3.8.4}/src/groundx/api_key/raw_client.py +0 -0
- {groundx-3.8.2 → groundx-3.8.4}/src/groundx/buckets/__init__.py +0 -0
- {groundx-3.8.2 → groundx-3.8.4}/src/groundx/buckets/client.py +0 -0
- {groundx-3.8.2 → groundx-3.8.4}/src/groundx/buckets/raw_client.py +0 -0
- {groundx-3.8.2 → groundx-3.8.4}/src/groundx/client.py +0 -0
- {groundx-3.8.2 → groundx-3.8.4}/src/groundx/core/__init__.py +0 -0
- {groundx-3.8.2 → groundx-3.8.4}/src/groundx/core/api_error.py +0 -0
- {groundx-3.8.2 → groundx-3.8.4}/src/groundx/core/datetime_utils.py +0 -0
- {groundx-3.8.2 → groundx-3.8.4}/src/groundx/core/file.py +0 -0
- {groundx-3.8.2 → groundx-3.8.4}/src/groundx/core/force_multipart.py +0 -0
- {groundx-3.8.2 → groundx-3.8.4}/src/groundx/core/http_client.py +0 -0
- {groundx-3.8.2 → groundx-3.8.4}/src/groundx/core/http_response.py +0 -0
- {groundx-3.8.2 → groundx-3.8.4}/src/groundx/core/http_sse/__init__.py +0 -0
- {groundx-3.8.2 → groundx-3.8.4}/src/groundx/core/http_sse/_api.py +0 -0
- {groundx-3.8.2 → groundx-3.8.4}/src/groundx/core/http_sse/_decoders.py +0 -0
- {groundx-3.8.2 → groundx-3.8.4}/src/groundx/core/http_sse/_exceptions.py +0 -0
- {groundx-3.8.2 → groundx-3.8.4}/src/groundx/core/http_sse/_models.py +0 -0
- {groundx-3.8.2 → groundx-3.8.4}/src/groundx/core/jsonable_encoder.py +0 -0
- {groundx-3.8.2 → groundx-3.8.4}/src/groundx/core/logging.py +0 -0
- {groundx-3.8.2 → groundx-3.8.4}/src/groundx/core/parse_error.py +0 -0
- {groundx-3.8.2 → groundx-3.8.4}/src/groundx/core/pydantic_utilities.py +0 -0
- {groundx-3.8.2 → groundx-3.8.4}/src/groundx/core/query_encoder.py +0 -0
- {groundx-3.8.2 → groundx-3.8.4}/src/groundx/core/remove_none_from_dict.py +0 -0
- {groundx-3.8.2 → groundx-3.8.4}/src/groundx/core/request_options.py +0 -0
- {groundx-3.8.2 → groundx-3.8.4}/src/groundx/core/serialization.py +0 -0
- {groundx-3.8.2 → groundx-3.8.4}/src/groundx/csv_splitter.py +0 -0
- {groundx-3.8.2 → groundx-3.8.4}/src/groundx/customer/__init__.py +0 -0
- {groundx-3.8.2 → groundx-3.8.4}/src/groundx/customer/client.py +0 -0
- {groundx-3.8.2 → groundx-3.8.4}/src/groundx/customer/raw_client.py +0 -0
- {groundx-3.8.2 → groundx-3.8.4}/src/groundx/documents/__init__.py +0 -0
- {groundx-3.8.2 → groundx-3.8.4}/src/groundx/documents/client.py +0 -0
- {groundx-3.8.2 → groundx-3.8.4}/src/groundx/documents/raw_client.py +0 -0
- {groundx-3.8.2 → groundx-3.8.4}/src/groundx/environment.py +0 -0
- {groundx-3.8.2 → groundx-3.8.4}/src/groundx/errors/__init__.py +0 -0
- {groundx-3.8.2 → groundx-3.8.4}/src/groundx/errors/bad_request_error.py +0 -0
- {groundx-3.8.2 → groundx-3.8.4}/src/groundx/errors/unauthorized_error.py +0 -0
- {groundx-3.8.2 → groundx-3.8.4}/src/groundx/extract/__init__.py +0 -0
- {groundx-3.8.2 → groundx-3.8.4}/src/groundx/extract/agents/__init__.py +0 -0
- {groundx-3.8.2 → groundx-3.8.4}/src/groundx/extract/agents/agent.py +0 -0
- {groundx-3.8.2 → groundx-3.8.4}/src/groundx/extract/classes/__init__.py +0 -0
- {groundx-3.8.2 → groundx-3.8.4}/src/groundx/extract/classes/agent.py +0 -0
- {groundx-3.8.2 → groundx-3.8.4}/src/groundx/extract/classes/api.py +0 -0
- {groundx-3.8.2 → groundx-3.8.4}/src/groundx/extract/classes/element.py +0 -0
- {groundx-3.8.2 → groundx-3.8.4}/src/groundx/extract/classes/field.py +0 -0
- {groundx-3.8.2 → groundx-3.8.4}/src/groundx/extract/classes/group.py +0 -0
- {groundx-3.8.2 → groundx-3.8.4}/src/groundx/extract/classes/prompt.py +0 -0
- {groundx-3.8.2 → groundx-3.8.4}/src/groundx/extract/classes/testing.py +0 -0
- {groundx-3.8.2 → groundx-3.8.4}/src/groundx/extract/post_process/__init__.py +0 -0
- {groundx-3.8.2 → groundx-3.8.4}/src/groundx/extract/post_process/post_process.py +0 -0
- {groundx-3.8.2 → groundx-3.8.4}/src/groundx/extract/prompt/__init__.py +0 -0
- {groundx-3.8.2 → groundx-3.8.4}/src/groundx/extract/prompt/manager.py +0 -0
- {groundx-3.8.2 → groundx-3.8.4}/src/groundx/extract/prompt/object_store.py +0 -0
- {groundx-3.8.2 → groundx-3.8.4}/src/groundx/extract/prompt/source.py +0 -0
- {groundx-3.8.2 → groundx-3.8.4}/src/groundx/extract/services/.DS_Store +0 -0
- {groundx-3.8.2 → groundx-3.8.4}/src/groundx/extract/services/__init__.py +0 -0
- {groundx-3.8.2 → groundx-3.8.4}/src/groundx/extract/services/csv.py +0 -0
- {groundx-3.8.2 → groundx-3.8.4}/src/groundx/extract/services/logging_cfg.py +0 -0
- {groundx-3.8.2 → groundx-3.8.4}/src/groundx/extract/services/ratelimit.py +0 -0
- {groundx-3.8.2 → groundx-3.8.4}/src/groundx/extract/services/sheets_client.py +0 -0
- {groundx-3.8.2 → groundx-3.8.4}/src/groundx/extract/services/status.py +0 -0
- {groundx-3.8.2 → groundx-3.8.4}/src/groundx/extract/services/upload.py +0 -0
- {groundx-3.8.2 → groundx-3.8.4}/src/groundx/extract/services/upload_minio.py +0 -0
- {groundx-3.8.2 → groundx-3.8.4}/src/groundx/extract/services/upload_s3.py +0 -0
- {groundx-3.8.2 → groundx-3.8.4}/src/groundx/extract/services/utility.py +0 -0
- {groundx-3.8.2 → groundx-3.8.4}/src/groundx/extract/settings/__init__.py +0 -0
- {groundx-3.8.2 → groundx-3.8.4}/src/groundx/extract/settings/settings.py +0 -0
- {groundx-3.8.2 → groundx-3.8.4}/src/groundx/extract/tasks/__init__.py +0 -0
- {groundx-3.8.2 → groundx-3.8.4}/src/groundx/extract/tasks/utility.py +0 -0
- {groundx-3.8.2 → groundx-3.8.4}/src/groundx/extract/utility/__init__.py +0 -0
- {groundx-3.8.2 → groundx-3.8.4}/src/groundx/extract/utility/utility.py +0 -0
- {groundx-3.8.2 → groundx-3.8.4}/src/groundx/extract/workflows.py +0 -0
- {groundx-3.8.2 → groundx-3.8.4}/src/groundx/groups/__init__.py +0 -0
- {groundx-3.8.2 → groundx-3.8.4}/src/groundx/groups/client.py +0 -0
- {groundx-3.8.2 → groundx-3.8.4}/src/groundx/groups/raw_client.py +0 -0
- {groundx-3.8.2 → groundx-3.8.4}/src/groundx/health/__init__.py +0 -0
- {groundx-3.8.2 → groundx-3.8.4}/src/groundx/health/client.py +0 -0
- {groundx-3.8.2 → groundx-3.8.4}/src/groundx/health/raw_client.py +0 -0
- {groundx-3.8.2 → groundx-3.8.4}/src/groundx/ingest.py +0 -0
- {groundx-3.8.2 → groundx-3.8.4}/src/groundx/py.typed +0 -0
- {groundx-3.8.2 → groundx-3.8.4}/src/groundx/search/__init__.py +0 -0
- {groundx-3.8.2 → groundx-3.8.4}/src/groundx/search/client.py +0 -0
- {groundx-3.8.2 → groundx-3.8.4}/src/groundx/search/raw_client.py +0 -0
- {groundx-3.8.2 → groundx-3.8.4}/src/groundx/search/types/__init__.py +0 -0
- {groundx-3.8.2 → groundx-3.8.4}/src/groundx/search/types/search_content_request_id.py +0 -0
- {groundx-3.8.2 → groundx-3.8.4}/src/groundx/types/__init__.py +0 -0
- {groundx-3.8.2 → groundx-3.8.4}/src/groundx/types/api_key_detail.py +0 -0
- {groundx-3.8.2 → groundx-3.8.4}/src/groundx/types/api_key_request.py +0 -0
- {groundx-3.8.2 → groundx-3.8.4}/src/groundx/types/api_key_response.py +0 -0
- {groundx-3.8.2 → groundx-3.8.4}/src/groundx/types/bounding_box_detail.py +0 -0
- {groundx-3.8.2 → groundx-3.8.4}/src/groundx/types/bucket_detail.py +0 -0
- {groundx-3.8.2 → groundx-3.8.4}/src/groundx/types/bucket_list_response.py +0 -0
- {groundx-3.8.2 → groundx-3.8.4}/src/groundx/types/bucket_response.py +0 -0
- {groundx-3.8.2 → groundx-3.8.4}/src/groundx/types/bucket_update_detail.py +0 -0
- {groundx-3.8.2 → groundx-3.8.4}/src/groundx/types/bucket_update_response.py +0 -0
- {groundx-3.8.2 → groundx-3.8.4}/src/groundx/types/custom_workflow_leaf_field.py +0 -0
- {groundx-3.8.2 → groundx-3.8.4}/src/groundx/types/custom_workflow_leaf_field_level.py +0 -0
- {groundx-3.8.2 → groundx-3.8.4}/src/groundx/types/custom_workflow_leaf_field_repetition_scope.py +0 -0
- {groundx-3.8.2 → groundx-3.8.4}/src/groundx/types/custom_workflow_output_map.py +0 -0
- {groundx-3.8.2 → groundx-3.8.4}/src/groundx/types/custom_workflow_output_route.py +0 -0
- {groundx-3.8.2 → groundx-3.8.4}/src/groundx/types/custom_workflow_output_route_level.py +0 -0
- {groundx-3.8.2 → groundx-3.8.4}/src/groundx/types/custom_workflow_output_route_output_map.py +0 -0
- {groundx-3.8.2 → groundx-3.8.4}/src/groundx/types/custom_workflow_step.py +0 -0
- {groundx-3.8.2 → groundx-3.8.4}/src/groundx/types/custom_workflow_step_config.py +0 -0
- {groundx-3.8.2 → groundx-3.8.4}/src/groundx/types/custom_workflow_step_element_config.py +0 -0
- {groundx-3.8.2 → groundx-3.8.4}/src/groundx/types/custom_workflow_step_kind.py +0 -0
- {groundx-3.8.2 → groundx-3.8.4}/src/groundx/types/custom_workflow_step_level.py +0 -0
- {groundx-3.8.2 → groundx-3.8.4}/src/groundx/types/customer_detail.py +0 -0
- {groundx-3.8.2 → groundx-3.8.4}/src/groundx/types/customer_detail_status.py +0 -0
- {groundx-3.8.2 → groundx-3.8.4}/src/groundx/types/customer_response.py +0 -0
- {groundx-3.8.2 → groundx-3.8.4}/src/groundx/types/document.py +0 -0
- {groundx-3.8.2 → groundx-3.8.4}/src/groundx/types/document_detail.py +0 -0
- {groundx-3.8.2 → groundx-3.8.4}/src/groundx/types/document_list_response.py +0 -0
- {groundx-3.8.2 → groundx-3.8.4}/src/groundx/types/document_local_ingest_request.py +0 -0
- {groundx-3.8.2 → groundx-3.8.4}/src/groundx/types/document_lookup_response.py +0 -0
- {groundx-3.8.2 → groundx-3.8.4}/src/groundx/types/document_response.py +0 -0
- {groundx-3.8.2 → groundx-3.8.4}/src/groundx/types/document_type.py +0 -0
- {groundx-3.8.2 → groundx-3.8.4}/src/groundx/types/document_update.py +0 -0
- {groundx-3.8.2 → groundx-3.8.4}/src/groundx/types/document_xray.py +0 -0
- {groundx-3.8.2 → groundx-3.8.4}/src/groundx/types/document_xray_chunk.py +0 -0
- {groundx-3.8.2 → groundx-3.8.4}/src/groundx/types/document_xray_page.py +0 -0
- {groundx-3.8.2 → groundx-3.8.4}/src/groundx/types/group_detail.py +0 -0
- {groundx-3.8.2 → groundx-3.8.4}/src/groundx/types/group_list_response.py +0 -0
- {groundx-3.8.2 → groundx-3.8.4}/src/groundx/types/group_response.py +0 -0
- {groundx-3.8.2 → groundx-3.8.4}/src/groundx/types/health_response.py +0 -0
- {groundx-3.8.2 → groundx-3.8.4}/src/groundx/types/health_response_health.py +0 -0
- {groundx-3.8.2 → groundx-3.8.4}/src/groundx/types/health_service.py +0 -0
- {groundx-3.8.2 → groundx-3.8.4}/src/groundx/types/health_service_status.py +0 -0
- {groundx-3.8.2 → groundx-3.8.4}/src/groundx/types/ingest_local_document.py +0 -0
- {groundx-3.8.2 → groundx-3.8.4}/src/groundx/types/ingest_local_document_metadata.py +0 -0
- {groundx-3.8.2 → groundx-3.8.4}/src/groundx/types/ingest_remote_document.py +0 -0
- {groundx-3.8.2 → groundx-3.8.4}/src/groundx/types/ingest_response.py +0 -0
- {groundx-3.8.2 → groundx-3.8.4}/src/groundx/types/ingest_status.py +0 -0
- {groundx-3.8.2 → groundx-3.8.4}/src/groundx/types/ingest_status_light.py +0 -0
- {groundx-3.8.2 → groundx-3.8.4}/src/groundx/types/ingest_status_progress.py +0 -0
- {groundx-3.8.2 → groundx-3.8.4}/src/groundx/types/ingest_status_progress_cancelled.py +0 -0
- {groundx-3.8.2 → groundx-3.8.4}/src/groundx/types/ingest_status_progress_complete.py +0 -0
- {groundx-3.8.2 → groundx-3.8.4}/src/groundx/types/ingest_status_progress_errors.py +0 -0
- {groundx-3.8.2 → groundx-3.8.4}/src/groundx/types/ingest_status_progress_processing.py +0 -0
- {groundx-3.8.2 → groundx-3.8.4}/src/groundx/types/ingest_status_progress_queued.py +0 -0
- {groundx-3.8.2 → groundx-3.8.4}/src/groundx/types/message_response.py +0 -0
- {groundx-3.8.2 → groundx-3.8.4}/src/groundx/types/meter_detail.py +0 -0
- {groundx-3.8.2 → groundx-3.8.4}/src/groundx/types/process_level.py +0 -0
- {groundx-3.8.2 → groundx-3.8.4}/src/groundx/types/processes_status_response.py +0 -0
- {groundx-3.8.2 → groundx-3.8.4}/src/groundx/types/processing_status.py +0 -0
- {groundx-3.8.2 → groundx-3.8.4}/src/groundx/types/search_response.py +0 -0
- {groundx-3.8.2 → groundx-3.8.4}/src/groundx/types/search_response_search.py +0 -0
- {groundx-3.8.2 → groundx-3.8.4}/src/groundx/types/search_result_item.py +0 -0
- {groundx-3.8.2 → groundx-3.8.4}/src/groundx/types/search_result_item_pages_item.py +0 -0
- {groundx-3.8.2 → groundx-3.8.4}/src/groundx/types/sort.py +0 -0
- {groundx-3.8.2 → groundx-3.8.4}/src/groundx/types/sort_order.py +0 -0
- {groundx-3.8.2 → groundx-3.8.4}/src/groundx/types/subscription_detail.py +0 -0
- {groundx-3.8.2 → groundx-3.8.4}/src/groundx/types/subscription_detail_meters.py +0 -0
- {groundx-3.8.2 → groundx-3.8.4}/src/groundx/types/website_source.py +0 -0
- {groundx-3.8.2 → groundx-3.8.4}/src/groundx/types/workflow_apply_request.py +0 -0
- {groundx-3.8.2 → groundx-3.8.4}/src/groundx/types/workflow_detail.py +0 -0
- {groundx-3.8.2 → groundx-3.8.4}/src/groundx/types/workflow_detail_chunk_strategy.py +0 -0
- {groundx-3.8.2 → groundx-3.8.4}/src/groundx/types/workflow_detail_relationships.py +0 -0
- {groundx-3.8.2 → groundx-3.8.4}/src/groundx/types/workflow_detail_section_strategy.py +0 -0
- {groundx-3.8.2 → groundx-3.8.4}/src/groundx/types/workflow_engine.py +0 -0
- {groundx-3.8.2 → groundx-3.8.4}/src/groundx/types/workflow_engine_reasoning_effort.py +0 -0
- {groundx-3.8.2 → groundx-3.8.4}/src/groundx/types/workflow_engine_service.py +0 -0
- {groundx-3.8.2 → groundx-3.8.4}/src/groundx/types/workflow_prompt.py +0 -0
- {groundx-3.8.2 → groundx-3.8.4}/src/groundx/types/workflow_prompt_group.py +0 -0
- {groundx-3.8.2 → groundx-3.8.4}/src/groundx/types/workflow_prompt_role.py +0 -0
- {groundx-3.8.2 → groundx-3.8.4}/src/groundx/types/workflow_request.py +0 -0
- {groundx-3.8.2 → groundx-3.8.4}/src/groundx/types/workflow_request_chunk_strategy.py +0 -0
- {groundx-3.8.2 → groundx-3.8.4}/src/groundx/types/workflow_request_section_strategy.py +0 -0
- {groundx-3.8.2 → groundx-3.8.4}/src/groundx/types/workflow_response.py +0 -0
- {groundx-3.8.2 → groundx-3.8.4}/src/groundx/types/workflow_step.py +0 -0
- {groundx-3.8.2 → groundx-3.8.4}/src/groundx/types/workflow_step_config.py +0 -0
- {groundx-3.8.2 → groundx-3.8.4}/src/groundx/types/workflow_step_config_field.py +0 -0
- {groundx-3.8.2 → groundx-3.8.4}/src/groundx/types/workflow_steps.py +0 -0
- {groundx-3.8.2 → groundx-3.8.4}/src/groundx/types/workflow_template.py +0 -0
- {groundx-3.8.2 → groundx-3.8.4}/src/groundx/types/workflows_response.py +0 -0
- {groundx-3.8.2 → groundx-3.8.4}/src/groundx/version.py +0 -0
- {groundx-3.8.2 → groundx-3.8.4}/src/groundx/workflows/__init__.py +0 -0
- {groundx-3.8.2 → groundx-3.8.4}/src/groundx/workflows/client.py +0 -0
- {groundx-3.8.2 → groundx-3.8.4}/src/groundx/workflows/raw_client.py +0 -0
- {groundx-3.8.2 → groundx-3.8.4}/src/groundx/workflows/types/__init__.py +0 -0
- {groundx-3.8.2 → groundx-3.8.4}/src/groundx/workflows/types/workflows_get_request_format.py +0 -0
- {groundx-3.8.2 → groundx-3.8.4}/src/groundx/workflows/types/workflows_get_request_id.py +0 -0
|
@@ -27,12 +27,12 @@ class BaseClientWrapper:
|
|
|
27
27
|
import platform
|
|
28
28
|
|
|
29
29
|
headers: typing.Dict[str, str] = {
|
|
30
|
-
"User-Agent": "groundx/3.8.
|
|
30
|
+
"User-Agent": "groundx/3.8.4",
|
|
31
31
|
"X-Fern-Language": "Python",
|
|
32
32
|
"X-Fern-Runtime": f"python/{platform.python_version()}",
|
|
33
33
|
"X-Fern-Platform": f"{platform.system().lower()}/{platform.release()}",
|
|
34
34
|
"X-Fern-SDK-Name": "groundx",
|
|
35
|
-
"X-Fern-SDK-Version": "3.8.
|
|
35
|
+
"X-Fern-SDK-Version": "3.8.4",
|
|
36
36
|
**(self.get_custom_headers() or {}),
|
|
37
37
|
}
|
|
38
38
|
headers["X-API-Key"] = self.api_key
|
|
@@ -1,20 +1,19 @@
|
|
|
1
1
|
import json
|
|
2
2
|
import os
|
|
3
3
|
import shutil
|
|
4
|
-
import time
|
|
5
4
|
import typing
|
|
6
5
|
from datetime import datetime, timezone
|
|
7
6
|
from io import BytesIO
|
|
8
7
|
from pathlib import Path
|
|
9
8
|
from urllib.parse import urlparse
|
|
10
9
|
|
|
11
|
-
import requests
|
|
12
10
|
from ..custom_outputs import (
|
|
13
11
|
custom_output_payload_identity,
|
|
14
12
|
custom_output_route_values,
|
|
15
13
|
custom_output_section_identity,
|
|
16
14
|
)
|
|
17
15
|
from ..prompt.manager import PromptManager
|
|
16
|
+
from ..services.http import BoundedRequestTimeout, bounded_get
|
|
18
17
|
from ..services.logger import Logger
|
|
19
18
|
from ..services.upload import Upload
|
|
20
19
|
from ..utility import clean_json
|
|
@@ -976,23 +975,26 @@ class DocumentRequest(BaseModel):
|
|
|
976
975
|
|
|
977
976
|
try:
|
|
978
977
|
self.print("WARN", f"[{attempt}] downloading [{page}]")
|
|
979
|
-
resp =
|
|
978
|
+
resp = bounded_get(
|
|
979
|
+
page,
|
|
980
|
+
operation="page image",
|
|
981
|
+
sleep_between_attempts=should_sleep,
|
|
982
|
+
)
|
|
980
983
|
resp.raise_for_status()
|
|
981
984
|
img = Image.open(BytesIO(resp.content))
|
|
982
985
|
if img:
|
|
983
986
|
self.page_image_dict[page] = len(self.page_images)
|
|
984
987
|
self.page_images.append(img)
|
|
985
988
|
pageImages.append(img)
|
|
989
|
+
except BoundedRequestTimeout as e:
|
|
990
|
+
self.print(
|
|
991
|
+
"ERROR", f"[{attempt}] Failed to load image from {page}: {e}"
|
|
992
|
+
)
|
|
993
|
+
raise RuntimeError(str(e)) from e
|
|
986
994
|
except Exception as e:
|
|
987
995
|
self.print(
|
|
988
996
|
"ERROR", f"[{attempt}] Failed to load image from {page}: {e}"
|
|
989
997
|
)
|
|
990
|
-
if attempt < 2:
|
|
991
|
-
if should_sleep:
|
|
992
|
-
time.sleep(2 * attempt + 1)
|
|
993
|
-
return self.load_images(
|
|
994
|
-
imgs, upload, attempt + 1, should_sleep=should_sleep
|
|
995
|
-
)
|
|
996
998
|
|
|
997
999
|
return pageImages
|
|
998
1000
|
|
|
@@ -1,11 +1,13 @@
|
|
|
1
|
-
import json
|
|
1
|
+
import json
|
|
2
|
+
import typing
|
|
2
3
|
from pathlib import Path
|
|
3
4
|
|
|
5
|
+
import requests
|
|
6
|
+
from ..services.http import bounded_get
|
|
7
|
+
from ..services.upload import Upload
|
|
4
8
|
from pydantic import BaseModel, ConfigDict, Field, field_validator
|
|
5
9
|
from typing_extensions import Annotated
|
|
6
10
|
|
|
7
|
-
from ..services.upload import Upload
|
|
8
|
-
|
|
9
11
|
|
|
10
12
|
class GroundXDocument(BaseModel):
|
|
11
13
|
model_config = ConfigDict(populate_by_name=True)
|
|
@@ -195,10 +197,12 @@ class XRayDocument(BaseModel):
|
|
|
195
197
|
|
|
196
198
|
url = gx_doc.xray_url(base=base)
|
|
197
199
|
try:
|
|
198
|
-
resp =
|
|
200
|
+
resp = bounded_get(url, operation="X-ray JSON")
|
|
199
201
|
resp.raise_for_status()
|
|
200
202
|
except requests.RequestException as e:
|
|
201
203
|
raise RuntimeError(f"Error fetching X-ray JSON from {url}: {e}")
|
|
204
|
+
except RuntimeError as e:
|
|
205
|
+
raise RuntimeError(f"Error fetching X-ray JSON from {url}: {e}") from e
|
|
202
206
|
|
|
203
207
|
try:
|
|
204
208
|
payload = resp.json()
|
|
@@ -58,8 +58,26 @@ class _RouteContainer:
|
|
|
58
58
|
page_numbers: typing.Tuple[int, ...] = ()
|
|
59
59
|
|
|
60
60
|
|
|
61
|
+
@dataclasses.dataclass(frozen=True)
|
|
62
|
+
class _ScalarCandidate:
|
|
63
|
+
value: typing.Any
|
|
64
|
+
quality: typing.Tuple[int, int, float]
|
|
65
|
+
|
|
66
|
+
|
|
61
67
|
_REPEATED_STEP_KINDS = {"keys", "summary"}
|
|
62
68
|
_EXTRACTED_FIELD_VALUE_KEYS = {"value", "confidence", "conflicts", "qa"}
|
|
69
|
+
_DEFAULT_CANDIDATE_VALUES = {
|
|
70
|
+
"n/a",
|
|
71
|
+
"na",
|
|
72
|
+
"none",
|
|
73
|
+
"not applicable",
|
|
74
|
+
"not found",
|
|
75
|
+
"not provided",
|
|
76
|
+
"not specified",
|
|
77
|
+
"not stated",
|
|
78
|
+
"null",
|
|
79
|
+
"unknown",
|
|
80
|
+
}
|
|
63
81
|
_GET_ALIASES = {
|
|
64
82
|
"chunkId": ("chunk_id",),
|
|
65
83
|
"customChunkOutputs": ("custom_chunk_outputs",),
|
|
@@ -94,10 +112,12 @@ def reassemble_custom_outputs_from_xray(
|
|
|
94
112
|
typing.Tuple[typing.Tuple[str, ...], typing.Tuple[typing.Any, ...]],
|
|
95
113
|
typing.Dict[str, typing.Any],
|
|
96
114
|
] = {}
|
|
115
|
+
scalar_candidates: typing.Dict[str, _ScalarCandidate] = {}
|
|
97
116
|
workflow_repeated_records: typing.Dict[
|
|
98
117
|
typing.Tuple[str, typing.Tuple[typing.Any, ...]],
|
|
99
118
|
typing.Dict[str, typing.Any],
|
|
100
119
|
] = {}
|
|
120
|
+
diagnostics: typing.List[CustomOutputDiagnostic] = []
|
|
101
121
|
|
|
102
122
|
for route_index, route in enumerate(routes):
|
|
103
123
|
route_hits[route_index] = False
|
|
@@ -143,14 +163,20 @@ def reassemble_custom_outputs_from_xray(
|
|
|
143
163
|
copy.deepcopy(route_value.value),
|
|
144
164
|
repeated_records=repeated_records,
|
|
145
165
|
record_key=record_key,
|
|
166
|
+
route=route_map,
|
|
167
|
+
page_numbers=route_container.page_numbers,
|
|
168
|
+
scalar_candidates=scalar_candidates,
|
|
169
|
+
diagnostics=diagnostics,
|
|
146
170
|
)
|
|
147
171
|
|
|
148
172
|
relationships = workflow.get("output_relationships") if workflow else None
|
|
149
173
|
relationship_output = None
|
|
150
|
-
diagnostics
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
174
|
+
diagnostics.extend(
|
|
175
|
+
_missing_required_route_diagnostics(
|
|
176
|
+
typing.cast(typing.Sequence[typing.Any], routes),
|
|
177
|
+
route_hits,
|
|
178
|
+
workflow_extract,
|
|
179
|
+
)
|
|
154
180
|
)
|
|
155
181
|
if isinstance(relationships, list) and relationships:
|
|
156
182
|
relationship_output, diagnostics = _apply_relationships(
|
|
@@ -159,6 +185,13 @@ def reassemble_custom_outputs_from_xray(
|
|
|
159
185
|
diagnostics=diagnostics,
|
|
160
186
|
)
|
|
161
187
|
final_output = copy.deepcopy(relationship_output)
|
|
188
|
+
else:
|
|
189
|
+
diagnostics.extend(
|
|
190
|
+
_missing_relationship_metadata_diagnostics(
|
|
191
|
+
final_output,
|
|
192
|
+
workflow_extract,
|
|
193
|
+
)
|
|
194
|
+
)
|
|
162
195
|
|
|
163
196
|
return CustomOutputReassemblyResult(
|
|
164
197
|
final_output=final_output,
|
|
@@ -480,6 +513,56 @@ def _is_empty_output(value: typing.Any) -> bool:
|
|
|
480
513
|
return False
|
|
481
514
|
|
|
482
515
|
|
|
516
|
+
def _scalar_candidate(
|
|
517
|
+
value: typing.Any,
|
|
518
|
+
page_numbers: typing.Tuple[int, ...],
|
|
519
|
+
) -> _ScalarCandidate:
|
|
520
|
+
return _ScalarCandidate(
|
|
521
|
+
value=value,
|
|
522
|
+
quality=_scalar_candidate_quality(value, page_numbers),
|
|
523
|
+
)
|
|
524
|
+
|
|
525
|
+
|
|
526
|
+
def _scalar_candidate_quality(
|
|
527
|
+
value: typing.Any,
|
|
528
|
+
page_numbers: typing.Tuple[int, ...],
|
|
529
|
+
) -> typing.Tuple[int, int, float]:
|
|
530
|
+
normalized = _normalized_candidate_value(value)
|
|
531
|
+
is_specific = 0 if _is_default_candidate_value(normalized) else 1
|
|
532
|
+
has_source_pages = 1 if page_numbers else 0
|
|
533
|
+
return (is_specific, has_source_pages, _candidate_confidence(value))
|
|
534
|
+
|
|
535
|
+
|
|
536
|
+
def _normalized_candidate_value(value: typing.Any) -> typing.Any:
|
|
537
|
+
unwrapped = _unwrap_match_value(value)
|
|
538
|
+
if isinstance(unwrapped, str):
|
|
539
|
+
return " ".join(unwrapped.strip().casefold().split())
|
|
540
|
+
if isinstance(unwrapped, numbers.Real):
|
|
541
|
+
return float(unwrapped)
|
|
542
|
+
return unwrapped
|
|
543
|
+
|
|
544
|
+
|
|
545
|
+
def _is_default_candidate_value(value: typing.Any) -> bool:
|
|
546
|
+
if value is None:
|
|
547
|
+
return True
|
|
548
|
+
if isinstance(value, str):
|
|
549
|
+
return value in _DEFAULT_CANDIDATE_VALUES
|
|
550
|
+
return False
|
|
551
|
+
|
|
552
|
+
|
|
553
|
+
def _candidate_confidence(value: typing.Any) -> float:
|
|
554
|
+
if not isinstance(value, typing.Mapping):
|
|
555
|
+
return 0.0
|
|
556
|
+
confidence = value.get("confidence")
|
|
557
|
+
if not isinstance(confidence, numbers.Real):
|
|
558
|
+
return 0.0
|
|
559
|
+
return max(0.0, min(1.0, float(confidence)))
|
|
560
|
+
|
|
561
|
+
|
|
562
|
+
def _string_value(value: typing.Any) -> typing.Optional[str]:
|
|
563
|
+
return value if isinstance(value, str) else None
|
|
564
|
+
|
|
565
|
+
|
|
483
566
|
def _effective_pointer(pointer: str, *, repeated: bool) -> str:
|
|
484
567
|
if not repeated or "*" in pointer:
|
|
485
568
|
return pointer
|
|
@@ -499,6 +582,10 @@ def _set_pointer(
|
|
|
499
582
|
typing.Dict[str, typing.Any],
|
|
500
583
|
],
|
|
501
584
|
record_key: typing.Tuple[typing.Any, ...],
|
|
585
|
+
route: typing.Mapping[str, typing.Any],
|
|
586
|
+
page_numbers: typing.Tuple[int, ...],
|
|
587
|
+
scalar_candidates: typing.Dict[str, _ScalarCandidate],
|
|
588
|
+
diagnostics: typing.List[CustomOutputDiagnostic],
|
|
502
589
|
) -> None:
|
|
503
590
|
parts = _pointer_parts(pointer)
|
|
504
591
|
if not parts:
|
|
@@ -538,7 +625,28 @@ def _set_pointer(
|
|
|
538
625
|
if not isinstance(next_value, dict):
|
|
539
626
|
return
|
|
540
627
|
current = next_value
|
|
628
|
+
candidate = _scalar_candidate(value, page_numbers)
|
|
629
|
+
existing = scalar_candidates.get(pointer)
|
|
630
|
+
if existing is not None:
|
|
631
|
+
if candidate.quality < existing.quality:
|
|
632
|
+
return
|
|
633
|
+
if candidate.quality == existing.quality:
|
|
634
|
+
if _normalized_candidate_value(candidate.value) != _normalized_candidate_value(
|
|
635
|
+
existing.value
|
|
636
|
+
):
|
|
637
|
+
diagnostics.append(
|
|
638
|
+
CustomOutputDiagnostic(
|
|
639
|
+
code="conflicting_output_candidates",
|
|
640
|
+
message=f"multiple equal-quality candidates for [{pointer}]",
|
|
641
|
+
severity="warning",
|
|
642
|
+
workflow_group=_string_value(route.get("workflow_group")),
|
|
643
|
+
workflow_field=_string_value(route.get("workflow_field")),
|
|
644
|
+
final_path=pointer,
|
|
645
|
+
)
|
|
646
|
+
)
|
|
647
|
+
return
|
|
541
648
|
current[parts[-1]] = value
|
|
649
|
+
scalar_candidates[pointer] = candidate
|
|
542
650
|
|
|
543
651
|
|
|
544
652
|
def _set_workflow_value(
|
|
@@ -670,6 +778,80 @@ def _missing_required_route_diagnostics(
|
|
|
670
778
|
return diagnostics
|
|
671
779
|
|
|
672
780
|
|
|
781
|
+
def _missing_relationship_metadata_diagnostics(
|
|
782
|
+
final_output: typing.Mapping[str, typing.Any],
|
|
783
|
+
workflow_extract: typing.Optional[typing.Mapping[str, typing.Any]],
|
|
784
|
+
) -> typing.List[CustomOutputDiagnostic]:
|
|
785
|
+
if not isinstance(workflow_extract, typing.Mapping):
|
|
786
|
+
return []
|
|
787
|
+
|
|
788
|
+
diagnostics: typing.List[CustomOutputDiagnostic] = []
|
|
789
|
+
for group_name, group_spec in _relationship_intent_group_specs(workflow_extract):
|
|
790
|
+
if not isinstance(group_spec, typing.Mapping):
|
|
791
|
+
continue
|
|
792
|
+
if not _group_requires_relationship_metadata(group_spec):
|
|
793
|
+
continue
|
|
794
|
+
group_output = final_output.get(group_name)
|
|
795
|
+
if not isinstance(group_output, list) or len(group_output) == 0:
|
|
796
|
+
continue
|
|
797
|
+
diagnostics.append(
|
|
798
|
+
CustomOutputDiagnostic(
|
|
799
|
+
code="missing_output_relationships",
|
|
800
|
+
message=(
|
|
801
|
+
f"workflow group [{group_name}] requires output relationship "
|
|
802
|
+
"metadata but no output_relationships were provided"
|
|
803
|
+
),
|
|
804
|
+
workflow_group=group_name,
|
|
805
|
+
)
|
|
806
|
+
)
|
|
807
|
+
return diagnostics
|
|
808
|
+
|
|
809
|
+
|
|
810
|
+
def _relationship_intent_group_specs(
|
|
811
|
+
workflow_extract: typing.Mapping[str, typing.Any],
|
|
812
|
+
) -> typing.Iterator[typing.Tuple[str, typing.Any]]:
|
|
813
|
+
seen: typing.Set[str] = set()
|
|
814
|
+
for container_key in (
|
|
815
|
+
"_groundx_persisted_extract",
|
|
816
|
+
"groups",
|
|
817
|
+
"prepared_final_groups",
|
|
818
|
+
):
|
|
819
|
+
container = workflow_extract.get(container_key)
|
|
820
|
+
if not isinstance(container, typing.Mapping):
|
|
821
|
+
continue
|
|
822
|
+
for group_name, group_spec in container.items():
|
|
823
|
+
if isinstance(group_name, str) and group_name not in seen:
|
|
824
|
+
seen.add(group_name)
|
|
825
|
+
yield group_name, group_spec
|
|
826
|
+
|
|
827
|
+
for group_name, group_spec in workflow_extract.items():
|
|
828
|
+
if group_name in {
|
|
829
|
+
"_groundx_persisted_extract",
|
|
830
|
+
"groups",
|
|
831
|
+
"prepared_final_groups",
|
|
832
|
+
"workflow",
|
|
833
|
+
}:
|
|
834
|
+
continue
|
|
835
|
+
if isinstance(group_name, str) and group_name not in seen:
|
|
836
|
+
seen.add(group_name)
|
|
837
|
+
yield group_name, group_spec
|
|
838
|
+
|
|
839
|
+
|
|
840
|
+
def _group_requires_relationship_metadata(
|
|
841
|
+
group_spec: typing.Mapping[str, typing.Any],
|
|
842
|
+
) -> bool:
|
|
843
|
+
match_attrs = group_spec.get("match_attrs")
|
|
844
|
+
if isinstance(match_attrs, list) and len(match_attrs) > 0:
|
|
845
|
+
return True
|
|
846
|
+
passthrough = group_spec.get("passthrough")
|
|
847
|
+
if isinstance(passthrough, typing.Mapping) and isinstance(
|
|
848
|
+
passthrough.get("from"),
|
|
849
|
+
str,
|
|
850
|
+
):
|
|
851
|
+
return True
|
|
852
|
+
return False
|
|
853
|
+
|
|
854
|
+
|
|
673
855
|
def _route_is_required(
|
|
674
856
|
route: typing.Mapping[str, typing.Any],
|
|
675
857
|
workflow_extract: typing.Mapping[str, typing.Any],
|
|
@@ -834,7 +1016,11 @@ def _apply_relationships(
|
|
|
834
1016
|
)
|
|
835
1017
|
continue
|
|
836
1018
|
|
|
837
|
-
parent_list =
|
|
1019
|
+
parent_list = _dedupe_relationship_parents(
|
|
1020
|
+
typing.cast(typing.List[typing.Dict[str, typing.Any]], parent_records),
|
|
1021
|
+
typing.cast(typing.List[str], match_attrs),
|
|
1022
|
+
)
|
|
1023
|
+
result[parent_group] = parent_list
|
|
838
1024
|
child_list = typing.cast(typing.List[typing.Dict[str, typing.Any]], child_records)
|
|
839
1025
|
for parent in parent_list:
|
|
840
1026
|
parent.setdefault(parent_output_field, [])
|
|
@@ -883,6 +1069,50 @@ def _apply_relationships(
|
|
|
883
1069
|
return result, diagnostics
|
|
884
1070
|
|
|
885
1071
|
|
|
1072
|
+
def _dedupe_relationship_parents(
|
|
1073
|
+
parent_list: typing.List[typing.Dict[str, typing.Any]],
|
|
1074
|
+
match_attrs: typing.Sequence[str],
|
|
1075
|
+
) -> typing.List[typing.Dict[str, typing.Any]]:
|
|
1076
|
+
deduped: typing.List[typing.Dict[str, typing.Any]] = []
|
|
1077
|
+
by_key: typing.Dict[
|
|
1078
|
+
typing.Tuple[typing.Tuple[str, typing.Any], ...],
|
|
1079
|
+
typing.Dict[str, typing.Any],
|
|
1080
|
+
] = {}
|
|
1081
|
+
for parent in parent_list:
|
|
1082
|
+
parent_key = _match_key(parent, match_attrs)
|
|
1083
|
+
if not parent_key:
|
|
1084
|
+
deduped.append(parent)
|
|
1085
|
+
continue
|
|
1086
|
+
existing = by_key.get(parent_key)
|
|
1087
|
+
if existing is None:
|
|
1088
|
+
by_key[parent_key] = parent
|
|
1089
|
+
deduped.append(parent)
|
|
1090
|
+
continue
|
|
1091
|
+
_merge_relationship_parent(existing, parent)
|
|
1092
|
+
return deduped
|
|
1093
|
+
|
|
1094
|
+
|
|
1095
|
+
def _merge_relationship_parent(
|
|
1096
|
+
target: typing.Dict[str, typing.Any],
|
|
1097
|
+
source: typing.Mapping[str, typing.Any],
|
|
1098
|
+
) -> None:
|
|
1099
|
+
for key, value in source.items():
|
|
1100
|
+
if key not in target or _match_value_absent(target.get(key)):
|
|
1101
|
+
target[key] = copy.deepcopy(value)
|
|
1102
|
+
continue
|
|
1103
|
+
target_value = target.get(key)
|
|
1104
|
+
if isinstance(target_value, dict) and isinstance(value, typing.Mapping):
|
|
1105
|
+
_merge_relationship_parent(
|
|
1106
|
+
target_value,
|
|
1107
|
+
typing.cast(typing.Mapping[str, typing.Any], value),
|
|
1108
|
+
)
|
|
1109
|
+
continue
|
|
1110
|
+
if isinstance(target_value, list) and isinstance(value, list):
|
|
1111
|
+
for item in value:
|
|
1112
|
+
if item not in target_value:
|
|
1113
|
+
target_value.append(copy.deepcopy(item))
|
|
1114
|
+
|
|
1115
|
+
|
|
886
1116
|
def _relationship_application_order(
|
|
887
1117
|
relationships: typing.Sequence[typing.Any],
|
|
888
1118
|
) -> typing.List[typing.Any]:
|
|
@@ -1628,6 +1628,7 @@ def _collect_custom_workflow_routes(
|
|
|
1628
1628
|
step_name: typing.Optional[str],
|
|
1629
1629
|
steps_by_name: typing.Dict[str, typing.Dict[str, typing.Any]],
|
|
1630
1630
|
prefix: typing.Tuple[str, ...] = (),
|
|
1631
|
+
document_root: bool = False,
|
|
1631
1632
|
) -> typing.Tuple[
|
|
1632
1633
|
typing.List[typing.Dict[str, typing.Any]],
|
|
1633
1634
|
typing.List[typing.Dict[str, typing.Any]],
|
|
@@ -1655,6 +1656,7 @@ def _collect_custom_workflow_routes(
|
|
|
1655
1656
|
step_name,
|
|
1656
1657
|
steps_by_name,
|
|
1657
1658
|
(*prefix, field_name, "*"),
|
|
1659
|
+
document_root,
|
|
1658
1660
|
)
|
|
1659
1661
|
routes.extend(nested_routes)
|
|
1660
1662
|
leaves.extend(nested_leaves)
|
|
@@ -1683,7 +1685,11 @@ def _collect_custom_workflow_routes(
|
|
|
1683
1685
|
output_key_raw,
|
|
1684
1686
|
f"{field_path}.{_CUSTOM_WORKFLOW_FIELD_METADATA_KEY}",
|
|
1685
1687
|
)
|
|
1686
|
-
segments = (
|
|
1688
|
+
segments = (
|
|
1689
|
+
(*prefix, field_name)
|
|
1690
|
+
if document_root
|
|
1691
|
+
else (group_name, *prefix, field_name)
|
|
1692
|
+
)
|
|
1687
1693
|
final_path = _encode_pointer(segments)
|
|
1688
1694
|
level = typing.cast(str, step["level"])
|
|
1689
1695
|
workflow_field = _custom_workflow_field_name(prefix, field_name)
|
|
@@ -1723,6 +1729,7 @@ def _collect_custom_workflow_routes(
|
|
|
1723
1729
|
step_name,
|
|
1724
1730
|
steps_by_name,
|
|
1725
1731
|
(*prefix, field_name),
|
|
1732
|
+
document_root,
|
|
1726
1733
|
)
|
|
1727
1734
|
routes.extend(nested_routes)
|
|
1728
1735
|
leaves.extend(nested_leaves)
|
|
@@ -1733,6 +1740,7 @@ def _collect_custom_workflow_routes(
|
|
|
1733
1740
|
step_name,
|
|
1734
1741
|
steps_by_name,
|
|
1735
1742
|
(*prefix, field_name),
|
|
1743
|
+
document_root,
|
|
1736
1744
|
)
|
|
1737
1745
|
routes.extend(nested_routes)
|
|
1738
1746
|
leaves.extend(nested_leaves)
|
|
@@ -1740,6 +1748,46 @@ def _collect_custom_workflow_routes(
|
|
|
1740
1748
|
return routes, leaves
|
|
1741
1749
|
|
|
1742
1750
|
|
|
1751
|
+
def _agent_chain_group_roles(raw_chain: typing.Any) -> typing.Dict[str, str]:
|
|
1752
|
+
roles: typing.Dict[str, str] = {}
|
|
1753
|
+
if not isinstance(raw_chain, list):
|
|
1754
|
+
return roles
|
|
1755
|
+
|
|
1756
|
+
for raw_stage in raw_chain:
|
|
1757
|
+
if not isinstance(raw_stage, dict) or set(raw_stage.keys()) != {"parallel"}:
|
|
1758
|
+
continue
|
|
1759
|
+
raw_branches = raw_stage["parallel"]
|
|
1760
|
+
if not isinstance(raw_branches, list):
|
|
1761
|
+
continue
|
|
1762
|
+
for raw_branch in raw_branches:
|
|
1763
|
+
if not isinstance(raw_branch, dict):
|
|
1764
|
+
continue
|
|
1765
|
+
group = raw_branch.get("group")
|
|
1766
|
+
chain = raw_branch.get("chain")
|
|
1767
|
+
if not isinstance(group, str) or not isinstance(chain, list):
|
|
1768
|
+
continue
|
|
1769
|
+
suffixes = {
|
|
1770
|
+
_agent_chain_task_suffix(task)
|
|
1771
|
+
for task in chain
|
|
1772
|
+
if isinstance(task, str)
|
|
1773
|
+
and task in _CUSTOM_WORKFLOW_AGENT_CHAIN_SUPPORTED_TASKS
|
|
1774
|
+
}
|
|
1775
|
+
if len(suffixes) == 1:
|
|
1776
|
+
roles[group] = next(iter(suffixes))
|
|
1777
|
+
return roles
|
|
1778
|
+
|
|
1779
|
+
|
|
1780
|
+
def _is_document_root_statement_group(
|
|
1781
|
+
group_metadata: typing.Mapping[str, typing.Any],
|
|
1782
|
+
group_role: typing.Optional[str],
|
|
1783
|
+
) -> bool:
|
|
1784
|
+
if group_role != "statement":
|
|
1785
|
+
return False
|
|
1786
|
+
return isinstance(group_metadata.get("final_value_aliases"), dict) or isinstance(
|
|
1787
|
+
group_metadata.get("fill_rules"), list
|
|
1788
|
+
)
|
|
1789
|
+
|
|
1790
|
+
|
|
1743
1791
|
def _collect_pseudo_custom_workflow_routes(
|
|
1744
1792
|
group_name: str,
|
|
1745
1793
|
group: typing.Dict[str, typing.Any],
|
|
@@ -1830,6 +1878,9 @@ def _build_authored_custom_workflow_metadata(
|
|
|
1830
1878
|
|
|
1831
1879
|
routes: typing.List[typing.Dict[str, typing.Any]] = []
|
|
1832
1880
|
leaves: typing.List[typing.Dict[str, typing.Any]] = []
|
|
1881
|
+
agent_chain_group_roles = _agent_chain_group_roles(
|
|
1882
|
+
workflow.get(_CUSTOM_WORKFLOW_AGENT_CHAIN_KEY)
|
|
1883
|
+
)
|
|
1833
1884
|
for group_name, group in workflow_groups.items():
|
|
1834
1885
|
group_step = workflow_group_metadata.get(group_name, {}).get(
|
|
1835
1886
|
_CUSTOM_WORKFLOW_GROUP_METADATA_KEY
|
|
@@ -1846,11 +1897,16 @@ def _build_authored_custom_workflow_metadata(
|
|
|
1846
1897
|
workflow_field_paths.get(group_name, {}),
|
|
1847
1898
|
)
|
|
1848
1899
|
else:
|
|
1900
|
+
document_root = _is_document_root_statement_group(
|
|
1901
|
+
final_group_metadata.get(group_name, {}),
|
|
1902
|
+
agent_chain_group_roles.get(group_name),
|
|
1903
|
+
)
|
|
1849
1904
|
group_routes, group_leaves = _collect_custom_workflow_routes(
|
|
1850
1905
|
fields,
|
|
1851
1906
|
group_name,
|
|
1852
1907
|
typing.cast(typing.Optional[str], group_step),
|
|
1853
1908
|
steps_by_name,
|
|
1909
|
+
document_root=document_root,
|
|
1854
1910
|
)
|
|
1855
1911
|
routes.extend(group_routes)
|
|
1856
1912
|
leaves.extend(group_leaves)
|