groundx 2.5.8__tar.gz → 2.7.3__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.
Files changed (155) hide show
  1. {groundx-2.5.8 → groundx-2.7.3}/PKG-INFO +1 -1
  2. {groundx-2.5.8 → groundx-2.7.3}/pyproject.toml +1 -1
  3. {groundx-2.5.8 → groundx-2.7.3}/src/groundx/__init__.py +40 -1
  4. {groundx-2.5.8 → groundx-2.7.3}/src/groundx/client.py +3 -0
  5. {groundx-2.5.8 → groundx-2.7.3}/src/groundx/core/client_wrapper.py +2 -2
  6. {groundx-2.5.8 → groundx-2.7.3}/src/groundx/extract/agents/agent.py +5 -5
  7. {groundx-2.5.8 → groundx-2.7.3}/src/groundx/extract/classes/agent.py +2 -1
  8. {groundx-2.5.8 → groundx-2.7.3}/src/groundx/extract/classes/document.py +33 -16
  9. {groundx-2.5.8 → groundx-2.7.3}/src/groundx/extract/classes/groundx.py +36 -17
  10. {groundx-2.5.8 → groundx-2.7.3}/src/groundx/extract/services/logging_cfg.py +0 -2
  11. {groundx-2.5.8 → groundx-2.7.3}/src/groundx/extract/services/upload.py +0 -3
  12. {groundx-2.5.8 → groundx-2.7.3}/src/groundx/extract/services/upload_s3.py +9 -3
  13. {groundx-2.5.8 → groundx-2.7.3}/src/groundx/extract/settings/settings.py +51 -9
  14. {groundx-2.5.8 → groundx-2.7.3}/src/groundx/extract/settings/test_settings.py +0 -3
  15. {groundx-2.5.8 → groundx-2.7.3}/src/groundx/ingest.py +100 -37
  16. {groundx-2.5.8 → groundx-2.7.3}/src/groundx/types/__init__.py +36 -0
  17. groundx-2.7.3/src/groundx/types/workflow_apply_request.py +24 -0
  18. groundx-2.7.3/src/groundx/types/workflow_detail.py +59 -0
  19. groundx-2.7.3/src/groundx/types/workflow_detail_chunk_strategy.py +5 -0
  20. groundx-2.7.3/src/groundx/types/workflow_detail_relationships.py +36 -0
  21. groundx-2.7.3/src/groundx/types/workflow_engine.py +58 -0
  22. groundx-2.7.3/src/groundx/types/workflow_engine_reasoning_effort.py +5 -0
  23. groundx-2.7.3/src/groundx/types/workflow_engine_service.py +7 -0
  24. groundx-2.7.3/src/groundx/types/workflow_prompt.py +37 -0
  25. groundx-2.7.3/src/groundx/types/workflow_prompt_group.py +25 -0
  26. groundx-2.7.3/src/groundx/types/workflow_prompt_role.py +5 -0
  27. groundx-2.7.3/src/groundx/types/workflow_request.py +31 -0
  28. groundx-2.7.3/src/groundx/types/workflow_request_chunk_strategy.py +5 -0
  29. groundx-2.7.3/src/groundx/types/workflow_response.py +20 -0
  30. groundx-2.7.3/src/groundx/types/workflow_step.py +33 -0
  31. groundx-2.7.3/src/groundx/types/workflow_step_config.py +33 -0
  32. groundx-2.7.3/src/groundx/types/workflow_step_config_field.py +8 -0
  33. groundx-2.7.3/src/groundx/types/workflow_steps.py +38 -0
  34. groundx-2.7.3/src/groundx/types/workflows_response.py +20 -0
  35. groundx-2.7.3/src/groundx/workflows/__init__.py +7 -0
  36. groundx-2.7.3/src/groundx/workflows/client.py +736 -0
  37. groundx-2.7.3/src/groundx/workflows/raw_client.py +841 -0
  38. groundx-2.7.3/src/groundx/workflows/types/__init__.py +7 -0
  39. groundx-2.7.3/src/groundx/workflows/types/workflows_get_request_id.py +5 -0
  40. {groundx-2.5.8 → groundx-2.7.3}/LICENSE +0 -0
  41. {groundx-2.5.8 → groundx-2.7.3}/README.md +0 -0
  42. {groundx-2.5.8 → groundx-2.7.3}/src/groundx/buckets/__init__.py +0 -0
  43. {groundx-2.5.8 → groundx-2.7.3}/src/groundx/buckets/client.py +0 -0
  44. {groundx-2.5.8 → groundx-2.7.3}/src/groundx/buckets/raw_client.py +0 -0
  45. {groundx-2.5.8 → groundx-2.7.3}/src/groundx/core/__init__.py +0 -0
  46. {groundx-2.5.8 → groundx-2.7.3}/src/groundx/core/api_error.py +0 -0
  47. {groundx-2.5.8 → groundx-2.7.3}/src/groundx/core/datetime_utils.py +0 -0
  48. {groundx-2.5.8 → groundx-2.7.3}/src/groundx/core/file.py +0 -0
  49. {groundx-2.5.8 → groundx-2.7.3}/src/groundx/core/force_multipart.py +0 -0
  50. {groundx-2.5.8 → groundx-2.7.3}/src/groundx/core/http_client.py +0 -0
  51. {groundx-2.5.8 → groundx-2.7.3}/src/groundx/core/http_response.py +0 -0
  52. {groundx-2.5.8 → groundx-2.7.3}/src/groundx/core/jsonable_encoder.py +0 -0
  53. {groundx-2.5.8 → groundx-2.7.3}/src/groundx/core/pydantic_utilities.py +0 -0
  54. {groundx-2.5.8 → groundx-2.7.3}/src/groundx/core/query_encoder.py +0 -0
  55. {groundx-2.5.8 → groundx-2.7.3}/src/groundx/core/remove_none_from_dict.py +0 -0
  56. {groundx-2.5.8 → groundx-2.7.3}/src/groundx/core/request_options.py +0 -0
  57. {groundx-2.5.8 → groundx-2.7.3}/src/groundx/core/serialization.py +0 -0
  58. {groundx-2.5.8 → groundx-2.7.3}/src/groundx/csv_splitter.py +0 -0
  59. {groundx-2.5.8 → groundx-2.7.3}/src/groundx/customer/__init__.py +0 -0
  60. {groundx-2.5.8 → groundx-2.7.3}/src/groundx/customer/client.py +0 -0
  61. {groundx-2.5.8 → groundx-2.7.3}/src/groundx/customer/raw_client.py +0 -0
  62. {groundx-2.5.8 → groundx-2.7.3}/src/groundx/documents/__init__.py +0 -0
  63. {groundx-2.5.8 → groundx-2.7.3}/src/groundx/documents/client.py +0 -0
  64. {groundx-2.5.8 → groundx-2.7.3}/src/groundx/documents/raw_client.py +0 -0
  65. {groundx-2.5.8 → groundx-2.7.3}/src/groundx/environment.py +0 -0
  66. {groundx-2.5.8 → groundx-2.7.3}/src/groundx/errors/__init__.py +0 -0
  67. {groundx-2.5.8 → groundx-2.7.3}/src/groundx/errors/bad_request_error.py +0 -0
  68. {groundx-2.5.8 → groundx-2.7.3}/src/groundx/errors/unauthorized_error.py +0 -0
  69. {groundx-2.5.8 → groundx-2.7.3}/src/groundx/extract/__init__.py +0 -0
  70. {groundx-2.5.8 → groundx-2.7.3}/src/groundx/extract/agents/__init__.py +0 -0
  71. {groundx-2.5.8 → groundx-2.7.3}/src/groundx/extract/classes/__init__.py +0 -0
  72. {groundx-2.5.8 → groundx-2.7.3}/src/groundx/extract/classes/api.py +0 -0
  73. {groundx-2.5.8 → groundx-2.7.3}/src/groundx/extract/classes/field.py +0 -0
  74. {groundx-2.5.8 → groundx-2.7.3}/src/groundx/extract/classes/prompt.py +0 -0
  75. {groundx-2.5.8 → groundx-2.7.3}/src/groundx/extract/classes/test_document.py +0 -0
  76. {groundx-2.5.8 → groundx-2.7.3}/src/groundx/extract/classes/test_field.py +0 -0
  77. {groundx-2.5.8 → groundx-2.7.3}/src/groundx/extract/classes/test_groundx.py +0 -0
  78. {groundx-2.5.8 → groundx-2.7.3}/src/groundx/extract/classes/test_prompt.py +0 -0
  79. {groundx-2.5.8 → groundx-2.7.3}/src/groundx/extract/post_process/__init__.py +0 -0
  80. {groundx-2.5.8 → groundx-2.7.3}/src/groundx/extract/post_process/post_process.py +0 -0
  81. {groundx-2.5.8 → groundx-2.7.3}/src/groundx/extract/services/.DS_Store +0 -0
  82. {groundx-2.5.8 → groundx-2.7.3}/src/groundx/extract/services/__init__.py +0 -0
  83. {groundx-2.5.8 → groundx-2.7.3}/src/groundx/extract/services/csv.py +0 -0
  84. {groundx-2.5.8 → groundx-2.7.3}/src/groundx/extract/services/logger.py +0 -0
  85. {groundx-2.5.8 → groundx-2.7.3}/src/groundx/extract/services/ratelimit.py +0 -0
  86. {groundx-2.5.8 → groundx-2.7.3}/src/groundx/extract/services/sheets_client.py +0 -0
  87. {groundx-2.5.8 → groundx-2.7.3}/src/groundx/extract/services/status.py +0 -0
  88. {groundx-2.5.8 → groundx-2.7.3}/src/groundx/extract/services/upload_minio.py +0 -0
  89. {groundx-2.5.8 → groundx-2.7.3}/src/groundx/extract/services/utility.py +0 -0
  90. {groundx-2.5.8 → groundx-2.7.3}/src/groundx/extract/settings/__init__.py +0 -0
  91. {groundx-2.5.8 → groundx-2.7.3}/src/groundx/extract/tasks/__init__.py +0 -0
  92. {groundx-2.5.8 → groundx-2.7.3}/src/groundx/extract/tasks/utility.py +0 -0
  93. {groundx-2.5.8 → groundx-2.7.3}/src/groundx/extract/utility/__init__.py +0 -0
  94. {groundx-2.5.8 → groundx-2.7.3}/src/groundx/extract/utility/classes.py +0 -0
  95. {groundx-2.5.8 → groundx-2.7.3}/src/groundx/extract/utility/test_utility.py +0 -0
  96. {groundx-2.5.8 → groundx-2.7.3}/src/groundx/groups/__init__.py +0 -0
  97. {groundx-2.5.8 → groundx-2.7.3}/src/groundx/groups/client.py +0 -0
  98. {groundx-2.5.8 → groundx-2.7.3}/src/groundx/groups/raw_client.py +0 -0
  99. {groundx-2.5.8 → groundx-2.7.3}/src/groundx/health/__init__.py +0 -0
  100. {groundx-2.5.8 → groundx-2.7.3}/src/groundx/health/client.py +0 -0
  101. {groundx-2.5.8 → groundx-2.7.3}/src/groundx/health/raw_client.py +0 -0
  102. {groundx-2.5.8 → groundx-2.7.3}/src/groundx/py.typed +0 -0
  103. {groundx-2.5.8 → groundx-2.7.3}/src/groundx/search/__init__.py +0 -0
  104. {groundx-2.5.8 → groundx-2.7.3}/src/groundx/search/client.py +0 -0
  105. {groundx-2.5.8 → groundx-2.7.3}/src/groundx/search/raw_client.py +0 -0
  106. {groundx-2.5.8 → groundx-2.7.3}/src/groundx/search/types/__init__.py +0 -0
  107. {groundx-2.5.8 → groundx-2.7.3}/src/groundx/search/types/search_content_request_id.py +0 -0
  108. {groundx-2.5.8 → groundx-2.7.3}/src/groundx/types/bounding_box_detail.py +0 -0
  109. {groundx-2.5.8 → groundx-2.7.3}/src/groundx/types/bucket_detail.py +0 -0
  110. {groundx-2.5.8 → groundx-2.7.3}/src/groundx/types/bucket_list_response.py +0 -0
  111. {groundx-2.5.8 → groundx-2.7.3}/src/groundx/types/bucket_response.py +0 -0
  112. {groundx-2.5.8 → groundx-2.7.3}/src/groundx/types/bucket_update_detail.py +0 -0
  113. {groundx-2.5.8 → groundx-2.7.3}/src/groundx/types/bucket_update_response.py +0 -0
  114. {groundx-2.5.8 → groundx-2.7.3}/src/groundx/types/customer_detail.py +0 -0
  115. {groundx-2.5.8 → groundx-2.7.3}/src/groundx/types/customer_response.py +0 -0
  116. {groundx-2.5.8 → groundx-2.7.3}/src/groundx/types/document.py +0 -0
  117. {groundx-2.5.8 → groundx-2.7.3}/src/groundx/types/document_detail.py +0 -0
  118. {groundx-2.5.8 → groundx-2.7.3}/src/groundx/types/document_list_response.py +0 -0
  119. {groundx-2.5.8 → groundx-2.7.3}/src/groundx/types/document_local_ingest_request.py +0 -0
  120. {groundx-2.5.8 → groundx-2.7.3}/src/groundx/types/document_lookup_response.py +0 -0
  121. {groundx-2.5.8 → groundx-2.7.3}/src/groundx/types/document_response.py +0 -0
  122. {groundx-2.5.8 → groundx-2.7.3}/src/groundx/types/document_type.py +0 -0
  123. {groundx-2.5.8 → groundx-2.7.3}/src/groundx/types/group_detail.py +0 -0
  124. {groundx-2.5.8 → groundx-2.7.3}/src/groundx/types/group_list_response.py +0 -0
  125. {groundx-2.5.8 → groundx-2.7.3}/src/groundx/types/group_response.py +0 -0
  126. {groundx-2.5.8 → groundx-2.7.3}/src/groundx/types/health_response.py +0 -0
  127. {groundx-2.5.8 → groundx-2.7.3}/src/groundx/types/health_response_health.py +0 -0
  128. {groundx-2.5.8 → groundx-2.7.3}/src/groundx/types/health_service.py +0 -0
  129. {groundx-2.5.8 → groundx-2.7.3}/src/groundx/types/health_service_status.py +0 -0
  130. {groundx-2.5.8 → groundx-2.7.3}/src/groundx/types/ingest_local_document.py +0 -0
  131. {groundx-2.5.8 → groundx-2.7.3}/src/groundx/types/ingest_local_document_metadata.py +0 -0
  132. {groundx-2.5.8 → groundx-2.7.3}/src/groundx/types/ingest_remote_document.py +0 -0
  133. {groundx-2.5.8 → groundx-2.7.3}/src/groundx/types/ingest_response.py +0 -0
  134. {groundx-2.5.8 → groundx-2.7.3}/src/groundx/types/ingest_status.py +0 -0
  135. {groundx-2.5.8 → groundx-2.7.3}/src/groundx/types/ingest_status_light.py +0 -0
  136. {groundx-2.5.8 → groundx-2.7.3}/src/groundx/types/ingest_status_progress.py +0 -0
  137. {groundx-2.5.8 → groundx-2.7.3}/src/groundx/types/ingest_status_progress_cancelled.py +0 -0
  138. {groundx-2.5.8 → groundx-2.7.3}/src/groundx/types/ingest_status_progress_complete.py +0 -0
  139. {groundx-2.5.8 → groundx-2.7.3}/src/groundx/types/ingest_status_progress_errors.py +0 -0
  140. {groundx-2.5.8 → groundx-2.7.3}/src/groundx/types/ingest_status_progress_processing.py +0 -0
  141. {groundx-2.5.8 → groundx-2.7.3}/src/groundx/types/message_response.py +0 -0
  142. {groundx-2.5.8 → groundx-2.7.3}/src/groundx/types/meter_detail.py +0 -0
  143. {groundx-2.5.8 → groundx-2.7.3}/src/groundx/types/process_level.py +0 -0
  144. {groundx-2.5.8 → groundx-2.7.3}/src/groundx/types/processes_status_response.py +0 -0
  145. {groundx-2.5.8 → groundx-2.7.3}/src/groundx/types/processing_status.py +0 -0
  146. {groundx-2.5.8 → groundx-2.7.3}/src/groundx/types/search_response.py +0 -0
  147. {groundx-2.5.8 → groundx-2.7.3}/src/groundx/types/search_response_search.py +0 -0
  148. {groundx-2.5.8 → groundx-2.7.3}/src/groundx/types/search_result_item.py +0 -0
  149. {groundx-2.5.8 → groundx-2.7.3}/src/groundx/types/search_result_item_pages_item.py +0 -0
  150. {groundx-2.5.8 → groundx-2.7.3}/src/groundx/types/sort.py +0 -0
  151. {groundx-2.5.8 → groundx-2.7.3}/src/groundx/types/sort_order.py +0 -0
  152. {groundx-2.5.8 → groundx-2.7.3}/src/groundx/types/subscription_detail.py +0 -0
  153. {groundx-2.5.8 → groundx-2.7.3}/src/groundx/types/subscription_detail_meters.py +0 -0
  154. {groundx-2.5.8 → groundx-2.7.3}/src/groundx/types/website_source.py +0 -0
  155. {groundx-2.5.8 → groundx-2.7.3}/src/groundx/version.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: groundx
3
- Version: 2.5.8
3
+ Version: 2.7.3
4
4
  Summary:
5
5
  License: MIT
6
6
  Requires-Python: >=3.8,<4.0
@@ -3,7 +3,7 @@ name = "groundx"
3
3
 
4
4
  [tool.poetry]
5
5
  name = "groundx"
6
- version = "2.5.8"
6
+ version = "2.7.3"
7
7
  description = ""
8
8
  readme = "README.md"
9
9
  authors = []
@@ -50,13 +50,32 @@ from .types import (
50
50
  SubscriptionDetail,
51
51
  SubscriptionDetailMeters,
52
52
  WebsiteSource,
53
+ WorkflowApplyRequest,
54
+ WorkflowDetail,
55
+ WorkflowDetailChunkStrategy,
56
+ WorkflowDetailRelationships,
57
+ WorkflowEngine,
58
+ WorkflowEngineReasoningEffort,
59
+ WorkflowEngineService,
60
+ WorkflowPrompt,
61
+ WorkflowPromptGroup,
62
+ WorkflowPromptRole,
63
+ WorkflowRequest,
64
+ WorkflowRequestChunkStrategy,
65
+ WorkflowResponse,
66
+ WorkflowStep,
67
+ WorkflowStepConfig,
68
+ WorkflowStepConfigField,
69
+ WorkflowSteps,
70
+ WorkflowsResponse,
53
71
  )
54
72
  from .errors import BadRequestError, UnauthorizedError
55
- from . import buckets, customer, documents, groups, health, search
73
+ from . import buckets, customer, documents, groups, health, search, workflows
56
74
  from .environment import GroundXEnvironment
57
75
  from .ingest import AsyncGroundX, GroundX
58
76
  from .search import SearchContentRequestId
59
77
  from .version import __version__
78
+ from .workflows import WorkflowsGetRequestId
60
79
 
61
80
  __all__ = [
62
81
  "AsyncGroundX",
@@ -112,6 +131,25 @@ __all__ = [
112
131
  "SubscriptionDetailMeters",
113
132
  "UnauthorizedError",
114
133
  "WebsiteSource",
134
+ "WorkflowApplyRequest",
135
+ "WorkflowDetail",
136
+ "WorkflowDetailChunkStrategy",
137
+ "WorkflowDetailRelationships",
138
+ "WorkflowEngine",
139
+ "WorkflowEngineReasoningEffort",
140
+ "WorkflowEngineService",
141
+ "WorkflowPrompt",
142
+ "WorkflowPromptGroup",
143
+ "WorkflowPromptRole",
144
+ "WorkflowRequest",
145
+ "WorkflowRequestChunkStrategy",
146
+ "WorkflowResponse",
147
+ "WorkflowStep",
148
+ "WorkflowStepConfig",
149
+ "WorkflowStepConfigField",
150
+ "WorkflowSteps",
151
+ "WorkflowsGetRequestId",
152
+ "WorkflowsResponse",
115
153
  "__version__",
116
154
  "buckets",
117
155
  "customer",
@@ -119,4 +157,5 @@ __all__ = [
119
157
  "groups",
120
158
  "health",
121
159
  "search",
160
+ "workflows",
122
161
  ]
@@ -11,6 +11,7 @@ from .environment import GroundXEnvironment
11
11
  from .groups.client import AsyncGroupsClient, GroupsClient
12
12
  from .health.client import AsyncHealthClient, HealthClient
13
13
  from .search.client import AsyncSearchClient, SearchClient
14
+ from .workflows.client import AsyncWorkflowsClient, WorkflowsClient
14
15
 
15
16
 
16
17
  class GroundXBase:
@@ -77,6 +78,7 @@ class GroundXBase:
77
78
  self.search = SearchClient(client_wrapper=self._client_wrapper)
78
79
  self.buckets = BucketsClient(client_wrapper=self._client_wrapper)
79
80
  self.groups = GroupsClient(client_wrapper=self._client_wrapper)
81
+ self.workflows = WorkflowsClient(client_wrapper=self._client_wrapper)
80
82
  self.customer = CustomerClient(client_wrapper=self._client_wrapper)
81
83
  self.health = HealthClient(client_wrapper=self._client_wrapper)
82
84
 
@@ -145,6 +147,7 @@ class AsyncGroundXBase:
145
147
  self.search = AsyncSearchClient(client_wrapper=self._client_wrapper)
146
148
  self.buckets = AsyncBucketsClient(client_wrapper=self._client_wrapper)
147
149
  self.groups = AsyncGroupsClient(client_wrapper=self._client_wrapper)
150
+ self.workflows = AsyncWorkflowsClient(client_wrapper=self._client_wrapper)
148
151
  self.customer = AsyncCustomerClient(client_wrapper=self._client_wrapper)
149
152
  self.health = AsyncHealthClient(client_wrapper=self._client_wrapper)
150
153
 
@@ -14,10 +14,10 @@ class BaseClientWrapper:
14
14
 
15
15
  def get_headers(self) -> typing.Dict[str, str]:
16
16
  headers: typing.Dict[str, str] = {
17
- "User-Agent": "groundx/2.5.8",
17
+ "User-Agent": "groundx/2.7.3",
18
18
  "X-Fern-Language": "Python",
19
19
  "X-Fern-SDK-Name": "groundx",
20
- "X-Fern-SDK-Version": "2.5.8",
20
+ "X-Fern-SDK-Version": "2.7.3",
21
21
  }
22
22
  headers["X-API-Key"] = self.api_key
23
23
  return headers
@@ -84,7 +84,7 @@ class AgentCode(CodeAgent):
84
84
  def __init__(
85
85
  self,
86
86
  settings: AgentSettings,
87
- logger: Logger,
87
+ log: Logger,
88
88
  name: typing.Optional[str] = None,
89
89
  description: typing.Optional[str] = None,
90
90
  tools: typing.Optional[typing.List[Tool]] = None,
@@ -114,7 +114,7 @@ class AgentCode(CodeAgent):
114
114
 
115
115
  self.python_executor.static_tools.update({"open": open}) # type: ignore
116
116
 
117
- self.logger = logger
117
+ self.log = log
118
118
 
119
119
  def process(
120
120
  self,
@@ -137,7 +137,7 @@ class AgentCode(CodeAgent):
137
137
  f"agent process result is not of expected type(s) {expected_types!r}: [{e}]\n\n{res}"
138
138
  )
139
139
 
140
- self.logger.debug_msg(
140
+ self.log.debug_msg(
141
141
  f"agent process result is not of expected type(s) {expected_types!r}: [{e}], attempting again [{attempt+1}]\n\n{res}"
142
142
  )
143
143
 
@@ -148,7 +148,7 @@ class AgentTool(ToolCallingAgent):
148
148
  def __init__(
149
149
  self,
150
150
  settings: AgentSettings,
151
- logger: Logger,
151
+ log: Logger,
152
152
  name: typing.Optional[str] = None,
153
153
  description: typing.Optional[str] = None,
154
154
  tools: typing.Optional[typing.List[Tool]] = None,
@@ -172,7 +172,7 @@ class AgentTool(ToolCallingAgent):
172
172
  verbosity_level=verbosity,
173
173
  )
174
174
 
175
- self.logger = logger
175
+ self.log = log
176
176
 
177
177
  def process(
178
178
  self,
@@ -8,12 +8,13 @@ DocT = typing.TypeVar("DocT", bound=Document)
8
8
 
9
9
 
10
10
  class AgentRequest(BaseModel, typing.Generic[ReqT, DocT]):
11
- allowed_request_types: typing.List[str] = []
11
+ allowed_request_types: typing.ClassVar[typing.List[str]] = []
12
12
  request: ReqT
13
13
  request_type: str
14
14
  statement: DocT
15
15
 
16
16
  @field_validator("request_type")
17
+ @classmethod
17
18
  def validate_request_type(cls, value: str):
18
19
  if value not in cls.allowed_request_types:
19
20
  raise ValueError(
@@ -4,9 +4,11 @@ from io import BytesIO
4
4
  from pathlib import Path
5
5
  from PIL import Image
6
6
  from pydantic import BaseModel, ConfigDict, Field, PrivateAttr
7
+ from urllib.parse import urlparse
7
8
 
8
9
  from .groundx import GroundXDocument
9
10
  from ..services.logger import Logger
11
+ from ..services.upload import Upload
10
12
  from ..utility.classes import clean_json
11
13
 
12
14
 
@@ -44,6 +46,7 @@ class Document(BaseModel):
44
46
  base_url: str,
45
47
  cache_dir: Path,
46
48
  req: "DocumentRequest",
49
+ upload: typing.Optional[Upload] = None,
47
50
  **data: typing.Any,
48
51
  ) -> DocT:
49
52
  st = cls(**data)
@@ -56,7 +59,7 @@ class Document(BaseModel):
56
59
  base_url=base_url,
57
60
  documentID=req.document_id,
58
61
  taskID=req.task_id,
59
- ).xray(cache_dir=cache_dir, clear_cache=req.clear_cache)
62
+ ).xray(upload=upload, cache_dir=cache_dir, clear_cache=req.clear_cache)
60
63
 
61
64
  for page in xray_doc.documentPages:
62
65
  st.page_images.append(page.pageUrl)
@@ -250,6 +253,7 @@ class DocumentRequest(BaseModel):
250
253
  def load_images(
251
254
  self,
252
255
  imgs: typing.List[str],
256
+ upload: typing.Optional[Upload] = None,
253
257
  attempt: int = 0,
254
258
  should_sleep: bool = True,
255
259
  ) -> typing.List[Image.Image]:
@@ -261,26 +265,39 @@ class DocumentRequest(BaseModel):
261
265
  f"[{attempt}] loading cached [{self.page_image_dict[page]}] [{page}]",
262
266
  )
263
267
  pageImages.append(self.page_images[self.page_image_dict[page]])
264
- else:
265
- try:
266
- self.print("WARN", f"[{attempt}] downloading [{page}]")
267
- resp = requests.get(page)
268
- resp.raise_for_status()
269
- img = Image.open(BytesIO(resp.content))
268
+ continue
269
+
270
+ if upload:
271
+ parsed = urlparse(page)
272
+ path = parsed.path + ("?" + parsed.query if parsed.query else "")
273
+ ru = upload.get_object(path)
274
+ if ru:
275
+ img = Image.open(BytesIO(ru))
270
276
  if img:
271
277
  self.page_image_dict[page] = len(self.page_images)
272
278
  self.page_images.append(img)
273
279
  pageImages.append(img)
274
- except Exception as e:
275
- self.print(
276
- "ERROR", f"[{attempt}] Failed to load image from {page}: {e}"
280
+ continue
281
+
282
+ try:
283
+ self.print("WARN", f"[{attempt}] downloading [{page}]")
284
+ resp = requests.get(page)
285
+ resp.raise_for_status()
286
+ img = Image.open(BytesIO(resp.content))
287
+ if img:
288
+ self.page_image_dict[page] = len(self.page_images)
289
+ self.page_images.append(img)
290
+ pageImages.append(img)
291
+ except Exception as e:
292
+ self.print(
293
+ "ERROR", f"[{attempt}] Failed to load image from {page}: {e}"
294
+ )
295
+ if attempt < 2:
296
+ if should_sleep:
297
+ time.sleep(2 * attempt + 1)
298
+ return self.load_images(
299
+ imgs, upload, attempt + 1, should_sleep=should_sleep
277
300
  )
278
- if attempt < 2:
279
- if should_sleep:
280
- time.sleep(2 * attempt + 1)
281
- return self.load_images(
282
- imgs, attempt + 1, should_sleep=should_sleep
283
- )
284
301
 
285
302
  return pageImages
286
303
 
@@ -3,6 +3,8 @@ from pathlib import Path
3
3
 
4
4
  from pydantic import BaseModel, ConfigDict, Field
5
5
 
6
+ from ..services.upload import Upload
7
+
6
8
 
7
9
  class GroundXDocument(BaseModel):
8
10
  model_config = ConfigDict(populate_by_name=True)
@@ -10,6 +12,9 @@ class GroundXDocument(BaseModel):
10
12
  document_id: str = Field(alias="documentID")
11
13
  task_id: str = Field(alias="taskID")
12
14
 
15
+ def xray_path(self) -> str:
16
+ return f"layout/processed/{self.task_id}/{self.document_id}-xray.json"
17
+
13
18
  def xray_url(self, base: typing.Optional[str] = None) -> str:
14
19
  if not base:
15
20
  base = self.base_url
@@ -20,12 +25,14 @@ class GroundXDocument(BaseModel):
20
25
  def xray(
21
26
  self,
22
27
  cache_dir: Path,
28
+ upload: typing.Optional[Upload] = None,
23
29
  clear_cache: bool = False,
24
30
  is_test: bool = False,
25
31
  base: typing.Optional[str] = None,
26
32
  ) -> "XRayDocument":
27
33
  return XRayDocument.download(
28
34
  self,
35
+ upload=upload,
29
36
  cache_dir=cache_dir,
30
37
  base=base,
31
38
  clear_cache=clear_cache,
@@ -87,6 +94,7 @@ class XRayDocument(BaseModel):
87
94
  cls,
88
95
  gx_doc: GroundXDocument,
89
96
  cache_dir: Path,
97
+ upload: typing.Optional[Upload] = None,
90
98
  clear_cache: bool = False,
91
99
  is_test: bool = False,
92
100
  base: typing.Optional[str] = None,
@@ -99,30 +107,41 @@ class XRayDocument(BaseModel):
99
107
  with cache_file.open("r", encoding="utf-8") as f:
100
108
  payload = json.load(f)
101
109
 
110
+ return cls(**payload)
102
111
  except Exception as e:
103
112
  raise RuntimeError(
104
113
  f"Error loading cached X-ray JSON from {cache_file}: {e}"
105
114
  )
106
- else:
107
- url = gx_doc.xray_url(base=base)
108
- try:
109
- resp = requests.get(url)
110
- resp.raise_for_status()
111
- except requests.RequestException as e:
112
- raise RuntimeError(f"Error fetching X-ray JSON from {url}: {e}")
113
-
114
- try:
115
- payload = resp.json()
116
- except ValueError as e:
117
- raise RuntimeError(f"Invalid JSON returned from {url}: {e}")
118
115
 
119
- if is_test is False:
116
+ if upload:
117
+ path = gx_doc.xray_path()
118
+ ru = upload.get_object(path)
119
+ if ru:
120
120
  try:
121
- with cache_file.open("w", encoding="utf-8") as f:
122
- json.dump(payload, f)
121
+ payload = json.loads(ru.decode("utf-8"))
122
+ return cls(**payload)
123
123
  except Exception as e:
124
- print(
125
- f"Warning: failed to write X-ray JSON cache to {cache_file}: {e}"
124
+ raise RuntimeError(
125
+ f"Error decoding X-ray JSON bytes from {path}: {e}"
126
126
  )
127
127
 
128
+ url = gx_doc.xray_url(base=base)
129
+ try:
130
+ resp = requests.get(url)
131
+ resp.raise_for_status()
132
+ except requests.RequestException as e:
133
+ raise RuntimeError(f"Error fetching X-ray JSON from {url}: {e}")
134
+
135
+ try:
136
+ payload = resp.json()
137
+ except ValueError as e:
138
+ raise RuntimeError(f"Invalid JSON returned from {url}: {e}")
139
+
140
+ if is_test is False:
141
+ try:
142
+ with cache_file.open("w", encoding="utf-8") as f:
143
+ json.dump(payload, f)
144
+ except Exception as e:
145
+ print(f"Warning: failed to write X-ray JSON cache to {cache_file}: {e}")
146
+
128
147
  return cls(**payload)
@@ -2,8 +2,6 @@ import typing
2
2
 
3
3
 
4
4
  def logging_config(name: str, level: str) -> typing.Dict[str, typing.Any]:
5
- print(level)
6
-
7
5
  return {
8
6
  "version": 1,
9
7
  "disable_existing_loggers": False,
@@ -46,9 +46,6 @@ class Upload:
46
46
  else:
47
47
  raise Exception(f"unsupported upload.type [{self.settings.upload.type}]")
48
48
 
49
- def get_file(self, url: str) -> bytes:
50
- return bytes()
51
-
52
49
  def get_object(self, url: str) -> typing.Optional[bytes]:
53
50
  self.client.get_object(url)
54
51
 
@@ -28,9 +28,15 @@ class S3Client:
28
28
  return None
29
29
 
30
30
  try:
31
- s3_uri_parts = url.replace("s3://", "").split("/")
32
- s3_bucket = s3_uri_parts[0]
33
- s3_key = "/".join(s3_uri_parts[1:])
31
+ if url.startswith("s3://"):
32
+ s3_uri_parts = url.replace("s3://", "").split("/")
33
+ s3_bucket = s3_uri_parts[0]
34
+ s3_key = "/".join(s3_uri_parts[1:])
35
+ else:
36
+ s3_bucket = self.settings.upload.bucket
37
+ s3_key = url
38
+ if url.startswith("/"):
39
+ s3_key = url[1:]
34
40
 
35
41
  response = self.client.get_object(Bucket=s3_bucket, Key=s3_key)
36
42
 
@@ -17,6 +17,8 @@ GX_DEFAULT_REGION: str = "GROUNDX_DEFAULT_REGION"
17
17
  GX_SECRET: str = "GROUNDX_SECRET_ACCESS_KEY"
18
18
  GX_TOKEN: str = "GROUNDX_SESSION_TOKEN"
19
19
  VALID_KEYS: str = "GROUNDX_VALID_API_KEYS"
20
+ GX_ADMIN_API_KEY: str = "GROUNDX_ADMIN_API_KEY"
21
+ GX_ADMIN_USERNAME: str = "GROUNDX_ADMIN_USERNAME"
20
22
 
21
23
 
22
24
  class AgentSettings(BaseModel):
@@ -77,22 +79,54 @@ class ContainerSettings(BaseModel):
77
79
  if key:
78
80
  return key
79
81
 
82
+ key = os.environ.get(GX_ADMIN_API_KEY)
83
+ if key:
84
+ return key
85
+
86
+ key = os.environ.get(GX_ADMIN_USERNAME)
87
+ if key:
88
+ return key
89
+
90
+ key = os.environ.get(GX_API_KEY)
91
+ if key:
92
+ return key
93
+
80
94
  raise Exception(f"you must set a callback_api_key")
81
95
 
82
96
  def get_valid_api_keys(self) -> typing.List[str]:
97
+ keys: typing.List[str] = []
98
+
83
99
  if self.valid_api_keys:
84
- return self.valid_api_keys
100
+ keys = self.valid_api_keys
85
101
 
86
- keys: typing.Optional[str] = os.environ.get(VALID_KEYS)
87
- if not keys:
88
- raise Exception(f"you must set an array of valid_api_keys")
102
+ env_keys: typing.Optional[str] = os.environ.get(VALID_KEYS)
103
+ if env_keys:
104
+ try:
105
+ data: typing.List[str] = json.loads(env_keys)
106
+ keys.extend(data)
107
+ except Exception as e:
108
+ raise Exception(f"you must set an array of valid_api_keys: {e}")
89
109
 
90
- try:
91
- data: typing.List[str] = json.loads(keys)
92
- except Exception as e:
93
- raise Exception(f"you must set an array of valid_api_keys: {e}")
110
+ key = os.environ.get(CALLBACK_KEY)
111
+ if key:
112
+ keys.append(key)
94
113
 
95
- return data
114
+ key = os.environ.get(GX_ADMIN_API_KEY)
115
+ if key:
116
+ keys.append(key)
117
+
118
+ key = os.environ.get(GX_ADMIN_USERNAME)
119
+ if key:
120
+ keys.append(key)
121
+
122
+ key = os.environ.get(GX_API_KEY)
123
+ if key:
124
+ keys.append(key)
125
+
126
+ if len(keys) < 1:
127
+ raise Exception(f"you must set an array of valid_api_keys")
128
+
129
+ return keys
96
130
 
97
131
  def loglevel(self) -> str:
98
132
  return self.log_level.upper()
@@ -167,4 +201,12 @@ class GroundXSettings(BaseModel):
167
201
  if key:
168
202
  return key
169
203
 
204
+ key = os.environ.get(GX_ADMIN_API_KEY)
205
+ if key:
206
+ return key
207
+
208
+ key = os.environ.get(GX_ADMIN_USERNAME)
209
+ if key:
210
+ return key
211
+
170
212
  raise Exception(f"you must set a valid GroundX api_key")
@@ -45,7 +45,6 @@ class TestAgentSettings(unittest.TestCase):
45
45
  "expect": {
46
46
  "api_base": "http://test.com",
47
47
  "api_key": "mykey",
48
- "api_key_env": "myenv",
49
48
  "max_steps": 4,
50
49
  "model_id": "gpt-5",
51
50
  },
@@ -452,10 +451,8 @@ class TestGroundXSettings(unittest.TestCase):
452
451
  def test(self) -> None:
453
452
  tsts: typing.List[typing.Dict[str, typing.Any]] = [
454
453
  {
455
- "api_key_env": "",
456
454
  "expect": {
457
455
  "api_key": Exception,
458
- "api_key_env": "",
459
456
  "base_url": None,
460
457
  "upload_url": "https://upload.eyelevel.ai",
461
458
  },