Skip to content

Commit 6d53eb5

Browse files
committed
:sprakles: add API for RAG operations
1 parent b3e7f1d commit 6d53eb5

19 files changed

Lines changed: 458 additions & 38 deletions
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
from datetime import datetime
2+
3+
from mindee.parsing.common import CommonResponse
4+
from mindee.parsing.common.string_dict import StringDict
5+
6+
7+
class BaseRagAnnotationResponse(CommonResponse):
8+
"""Base class for all RAG document responses from the V2 API."""
9+
10+
id: str
11+
"""Unique identifier of the RAG document."""
12+
filename: str
13+
"""Original filename of the uploaded document."""
14+
created_at: datetime
15+
"""Date and time of the document creation."""
16+
status: str
17+
"""Current status of the RAG document."""
18+
19+
def __init__(self, raw_response: StringDict) -> None:
20+
super().__init__(raw_response)
21+
self.id = raw_response["id"]
22+
self.filename = raw_response["filename"]
23+
self.created_at = datetime.fromisoformat(
24+
raw_response["created_at"].replace("Z", "+00:00")
25+
)
26+
self.status = raw_response["status"]
27+

‎mindee/v2/parsing/inference/field/base_field.py‎

Lines changed: 40 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
from enum import Enum
2+
from typing import TYPE_CHECKING, ClassVar, TypeAlias, Union
23

34
from mindee.parsing.common.string_dict import StringDict
45
from mindee.v2.parsing.inference.field.field_confidence import FieldConfidence
@@ -13,17 +14,33 @@ class FieldType(str, Enum):
1314
SIMPLE = "SimpleField"
1415

1516

17+
if TYPE_CHECKING:
18+
from mindee.v2.parsing.inference.field.list_field import ListField
19+
from mindee.v2.parsing.inference.field.object_field import ObjectField
20+
from mindee.v2.parsing.inference.field.simple_field import SimpleField
21+
22+
23+
ResultFieldsType: TypeAlias = Union["SimpleField", "ObjectField", "ListField"]
24+
25+
1626
class BaseField:
17-
"""Field with base information."""
27+
"""Base class for V2 fields."""
1828

1929
field_type: FieldType
20-
_indent_level: int
30+
"""The type of field."""
2131
locations: list[FieldLocation]
32+
"""List of the location candidates for the value."""
2233
confidence: FieldConfidence | None
34+
"""Confidence associated with the field."""
35+
_indent_level: int
36+
"""For pretty printing."""
37+
38+
_registry: ClassVar[dict[str, type[ResultFieldsType]]] = {}
2339

2440
def __init__(
2541
self, field_type: FieldType, raw_response: StringDict, indent_level: int = 0
2642
) -> None:
43+
2744
self.field_type = field_type
2845
self._indent_level = indent_level
2946

@@ -41,6 +58,27 @@ def __init__(
4158
for location in raw_response["locations"]:
4259
self.locations.append(FieldLocation(location))
4360

61+
@classmethod
62+
def register(cls, discriminator_key: str):
63+
"""Class decorator: subclasses declare which JSON key identifies them."""
64+
65+
def decorator(subclass):
66+
cls._registry[discriminator_key] = subclass
67+
return subclass
68+
69+
return decorator
70+
71+
@classmethod
72+
def build(cls, raw_response: dict, indent_level: int) -> ResultFieldsType:
73+
"""Build an instance of the appropriate subclass."""
74+
75+
if not isinstance(raw_response, dict):
76+
raise ValueError("Field must be a dict")
77+
for key, subclass in cls._registry.items():
78+
if key in raw_response:
79+
return subclass(raw_response, indent_level)
80+
raise ValueError("Invalid structure for field")
81+
4482
def multi_str(self) -> str:
4583
"""String representation of the field in a list."""
4684
return str(self)

‎mindee/v2/parsing/inference/field/factory.py‎

Lines changed: 0 additions & 17 deletions
This file was deleted.

‎mindee/v2/parsing/inference/field/inference_fields.py‎

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,29 @@
1-
from collections.abc import Callable
21
from typing import TYPE_CHECKING, cast
32

43
from mindee.parsing.common.string_dict import StringDict
5-
from mindee.v2.parsing.inference.field.base_field import BaseField, FieldType
4+
from mindee.v2.parsing.inference.field.base_field import (
5+
BaseField,
6+
FieldType,
7+
ResultFieldsType,
8+
)
69

710
if TYPE_CHECKING:
811
from mindee.v2.parsing.inference.field.list_field import ListField
912
from mindee.v2.parsing.inference.field.object_field import ObjectField
1013
from mindee.v2.parsing.inference.field.simple_field import SimpleField
1114

1215

13-
class InferenceFields(dict[str, BaseField]):
16+
class InferenceFields(dict[str, ResultFieldsType]):
1417
"""Inference fields dict."""
1518

1619
def __init__(
1720
self,
1821
raw_response: StringDict,
19-
parser_func: Callable[[StringDict, int], BaseField],
2022
indent_level: int = 0,
2123
) -> None:
2224
super().__init__()
2325
for key, value in raw_response.items():
24-
self[key] = parser_func(value, indent_level)
26+
self[key] = BaseField.build(value, indent_level)
2527

2628
def __getattr__(self, item):
2729
try:

‎mindee/v2/parsing/inference/field/list_field.py‎

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,10 @@
1-
from collections.abc import Callable
2-
31
from mindee.parsing.common.string_dict import StringDict
42
from mindee.v2.parsing.inference.field.base_field import BaseField, FieldType
53
from mindee.v2.parsing.inference.field.object_field import ObjectField
64
from mindee.v2.parsing.inference.field.simple_field import SimpleField
75

86

7+
@BaseField.register("items")
98
class ListField(BaseField):
109
"""List field containing multiple fields."""
1110

@@ -15,14 +14,13 @@ class ListField(BaseField):
1514
def __init__(
1615
self,
1716
raw_response: StringDict,
18-
parser_func: Callable[[StringDict, int], BaseField],
1917
indent_level: int = 0,
2018
):
2119
super().__init__(FieldType.LIST, raw_response, indent_level)
2220

2321
self.items = []
2422
for item in raw_response["items"]:
25-
self.items.append(parser_func(item, indent_level))
23+
self.items.append(BaseField.build(item, indent_level))
2624

2725
@property
2826
def simple_items(self) -> list[SimpleField]:

‎mindee/v2/parsing/inference/field/object_field.py‎

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
from collections.abc import Callable
21
from typing import TYPE_CHECKING, cast
32

43
from mindee.parsing.common.string_dict import StringDict
@@ -10,19 +9,18 @@
109
from mindee.v2.parsing.inference.field.simple_field import SimpleField
1110

1211

12+
@BaseField.register("fields")
1313
class ObjectField(BaseField):
1414
"""Object field containing multiple fields."""
1515

1616
fields: InferenceFields
1717
"""Fields contained in the object."""
1818

19-
def __init__(
20-
self, raw_response: StringDict, parser_func: Callable, indent_level: int = 0
21-
):
19+
def __init__(self, raw_response: StringDict, indent_level: int = 0):
2220
super().__init__(FieldType.OBJECT, raw_response, indent_level)
2321
inner_fields = raw_response.get("fields", raw_response)
2422

25-
self.fields = InferenceFields(inner_fields, parser_func, self._indent_level + 1)
23+
self.fields = InferenceFields(inner_fields, self._indent_level + 1)
2624

2725
def single_str(self) -> str:
2826
"""String representation of a single object field."""

‎mindee/v2/parsing/inference/field/simple_field.py‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
from mindee.v2.parsing.inference.field.base_field import BaseField, FieldType
33

44

5+
@BaseField.register("value")
56
class SimpleField(BaseField):
67
"""Simple field containing a single value."""
78

‎mindee/v2/product/extraction/extraction_result.py‎

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
from mindee.parsing.common.string_dict import StringDict
22
from mindee.v2.parsing.inference.field import InferenceFields
3-
from mindee.v2.parsing.inference.field.factory import create_field
43
from mindee.v2.parsing.inference.rag_metadata import RAGMetadata
54
from mindee.v2.parsing.inference.raw_text import RawText
65

@@ -16,7 +15,7 @@ class ExtractionResult:
1615
"""RAG metadata."""
1716

1817
def __init__(self, raw_response: StringDict) -> None:
19-
self.fields = InferenceFields(raw_response["fields"], create_field)
18+
self.fields = InferenceFields(raw_response["fields"])
2019
if raw_response.get("raw_text"):
2120
self.raw_text = RawText(raw_response["raw_text"])
2221
if raw_response.get("rag"):
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
from mindee.v2.product.extraction.rag_documents.extraction_rag_annotation_response import (
2+
ExtractionRagAnnotationResponse,
3+
)
4+
from mindee.v2.product.extraction.rag_documents.rag_annotation import RagAnnotation
5+
6+
__all__ = [
7+
"ExtractionRagAnnotationResponse",
8+
"RagAnnotation",
9+
]
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
from typing import TYPE_CHECKING, ClassVar, TypeAlias, Union
2+
3+
from mindee.parsing.common.string_dict import StringDict
4+
from mindee.v2.parsing.inference.field.base_field import FieldType
5+
6+
if TYPE_CHECKING:
7+
from mindee.v2.product.extraction.rag_documents.annotated_list_field import (
8+
AnnotatedListField,
9+
)
10+
from mindee.v2.product.extraction.rag_documents.annotated_object_field import (
11+
AnnotatedObjectField,
12+
)
13+
from mindee.v2.product.extraction.rag_documents.annotated_simple_field import (
14+
AnnotatedSimpleField,
15+
)
16+
17+
AnnotatedFieldsType: TypeAlias = Union[
18+
"AnnotatedSimpleField", "AnnotatedObjectField", "AnnotatedListField"
19+
]
20+
21+
22+
class AnnotatedBaseField:
23+
"""Base class for annotated fields."""
24+
25+
selected: bool = False
26+
"""When true, use the RAG information for the final result. When false, use the Data Schema information."""
27+
28+
guidelines: str | None = None
29+
"""Guidelines or instructions for processing this field."""
30+
31+
field_type: FieldType
32+
33+
_registry: ClassVar[dict[str, type[AnnotatedFieldsType]]] = {}
34+
35+
def __init__(self, field_type: FieldType, raw_response: StringDict):
36+
if "selected" in raw_response and raw_response["selected"] is not None:
37+
self.selected = raw_response["selected"]
38+
39+
if "guidelines" in raw_response and raw_response["guidelines"] is not None:
40+
self.guidelines = raw_response.get("guidelines")
41+
42+
self.field_type = field_type
43+
44+
@classmethod
45+
def register(cls, discriminator_key: str):
46+
"""Class decorator: subclasses declare which JSON key identifies them."""
47+
48+
def decorator(subclass):
49+
cls._registry[discriminator_key] = subclass
50+
return subclass
51+
52+
return decorator
53+
54+
@classmethod
55+
def build(cls, raw_response: dict) -> AnnotatedFieldsType:
56+
"""Build an instance of the appropriate subclass."""
57+
58+
if not isinstance(raw_response, dict):
59+
raise ValueError("Field must be a dict")
60+
for key, subclass in cls._registry.items():
61+
if key in raw_response:
62+
return subclass(raw_response)
63+
raise ValueError("Invalid structure for field")

0 commit comments

Comments
 (0)