groundx 2.0.15__py3-none-any.whl → 2.7.7__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- groundx/__init__.py +73 -21
- groundx/buckets/__init__.py +2 -0
- groundx/buckets/client.py +55 -388
- groundx/buckets/raw_client.py +628 -0
- groundx/client.py +22 -21
- groundx/core/__init__.py +5 -0
- groundx/core/api_error.py +13 -5
- groundx/core/client_wrapper.py +4 -3
- groundx/core/force_multipart.py +16 -0
- groundx/core/http_client.py +76 -32
- groundx/core/http_response.py +55 -0
- groundx/core/jsonable_encoder.py +0 -1
- groundx/core/pydantic_utilities.py +71 -112
- groundx/core/serialization.py +7 -3
- groundx/csv_splitter.py +64 -0
- groundx/customer/__init__.py +2 -0
- groundx/customer/client.py +31 -43
- groundx/customer/raw_client.py +91 -0
- groundx/documents/__init__.py +1 -2
- groundx/documents/client.py +455 -953
- groundx/documents/raw_client.py +1450 -0
- groundx/errors/__init__.py +2 -0
- groundx/errors/bad_request_error.py +4 -3
- groundx/errors/unauthorized_error.py +4 -3
- groundx/extract/__init__.py +48 -0
- groundx/extract/agents/__init__.py +7 -0
- groundx/extract/agents/agent.py +202 -0
- groundx/extract/classes/__init__.py +24 -0
- groundx/extract/classes/agent.py +23 -0
- groundx/extract/classes/api.py +15 -0
- groundx/extract/classes/document.py +338 -0
- groundx/extract/classes/field.py +88 -0
- groundx/extract/classes/groundx.py +147 -0
- groundx/extract/classes/prompt.py +36 -0
- groundx/extract/classes/test_document.py +109 -0
- groundx/extract/classes/test_field.py +43 -0
- groundx/extract/classes/test_groundx.py +223 -0
- groundx/extract/classes/test_prompt.py +68 -0
- groundx/extract/post_process/__init__.py +7 -0
- groundx/extract/post_process/post_process.py +33 -0
- groundx/extract/services/.DS_Store +0 -0
- groundx/extract/services/__init__.py +14 -0
- groundx/extract/services/csv.py +76 -0
- groundx/extract/services/logger.py +126 -0
- groundx/extract/services/logging_cfg.py +53 -0
- groundx/extract/services/ratelimit.py +104 -0
- groundx/extract/services/sheets_client.py +160 -0
- groundx/extract/services/status.py +197 -0
- groundx/extract/services/upload.py +68 -0
- groundx/extract/services/upload_minio.py +122 -0
- groundx/extract/services/upload_s3.py +91 -0
- groundx/extract/services/utility.py +52 -0
- groundx/extract/settings/__init__.py +15 -0
- groundx/extract/settings/settings.py +212 -0
- groundx/extract/settings/test_settings.py +512 -0
- groundx/extract/tasks/__init__.py +6 -0
- groundx/extract/tasks/utility.py +27 -0
- groundx/extract/utility/__init__.py +15 -0
- groundx/extract/utility/classes.py +193 -0
- groundx/extract/utility/test_utility.py +81 -0
- groundx/groups/__init__.py +2 -0
- groundx/groups/client.py +63 -550
- groundx/groups/raw_client.py +901 -0
- groundx/health/__init__.py +2 -0
- groundx/health/client.py +35 -101
- groundx/health/raw_client.py +193 -0
- groundx/ingest.py +771 -0
- groundx/search/__init__.py +2 -0
- groundx/search/client.py +94 -227
- groundx/search/raw_client.py +442 -0
- groundx/search/types/__init__.py +2 -0
- groundx/types/__init__.py +68 -16
- groundx/types/bounding_box_detail.py +4 -4
- groundx/types/bucket_detail.py +5 -5
- groundx/types/bucket_list_response.py +17 -3
- groundx/types/bucket_response.py +3 -3
- groundx/types/bucket_update_detail.py +4 -4
- groundx/types/bucket_update_response.py +3 -3
- groundx/types/customer_detail.py +2 -2
- groundx/types/customer_response.py +3 -3
- groundx/types/document.py +54 -0
- groundx/types/document_detail.py +16 -4
- groundx/types/document_list_response.py +4 -4
- groundx/types/document_local_ingest_request.py +7 -0
- groundx/types/document_lookup_response.py +8 -3
- groundx/types/document_response.py +3 -3
- groundx/types/document_type.py +21 -1
- groundx/types/group_detail.py +4 -4
- groundx/types/group_list_response.py +17 -3
- groundx/types/group_response.py +3 -3
- groundx/types/health_response.py +3 -3
- groundx/types/health_response_health.py +3 -3
- groundx/types/health_service.py +5 -5
- groundx/types/ingest_local_document.py +25 -0
- groundx/types/ingest_local_document_metadata.py +51 -0
- groundx/types/ingest_remote_document.py +15 -6
- groundx/types/ingest_response.py +4 -4
- groundx/types/{process_status_response_ingest.py → ingest_status.py} +8 -7
- groundx/types/{ingest_response_ingest.py → ingest_status_light.py} +7 -5
- groundx/types/ingest_status_progress.py +26 -0
- groundx/types/{process_status_response_ingest_progress_errors.py → ingest_status_progress_cancelled.py} +4 -4
- groundx/types/{process_status_response_ingest_progress_complete.py → ingest_status_progress_complete.py} +4 -4
- groundx/types/{process_status_response_ingest_progress_cancelled.py → ingest_status_progress_errors.py} +4 -4
- groundx/types/{process_status_response_ingest_progress_processing.py → ingest_status_progress_processing.py} +4 -4
- groundx/types/message_response.py +2 -2
- groundx/types/meter_detail.py +2 -2
- groundx/types/process_level.py +5 -0
- groundx/types/{process_status_response.py → processes_status_response.py} +8 -5
- groundx/types/processing_status.py +3 -1
- groundx/types/search_response.py +3 -3
- groundx/types/search_response_search.py +3 -3
- groundx/types/search_result_item.py +7 -5
- groundx/types/search_result_item_pages_item.py +41 -0
- groundx/types/subscription_detail.py +3 -3
- groundx/types/subscription_detail_meters.py +5 -5
- groundx/{documents/types/website_crawl_request_websites_item.py → types/website_source.py} +7 -7
- groundx/types/workflow_apply_request.py +24 -0
- groundx/types/workflow_detail.py +59 -0
- groundx/types/workflow_detail_chunk_strategy.py +5 -0
- groundx/types/workflow_detail_relationships.py +36 -0
- groundx/types/workflow_engine.py +58 -0
- groundx/types/workflow_engine_reasoning_effort.py +5 -0
- groundx/types/workflow_engine_service.py +7 -0
- groundx/types/workflow_prompt.py +37 -0
- groundx/types/workflow_prompt_group.py +25 -0
- groundx/types/workflow_prompt_role.py +5 -0
- groundx/types/workflow_request.py +31 -0
- groundx/types/workflow_request_chunk_strategy.py +5 -0
- groundx/types/workflow_response.py +20 -0
- groundx/types/workflow_step.py +33 -0
- groundx/types/workflow_step_config.py +33 -0
- groundx/types/workflow_step_config_field.py +8 -0
- groundx/types/workflow_steps.py +38 -0
- groundx/types/workflows_response.py +20 -0
- groundx/workflows/__init__.py +7 -0
- groundx/workflows/client.py +736 -0
- groundx/workflows/raw_client.py +841 -0
- groundx/workflows/types/__init__.py +7 -0
- groundx/workflows/types/workflows_get_request_id.py +5 -0
- {groundx-2.0.15.dist-info → groundx-2.7.7.dist-info}/LICENSE +1 -1
- {groundx-2.0.15.dist-info → groundx-2.7.7.dist-info}/METADATA +39 -22
- groundx-2.7.7.dist-info/RECORD +155 -0
- groundx/documents/types/__init__.py +0 -6
- groundx/documents/types/documents_ingest_local_request_files_item.py +0 -43
- groundx/types/process_status_response_ingest_progress.py +0 -26
- groundx-2.0.15.dist-info/RECORD +0 -82
- {groundx-2.0.15.dist-info → groundx-2.7.7.dist-info}/WHEEL +0 -0
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
import typing
|
|
2
|
+
|
|
3
|
+
if typing.TYPE_CHECKING:
|
|
4
|
+
from ..classes.prompt import Prompt
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def class_fields(cls: typing.Any) -> typing.Set[str]:
|
|
8
|
+
fields: typing.Set[str] = set()
|
|
9
|
+
if hasattr(cls, "model_fields"):
|
|
10
|
+
fields = set(cls.model_fields.keys())
|
|
11
|
+
elif hasattr(cls, "__fields__"):
|
|
12
|
+
fields = set(cls.__fields__.keys()) # type: ignore[reportDeprecated]
|
|
13
|
+
else:
|
|
14
|
+
fields = set()
|
|
15
|
+
|
|
16
|
+
return fields
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def clean_json(txt: str) -> str:
|
|
20
|
+
for p in ("json```\n", "```json\n", "json\n"):
|
|
21
|
+
if txt.startswith(p):
|
|
22
|
+
txt = txt[len(p) :]
|
|
23
|
+
if txt.endswith("```"):
|
|
24
|
+
txt = txt[:-3]
|
|
25
|
+
return txt.strip()
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def coerce_numeric_string(
|
|
29
|
+
value: typing.Any,
|
|
30
|
+
et: typing.Union[str, typing.List[str]],
|
|
31
|
+
) -> typing.Union[typing.Any, typing.List[typing.Any]]:
|
|
32
|
+
expected_types = str_to_type_sequence(et)
|
|
33
|
+
|
|
34
|
+
if any(t in (int, float) for t in expected_types):
|
|
35
|
+
if isinstance(value, str):
|
|
36
|
+
value = value.replace(",", "")
|
|
37
|
+
try:
|
|
38
|
+
value = float(value)
|
|
39
|
+
except ValueError:
|
|
40
|
+
return value
|
|
41
|
+
if float in expected_types:
|
|
42
|
+
return value
|
|
43
|
+
return int(value)
|
|
44
|
+
|
|
45
|
+
if str in expected_types and isinstance(value, str) and value == "0":
|
|
46
|
+
return None
|
|
47
|
+
|
|
48
|
+
return value
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def from_attr_name(
|
|
52
|
+
name: str, prompts: typing.Sequence[typing.Mapping[str, "Prompt"]]
|
|
53
|
+
) -> typing.Tuple[typing.Optional[str], typing.Optional[typing.Any]]:
|
|
54
|
+
for pmps in prompts:
|
|
55
|
+
for key, prompt in pmps.items():
|
|
56
|
+
if getattr(prompt, "attr_name", None) == name:
|
|
57
|
+
return key, prompt
|
|
58
|
+
|
|
59
|
+
return None, None
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def from_key(
|
|
63
|
+
name: str,
|
|
64
|
+
prompts: typing.Sequence[typing.Mapping[str, "Prompt"]],
|
|
65
|
+
) -> typing.Tuple[typing.Optional[str], typing.Optional[typing.Any]]:
|
|
66
|
+
for pmps in prompts:
|
|
67
|
+
for k, prompt in pmps.items():
|
|
68
|
+
if k == name:
|
|
69
|
+
return k, prompt
|
|
70
|
+
|
|
71
|
+
key, pmp = from_attr_name(name, prompts)
|
|
72
|
+
if pmp:
|
|
73
|
+
return key, pmp
|
|
74
|
+
|
|
75
|
+
return None, None
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
def str_to_type_sequence(
|
|
79
|
+
ty: typing.Union[str, typing.List[str]],
|
|
80
|
+
) -> typing.Sequence[typing.Type[typing.Any]]:
|
|
81
|
+
if isinstance(ty, list):
|
|
82
|
+
tys: typing.List[typing.Any] = []
|
|
83
|
+
for t in ty:
|
|
84
|
+
tys.append(str_to_type(t))
|
|
85
|
+
|
|
86
|
+
return tys
|
|
87
|
+
|
|
88
|
+
return [str_to_type(ty)]
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def str_to_type(
|
|
92
|
+
ty: str,
|
|
93
|
+
) -> typing.Type[typing.Any]:
|
|
94
|
+
if ty == "int":
|
|
95
|
+
return int
|
|
96
|
+
elif ty == "float":
|
|
97
|
+
return float
|
|
98
|
+
elif ty == "list":
|
|
99
|
+
return list
|
|
100
|
+
elif ty == "dict":
|
|
101
|
+
return dict
|
|
102
|
+
|
|
103
|
+
return str
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
def type_to_str(
|
|
107
|
+
ty: typing.Union[typing.Type[typing.Any], typing.Sequence[typing.Type[typing.Any]]],
|
|
108
|
+
) -> typing.Union[str, typing.List[str]]:
|
|
109
|
+
if isinstance(ty, list):
|
|
110
|
+
tys: typing.List[str] = []
|
|
111
|
+
for t in ty:
|
|
112
|
+
nt = type_to_str(t)
|
|
113
|
+
if isinstance(nt, str):
|
|
114
|
+
tys.append(nt)
|
|
115
|
+
else:
|
|
116
|
+
tys.append("list")
|
|
117
|
+
|
|
118
|
+
if ty == int:
|
|
119
|
+
return "int"
|
|
120
|
+
if ty == float:
|
|
121
|
+
return "float"
|
|
122
|
+
if ty == list:
|
|
123
|
+
return "list"
|
|
124
|
+
if ty == dict:
|
|
125
|
+
return "dict"
|
|
126
|
+
|
|
127
|
+
return "str"
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
def validate_confidence(
|
|
131
|
+
key: str,
|
|
132
|
+
key_data: typing.Any,
|
|
133
|
+
fields: typing.Set[str],
|
|
134
|
+
value: typing.Any,
|
|
135
|
+
errors: typing.Dict[str, str],
|
|
136
|
+
) -> typing.Tuple[
|
|
137
|
+
typing.Union[typing.Any, typing.List[typing.Any]],
|
|
138
|
+
typing.Optional[str],
|
|
139
|
+
typing.Optional[str],
|
|
140
|
+
]:
|
|
141
|
+
if key_data.attr_name not in fields:
|
|
142
|
+
return None, None, f"unexpected attribute [{key_data.attr_name}]"
|
|
143
|
+
|
|
144
|
+
if value is None:
|
|
145
|
+
return None, None, None
|
|
146
|
+
|
|
147
|
+
if not isinstance(value, dict):
|
|
148
|
+
return (
|
|
149
|
+
None,
|
|
150
|
+
None,
|
|
151
|
+
f"unexpected value type [{key_data.attr_name}] [{type(value)}] [{key_data.type}]\n[{value}]",
|
|
152
|
+
)
|
|
153
|
+
|
|
154
|
+
if "value" not in value:
|
|
155
|
+
return (
|
|
156
|
+
None,
|
|
157
|
+
None,
|
|
158
|
+
f'value is missing "value" key [{key_data.attr_name}]\n[{value}]',
|
|
159
|
+
)
|
|
160
|
+
|
|
161
|
+
if value["value"] is None:
|
|
162
|
+
return None, None, None
|
|
163
|
+
|
|
164
|
+
final_value = coerce_numeric_string(value["value"], key_data.type)
|
|
165
|
+
if not key_data.valid_value(final_value):
|
|
166
|
+
return (
|
|
167
|
+
final_value,
|
|
168
|
+
None,
|
|
169
|
+
f"unexpected type for statement [{key}] value [{type(final_value)}]\n\n{final_value}",
|
|
170
|
+
)
|
|
171
|
+
|
|
172
|
+
if "confidence" not in value:
|
|
173
|
+
return (
|
|
174
|
+
final_value,
|
|
175
|
+
None,
|
|
176
|
+
f'value is missing "confidence" key [{key_data.attr_name}]\n[{value}]',
|
|
177
|
+
)
|
|
178
|
+
|
|
179
|
+
if not isinstance(value["confidence"], str):
|
|
180
|
+
return (
|
|
181
|
+
final_value,
|
|
182
|
+
None,
|
|
183
|
+
f"confidence is not type str [{key_data.attr_name}]\n[{value}]",
|
|
184
|
+
)
|
|
185
|
+
|
|
186
|
+
if value["confidence"] not in ["low", "medium", "high"]:
|
|
187
|
+
return (
|
|
188
|
+
final_value,
|
|
189
|
+
None,
|
|
190
|
+
f'confidence value is unsupported value [{key_data.attr_name}]\n[{value["confidence"]}]',
|
|
191
|
+
)
|
|
192
|
+
|
|
193
|
+
return final_value, value["confidence"], None
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import typing, unittest
|
|
2
|
+
|
|
3
|
+
from .classes import class_fields, coerce_numeric_string
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class DummyModelFields:
|
|
7
|
+
model_fields = {"a": 1, "b": 2}
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class DummyDunderFields:
|
|
11
|
+
__fields__ = {"x": 10, "y": 20}
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class DummyBothFields:
|
|
15
|
+
model_fields = {"m": None}
|
|
16
|
+
__fields__ = {"f": None}
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class DummyNoFields:
|
|
20
|
+
pass
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class TestUtilClassFields(unittest.TestCase):
|
|
24
|
+
def test_model_fields(self):
|
|
25
|
+
expected = {"a", "b"}
|
|
26
|
+
# class and instance both should return model_fields keys
|
|
27
|
+
self.assertEqual(class_fields(DummyModelFields), expected)
|
|
28
|
+
self.assertEqual(class_fields(DummyModelFields()), expected)
|
|
29
|
+
|
|
30
|
+
def test_dunder_fields(self):
|
|
31
|
+
expected = {"x", "y"}
|
|
32
|
+
# fallback to __fields__ when model_fields not present
|
|
33
|
+
self.assertEqual(class_fields(DummyDunderFields), expected)
|
|
34
|
+
self.assertEqual(class_fields(DummyDunderFields()), expected)
|
|
35
|
+
|
|
36
|
+
def test_prefers_model_over_dunder(self):
|
|
37
|
+
# when both exist, model_fields takes precedence
|
|
38
|
+
expected = {"m"}
|
|
39
|
+
self.assertEqual(class_fields(DummyBothFields), expected)
|
|
40
|
+
self.assertEqual(class_fields(DummyBothFields()), expected)
|
|
41
|
+
|
|
42
|
+
def test_no_fields(self):
|
|
43
|
+
# no field attributes yields empty set
|
|
44
|
+
self.assertEqual(class_fields(DummyNoFields), set())
|
|
45
|
+
self.assertEqual(class_fields(DummyNoFields()), set())
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
class TestUtilCoerceNumericString(unittest.TestCase):
|
|
49
|
+
def test_expected_str(self) -> None:
|
|
50
|
+
# When expected type is str, no coercion occurs
|
|
51
|
+
self.assertEqual(coerce_numeric_string("42", "str"), "42")
|
|
52
|
+
self.assertEqual(coerce_numeric_string("foo", "str"), "foo")
|
|
53
|
+
self.assertEqual(coerce_numeric_string(7, "str"), 7)
|
|
54
|
+
self.assertEqual(coerce_numeric_string(2.71, "str"), 2.71)
|
|
55
|
+
|
|
56
|
+
def test_expected_int(self) -> None:
|
|
57
|
+
# Numeric string to int or float based on content
|
|
58
|
+
self.assertEqual(coerce_numeric_string("42", "int"), 42)
|
|
59
|
+
self.assertEqual(coerce_numeric_string("3.14", "int"), 3)
|
|
60
|
+
self.assertEqual(coerce_numeric_string("foo", "int"), "foo")
|
|
61
|
+
self.assertEqual(coerce_numeric_string(8, "int"), 8)
|
|
62
|
+
self.assertEqual(coerce_numeric_string(3.14, "int"), 3)
|
|
63
|
+
|
|
64
|
+
def test_expected_float(self) -> None:
|
|
65
|
+
self.assertEqual(coerce_numeric_string("42", "float"), 42.0)
|
|
66
|
+
self.assertEqual(coerce_numeric_string("3.14", "float"), 3.14)
|
|
67
|
+
self.assertEqual(coerce_numeric_string("foo", "float"), "foo")
|
|
68
|
+
self.assertEqual(coerce_numeric_string(9.81, "float"), 9.81)
|
|
69
|
+
self.assertEqual(coerce_numeric_string(10, "float"), 10)
|
|
70
|
+
|
|
71
|
+
def test_expected_int_float_list(self) -> None:
|
|
72
|
+
types: typing.List[str] = ["int", "float"]
|
|
73
|
+
self.assertEqual(coerce_numeric_string("42", types), 42)
|
|
74
|
+
self.assertEqual(coerce_numeric_string("3.14", types), 3.14)
|
|
75
|
+
self.assertEqual(coerce_numeric_string("foo", types), "foo")
|
|
76
|
+
self.assertEqual(coerce_numeric_string(11, types), 11)
|
|
77
|
+
self.assertEqual(coerce_numeric_string(2.718, types), 2.718)
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
if __name__ == "__main__":
|
|
81
|
+
unittest.main()
|
groundx/groups/__init__.py
CHANGED