msgflux 0.1.0__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.
- msgflux/__init__.py +104 -0
- msgflux/_private/__init__.py +0 -0
- msgflux/_private/client.py +105 -0
- msgflux/_private/core.py +14 -0
- msgflux/_private/executor.py +134 -0
- msgflux/_private/response.py +230 -0
- msgflux/auto/__init__.py +5 -0
- msgflux/auto/params.py +167 -0
- msgflux/cache.py +54 -0
- msgflux/core/__init__.py +5 -0
- msgflux/core/dotdict.py +264 -0
- msgflux/core/examples.py +169 -0
- msgflux/core/message.py +45 -0
- msgflux/data/__init__.py +0 -0
- msgflux/data/audio.py +1 -0
- msgflux/data/dbs/__init__.py +16 -0
- msgflux/data/dbs/base.py +124 -0
- msgflux/data/dbs/db.py +65 -0
- msgflux/data/dbs/providers/__init__.py +0 -0
- msgflux/data/dbs/providers/cachetools.py +57 -0
- msgflux/data/dbs/providers/diskcache.py +48 -0
- msgflux/data/dbs/providers/faiss.py +220 -0
- msgflux/data/dbs/registry.py +19 -0
- msgflux/data/dbs/response.py +10 -0
- msgflux/data/dbs/types.py +26 -0
- msgflux/data/parsers/__init__.py +16 -0
- msgflux/data/parsers/base.py +325 -0
- msgflux/data/parsers/parser.py +101 -0
- msgflux/data/parsers/providers/__init__.py +32 -0
- msgflux/data/parsers/providers/csv_parser.py +278 -0
- msgflux/data/parsers/providers/email_parser.py +264 -0
- msgflux/data/parsers/providers/html_parser.py +265 -0
- msgflux/data/parsers/providers/openpyxl.py +333 -0
- msgflux/data/parsers/providers/pypdf.py +186 -0
- msgflux/data/parsers/providers/python_docx.py +334 -0
- msgflux/data/parsers/providers/python_pptx.py +197 -0
- msgflux/data/parsers/registry.py +19 -0
- msgflux/data/parsers/response.py +32 -0
- msgflux/data/parsers/types.py +110 -0
- msgflux/data/retrievers/__init__.py +16 -0
- msgflux/data/retrievers/base.py +185 -0
- msgflux/data/retrievers/providers/__init__.py +0 -0
- msgflux/data/retrievers/providers/bm25.py +230 -0
- msgflux/data/retrievers/providers/bm25s.py +221 -0
- msgflux/data/retrievers/providers/rank_bm25.py +148 -0
- msgflux/data/retrievers/providers/rapidfuzz.py +125 -0
- msgflux/data/retrievers/providers/wikipedia.py +281 -0
- msgflux/data/retrievers/registry.py +19 -0
- msgflux/data/retrievers/retriever.py +83 -0
- msgflux/data/retrievers/types.py +24 -0
- msgflux/data/types/__init__.py +29 -0
- msgflux/data/types/audio.py +112 -0
- msgflux/data/types/base.py +70 -0
- msgflux/data/types/file.py +114 -0
- msgflux/data/types/image.py +134 -0
- msgflux/data/types/video.py +121 -0
- msgflux/dsl/__init__.py +0 -0
- msgflux/dsl/inline.py +484 -0
- msgflux/dsl/signature.py +393 -0
- msgflux/dsl/typed_parsers/__init__.py +19 -0
- msgflux/dsl/typed_parsers/base.py +24 -0
- msgflux/dsl/typed_parsers/providers/__init__.py +0 -0
- msgflux/dsl/typed_parsers/providers/xml.py +259 -0
- msgflux/dsl/typed_parsers/registry.py +14 -0
- msgflux/envs.py +100 -0
- msgflux/exceptions.py +84 -0
- msgflux/generation/__init__.py +0 -0
- msgflux/generation/control_flow.py +163 -0
- msgflux/generation/reasoning/__init__.py +5 -0
- msgflux/generation/reasoning/cot.py +7 -0
- msgflux/generation/reasoning/react.py +289 -0
- msgflux/generation/reasoning/self_consistency.py +16 -0
- msgflux/generation/templates.py +53 -0
- msgflux/logger.py +159 -0
- msgflux/models/__init__.py +16 -0
- msgflux/models/base.py +39 -0
- msgflux/models/cache.py +125 -0
- msgflux/models/gateway.py +466 -0
- msgflux/models/httpx.py +81 -0
- msgflux/models/model.py +122 -0
- msgflux/models/profiles/__init__.py +48 -0
- msgflux/models/profiles/base.py +122 -0
- msgflux/models/profiles/loader.py +264 -0
- msgflux/models/profiles/registry.py +207 -0
- msgflux/models/providers/__init__.py +0 -0
- msgflux/models/providers/cerebras.py +30 -0
- msgflux/models/providers/groq.py +52 -0
- msgflux/models/providers/imagerouter.py +33 -0
- msgflux/models/providers/jinaai.py +331 -0
- msgflux/models/providers/ollama.py +51 -0
- msgflux/models/providers/openai.py +1884 -0
- msgflux/models/providers/openrouter.py +64 -0
- msgflux/models/providers/replicate.py +218 -0
- msgflux/models/providers/sambanova.py +38 -0
- msgflux/models/providers/together.py +57 -0
- msgflux/models/providers/vllm.py +131 -0
- msgflux/models/registry.py +19 -0
- msgflux/models/response.py +26 -0
- msgflux/models/tool_call_agg.py +85 -0
- msgflux/models/types.py +196 -0
- msgflux/nn/__init__.py +57 -0
- msgflux/nn/functional.py +645 -0
- msgflux/nn/hooks/__init__.py +24 -0
- msgflux/nn/hooks/guard.py +109 -0
- msgflux/nn/hooks/hook.py +123 -0
- msgflux/nn/modules/__init__.py +68 -0
- msgflux/nn/modules/agent.py +1927 -0
- msgflux/nn/modules/container.py +672 -0
- msgflux/nn/modules/embedder.py +261 -0
- msgflux/nn/modules/generator.py +21 -0
- msgflux/nn/modules/mediamaker.py +285 -0
- msgflux/nn/modules/module.py +2106 -0
- msgflux/nn/modules/predictor.py +238 -0
- msgflux/nn/modules/searcher.py +383 -0
- msgflux/nn/modules/speaker.py +222 -0
- msgflux/nn/modules/tool.py +839 -0
- msgflux/nn/modules/transcriber.py +273 -0
- msgflux/nn/parameter.py +77 -0
- msgflux/protocols/__init__.py +1 -0
- msgflux/protocols/mcp/__init__.py +61 -0
- msgflux/protocols/mcp/auth/__init__.py +17 -0
- msgflux/protocols/mcp/auth/apikey.py +70 -0
- msgflux/protocols/mcp/auth/base.py +83 -0
- msgflux/protocols/mcp/auth/basic.py +57 -0
- msgflux/protocols/mcp/auth/bearer.py +97 -0
- msgflux/protocols/mcp/auth/custom.py +76 -0
- msgflux/protocols/mcp/auth/oauth2.py +133 -0
- msgflux/protocols/mcp/client.py +454 -0
- msgflux/protocols/mcp/exceptions.py +25 -0
- msgflux/protocols/mcp/integration.py +95 -0
- msgflux/protocols/mcp/loglevels.py +16 -0
- msgflux/protocols/mcp/transports.py +435 -0
- msgflux/protocols/mcp/types.py +51 -0
- msgflux/py.typed +0 -0
- msgflux/telemetry/__init__.py +22 -0
- msgflux/telemetry/config.py +104 -0
- msgflux/telemetry/span.py +245 -0
- msgflux/tools/__init__.py +10 -0
- msgflux/tools/builtin/__init__.py +5 -0
- msgflux/tools/builtin/web_fetch.py +121 -0
- msgflux/tools/config.py +196 -0
- msgflux/tools/definitions.py +11 -0
- msgflux/utils/__init__.py +0 -0
- msgflux/utils/chat.py +682 -0
- msgflux/utils/common.py +36 -0
- msgflux/utils/console.py +55 -0
- msgflux/utils/convert.py +20 -0
- msgflux/utils/encode.py +192 -0
- msgflux/utils/html.py +171 -0
- msgflux/utils/imports.py +115 -0
- msgflux/utils/inspect.py +81 -0
- msgflux/utils/logging.py +15 -0
- msgflux/utils/mermaid.py +20 -0
- msgflux/utils/msgspec.py +1116 -0
- msgflux/utils/pooling.py +51 -0
- msgflux/utils/templates.py +51 -0
- msgflux/utils/tenacity.py +38 -0
- msgflux/utils/torch.py +7 -0
- msgflux/utils/validation.py +31 -0
- msgflux/utils/xml.py +7 -0
- msgflux/version.py +1 -0
- msgflux-0.1.0.dist-info/METADATA +170 -0
- msgflux-0.1.0.dist-info/RECORD +165 -0
- msgflux-0.1.0.dist-info/WHEEL +4 -0
- msgflux-0.1.0.dist-info/licenses/LICENSE +21 -0
|
@@ -0,0 +1,325 @@
|
|
|
1
|
+
import base64
|
|
2
|
+
import pathlib
|
|
3
|
+
from io import BytesIO
|
|
4
|
+
from typing import Dict, Mapping, Union
|
|
5
|
+
|
|
6
|
+
try:
|
|
7
|
+
import httpx
|
|
8
|
+
except ImportError:
|
|
9
|
+
httpx = None
|
|
10
|
+
|
|
11
|
+
from msgflux._private.client import BaseClient
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class BaseParser(BaseClient):
|
|
15
|
+
"""Base class for all parsers.
|
|
16
|
+
|
|
17
|
+
Provides common functionality for file parsing operations including:
|
|
18
|
+
- File loading from various sources (path, URL, bytes)
|
|
19
|
+
- File type validation
|
|
20
|
+
- Image encoding utilities
|
|
21
|
+
"""
|
|
22
|
+
|
|
23
|
+
msgflux_type = "parser"
|
|
24
|
+
to_ignore = ["client"]
|
|
25
|
+
|
|
26
|
+
def instance_type(self) -> Mapping[str, str]:
|
|
27
|
+
return {"parser_type": self.parser_type}
|
|
28
|
+
|
|
29
|
+
def _validate_file_type(
|
|
30
|
+
self, file_path: str, expected_extensions: Union[str, list[str]]
|
|
31
|
+
) -> bool:
|
|
32
|
+
"""Validate that a file has the expected extension.
|
|
33
|
+
|
|
34
|
+
Args:
|
|
35
|
+
file_path:
|
|
36
|
+
Path to the file to validate.
|
|
37
|
+
expected_extensions:
|
|
38
|
+
Single extension string (e.g., ".pdf") or list of valid extensions.
|
|
39
|
+
|
|
40
|
+
Returns:
|
|
41
|
+
True if file extension is valid.
|
|
42
|
+
|
|
43
|
+
Raises:
|
|
44
|
+
ValueError:
|
|
45
|
+
If file extension doesn't match expected extensions.
|
|
46
|
+
"""
|
|
47
|
+
if isinstance(expected_extensions, str):
|
|
48
|
+
expected_extensions = [expected_extensions]
|
|
49
|
+
|
|
50
|
+
file_ext = pathlib.Path(file_path).suffix.lower()
|
|
51
|
+
if file_ext not in expected_extensions:
|
|
52
|
+
expected_str = ", ".join(expected_extensions)
|
|
53
|
+
raise ValueError(
|
|
54
|
+
f"Invalid file type. Expected {expected_str}, got {file_ext}"
|
|
55
|
+
)
|
|
56
|
+
return True
|
|
57
|
+
|
|
58
|
+
def _load_file(self, data: Union[str, bytes, BytesIO]) -> bytes:
|
|
59
|
+
"""Load file from various sources.
|
|
60
|
+
|
|
61
|
+
Args:
|
|
62
|
+
data:
|
|
63
|
+
Can be:
|
|
64
|
+
- str: file path or URL
|
|
65
|
+
- bytes: raw file data
|
|
66
|
+
- BytesIO: file buffer
|
|
67
|
+
|
|
68
|
+
Returns:
|
|
69
|
+
File content as bytes.
|
|
70
|
+
|
|
71
|
+
Raises:
|
|
72
|
+
ValueError:
|
|
73
|
+
If data type is not supported.
|
|
74
|
+
FileNotFoundError:
|
|
75
|
+
If file path doesn't exist.
|
|
76
|
+
"""
|
|
77
|
+
if isinstance(data, bytes):
|
|
78
|
+
return data
|
|
79
|
+
|
|
80
|
+
if isinstance(data, BytesIO):
|
|
81
|
+
return data.getvalue()
|
|
82
|
+
|
|
83
|
+
if isinstance(data, str):
|
|
84
|
+
# Check if it's a URL
|
|
85
|
+
if data.startswith(("http://", "https://")):
|
|
86
|
+
if httpx is None:
|
|
87
|
+
raise ImportError(
|
|
88
|
+
"`httpx` is required to load files from URLs. "
|
|
89
|
+
"Install with `pip install httpx`"
|
|
90
|
+
)
|
|
91
|
+
response = httpx.get(data)
|
|
92
|
+
response.raise_for_status()
|
|
93
|
+
return response.content
|
|
94
|
+
|
|
95
|
+
# Otherwise treat as file path
|
|
96
|
+
file_path = pathlib.Path(data)
|
|
97
|
+
if not file_path.exists():
|
|
98
|
+
raise FileNotFoundError(f"File not found: {data}")
|
|
99
|
+
|
|
100
|
+
with open(file_path, "rb") as f:
|
|
101
|
+
return f.read()
|
|
102
|
+
|
|
103
|
+
raise ValueError(
|
|
104
|
+
f"Unsupported data type: {type(data)}. "
|
|
105
|
+
"Expected str (path/URL), bytes, or BytesIO"
|
|
106
|
+
)
|
|
107
|
+
|
|
108
|
+
def _encode_image_to_base64(self, image_data: bytes) -> str:
|
|
109
|
+
"""Encode image bytes to base64 string.
|
|
110
|
+
|
|
111
|
+
Args:
|
|
112
|
+
image_data:
|
|
113
|
+
Raw image bytes.
|
|
114
|
+
|
|
115
|
+
Returns:
|
|
116
|
+
Base64 encoded string.
|
|
117
|
+
"""
|
|
118
|
+
return base64.b64encode(image_data).decode("utf-8")
|
|
119
|
+
|
|
120
|
+
def _prepare_images_dict(
|
|
121
|
+
self, images: Dict[str, bytes], encode_base64: bool = False
|
|
122
|
+
) -> Dict[str, Union[bytes, str]]:
|
|
123
|
+
"""Prepare images dictionary for response.
|
|
124
|
+
|
|
125
|
+
Args:
|
|
126
|
+
images:
|
|
127
|
+
Dictionary mapping image names to raw bytes.
|
|
128
|
+
encode_base64:
|
|
129
|
+
If True, encode images as base64 strings.
|
|
130
|
+
|
|
131
|
+
Returns:
|
|
132
|
+
Dictionary with optionally encoded images.
|
|
133
|
+
"""
|
|
134
|
+
if encode_base64:
|
|
135
|
+
return {
|
|
136
|
+
name: self._encode_image_to_base64(data)
|
|
137
|
+
for name, data in images.items()
|
|
138
|
+
}
|
|
139
|
+
return images
|
|
140
|
+
|
|
141
|
+
def chunk_text(
|
|
142
|
+
self,
|
|
143
|
+
text: str,
|
|
144
|
+
chunk_size: int = 1000,
|
|
145
|
+
chunk_overlap: int = 200,
|
|
146
|
+
separator: str = "\n\n",
|
|
147
|
+
) -> list[str]:
|
|
148
|
+
"""Split text into chunks with overlap.
|
|
149
|
+
|
|
150
|
+
Useful for processing long documents that need to be broken into
|
|
151
|
+
smaller pieces for embedding or LLM processing.
|
|
152
|
+
|
|
153
|
+
Args:
|
|
154
|
+
text:
|
|
155
|
+
Text to split into chunks.
|
|
156
|
+
chunk_size:
|
|
157
|
+
Target size for each chunk (in characters).
|
|
158
|
+
chunk_overlap:
|
|
159
|
+
Number of characters to overlap between chunks.
|
|
160
|
+
separator:
|
|
161
|
+
Separator to use for splitting (default: double newline).
|
|
162
|
+
|
|
163
|
+
Returns:
|
|
164
|
+
List of text chunks.
|
|
165
|
+
|
|
166
|
+
Example:
|
|
167
|
+
>>> parser = Parser.pdf("pypdf")
|
|
168
|
+
>>> response = parser("document.pdf")
|
|
169
|
+
>>> chunks = parser.chunk_text(
|
|
170
|
+
... response.data["text"],
|
|
171
|
+
... chunk_size=1000,
|
|
172
|
+
... chunk_overlap=200
|
|
173
|
+
... )
|
|
174
|
+
"""
|
|
175
|
+
if not text:
|
|
176
|
+
return []
|
|
177
|
+
|
|
178
|
+
# Try to split on separator first
|
|
179
|
+
splits = text.split(separator)
|
|
180
|
+
|
|
181
|
+
chunks = []
|
|
182
|
+
current_chunk = []
|
|
183
|
+
current_size = 0
|
|
184
|
+
|
|
185
|
+
for split in splits:
|
|
186
|
+
split_size = len(split)
|
|
187
|
+
|
|
188
|
+
# If single split is larger than chunk_size, split it further
|
|
189
|
+
if split_size > chunk_size:
|
|
190
|
+
# Add current chunk if it exists
|
|
191
|
+
if current_chunk:
|
|
192
|
+
chunks.append(separator.join(current_chunk))
|
|
193
|
+
current_chunk = []
|
|
194
|
+
current_size = 0
|
|
195
|
+
|
|
196
|
+
# Split the large piece by words
|
|
197
|
+
words = split.split()
|
|
198
|
+
word_chunk = []
|
|
199
|
+
word_chunk_size = 0
|
|
200
|
+
|
|
201
|
+
for word in words:
|
|
202
|
+
word_size = len(word) + 1 # +1 for space
|
|
203
|
+
if word_chunk_size + word_size > chunk_size and word_chunk:
|
|
204
|
+
chunks.append(" ".join(word_chunk))
|
|
205
|
+
# Keep overlap
|
|
206
|
+
overlap_words = []
|
|
207
|
+
overlap_size = 0
|
|
208
|
+
for w in reversed(word_chunk):
|
|
209
|
+
if overlap_size + len(w) + 1 <= chunk_overlap:
|
|
210
|
+
overlap_words.insert(0, w)
|
|
211
|
+
overlap_size += len(w) + 1
|
|
212
|
+
else:
|
|
213
|
+
break
|
|
214
|
+
word_chunk = overlap_words
|
|
215
|
+
word_chunk_size = overlap_size
|
|
216
|
+
|
|
217
|
+
word_chunk.append(word)
|
|
218
|
+
word_chunk_size += word_size
|
|
219
|
+
|
|
220
|
+
if word_chunk:
|
|
221
|
+
chunks.append(" ".join(word_chunk))
|
|
222
|
+
|
|
223
|
+
elif current_size + split_size + len(separator) > chunk_size:
|
|
224
|
+
# Current chunk is full, save it
|
|
225
|
+
if current_chunk:
|
|
226
|
+
chunks.append(separator.join(current_chunk))
|
|
227
|
+
|
|
228
|
+
# Start new chunk with overlap from previous
|
|
229
|
+
overlap_text = separator.join(current_chunk)
|
|
230
|
+
if len(overlap_text) > chunk_overlap:
|
|
231
|
+
# Take last chunk_overlap characters
|
|
232
|
+
overlap_text = overlap_text[-chunk_overlap:]
|
|
233
|
+
# Find last separator to avoid cutting mid-sentence
|
|
234
|
+
last_sep = overlap_text.rfind(separator)
|
|
235
|
+
if last_sep > 0:
|
|
236
|
+
overlap_text = overlap_text[last_sep + len(separator) :]
|
|
237
|
+
|
|
238
|
+
current_chunk = [overlap_text, split] if overlap_text else [split]
|
|
239
|
+
else:
|
|
240
|
+
current_chunk = [split]
|
|
241
|
+
|
|
242
|
+
current_size = sum(len(s) for s in current_chunk) + len(separator) * (
|
|
243
|
+
len(current_chunk) - 1
|
|
244
|
+
)
|
|
245
|
+
|
|
246
|
+
else:
|
|
247
|
+
# Add to current chunk
|
|
248
|
+
current_chunk.append(split)
|
|
249
|
+
current_size += split_size + len(separator)
|
|
250
|
+
|
|
251
|
+
# Add final chunk
|
|
252
|
+
if current_chunk:
|
|
253
|
+
chunks.append(separator.join(current_chunk))
|
|
254
|
+
|
|
255
|
+
return chunks
|
|
256
|
+
|
|
257
|
+
def chunk_by_tokens(
|
|
258
|
+
self,
|
|
259
|
+
text: str,
|
|
260
|
+
max_tokens: int = 512,
|
|
261
|
+
overlap_tokens: int = 50,
|
|
262
|
+
encoding_name: str = "cl100k_base",
|
|
263
|
+
) -> list[str]:
|
|
264
|
+
"""Split text into chunks based on token count.
|
|
265
|
+
|
|
266
|
+
Uses tiktoken library for accurate token counting.
|
|
267
|
+
Falls back to character-based chunking if tiktoken not available.
|
|
268
|
+
|
|
269
|
+
Args:
|
|
270
|
+
text:
|
|
271
|
+
Text to split into chunks.
|
|
272
|
+
max_tokens:
|
|
273
|
+
Maximum tokens per chunk.
|
|
274
|
+
overlap_tokens:
|
|
275
|
+
Number of tokens to overlap between chunks.
|
|
276
|
+
encoding_name:
|
|
277
|
+
Tiktoken encoding name (default: cl100k_base for GPT-4).
|
|
278
|
+
|
|
279
|
+
Returns:
|
|
280
|
+
List of text chunks.
|
|
281
|
+
|
|
282
|
+
Example:
|
|
283
|
+
>>> parser = Parser.pdf("pypdf")
|
|
284
|
+
>>> response = parser("document.pdf")
|
|
285
|
+
>>> chunks = parser.chunk_by_tokens(
|
|
286
|
+
... response.data["text"],
|
|
287
|
+
... max_tokens=512,
|
|
288
|
+
... overlap_tokens=50
|
|
289
|
+
... )
|
|
290
|
+
"""
|
|
291
|
+
try:
|
|
292
|
+
import tiktoken
|
|
293
|
+
|
|
294
|
+
encoding = tiktoken.get_encoding(encoding_name)
|
|
295
|
+
|
|
296
|
+
# Tokenize the text
|
|
297
|
+
tokens = encoding.encode(text)
|
|
298
|
+
|
|
299
|
+
if len(tokens) <= max_tokens:
|
|
300
|
+
return [text]
|
|
301
|
+
|
|
302
|
+
chunks = []
|
|
303
|
+
start = 0
|
|
304
|
+
|
|
305
|
+
while start < len(tokens):
|
|
306
|
+
# Get chunk tokens
|
|
307
|
+
end = start + max_tokens
|
|
308
|
+
chunk_tokens = tokens[start:end]
|
|
309
|
+
|
|
310
|
+
# Decode back to text
|
|
311
|
+
chunk_text = encoding.decode(chunk_tokens)
|
|
312
|
+
chunks.append(chunk_text)
|
|
313
|
+
|
|
314
|
+
# Move start position (with overlap)
|
|
315
|
+
start = end - overlap_tokens
|
|
316
|
+
|
|
317
|
+
return chunks
|
|
318
|
+
|
|
319
|
+
except ImportError:
|
|
320
|
+
# Fallback: estimate 1 token ≈ 4 characters
|
|
321
|
+
char_chunk_size = max_tokens * 4
|
|
322
|
+
char_overlap = overlap_tokens * 4
|
|
323
|
+
return self.chunk_text(
|
|
324
|
+
text, chunk_size=char_chunk_size, chunk_overlap=char_overlap
|
|
325
|
+
)
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
from typing import TYPE_CHECKING, Any, Mapping
|
|
2
|
+
|
|
3
|
+
from msgflux.data.parsers.registry import parser_registry
|
|
4
|
+
|
|
5
|
+
if TYPE_CHECKING:
|
|
6
|
+
from msgflux.data.parsers.base import BaseParser
|
|
7
|
+
|
|
8
|
+
from msgflux.data.parsers.types import (
|
|
9
|
+
CsvParser,
|
|
10
|
+
DocxParser,
|
|
11
|
+
EmailParser,
|
|
12
|
+
HtmlParser,
|
|
13
|
+
MarkdownParser,
|
|
14
|
+
PdfParser,
|
|
15
|
+
PptxParser,
|
|
16
|
+
XlsxParser,
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class Parser:
|
|
21
|
+
@classmethod
|
|
22
|
+
def providers(cls):
|
|
23
|
+
return {k: list(v.keys()) for k, v in parser_registry.items()}
|
|
24
|
+
|
|
25
|
+
@classmethod
|
|
26
|
+
def parser_types(cls):
|
|
27
|
+
return list(parser_registry.keys())
|
|
28
|
+
|
|
29
|
+
@classmethod
|
|
30
|
+
def _get_parser_class(cls, parser_type: str, provider: str) -> type["BaseParser"]:
|
|
31
|
+
if parser_type not in parser_registry:
|
|
32
|
+
raise ValueError(f"Parser type `{parser_type}` is not supported")
|
|
33
|
+
if provider not in parser_registry[parser_type]:
|
|
34
|
+
raise ValueError(
|
|
35
|
+
f"Provider `{provider}` not registered for type `{parser_type}`"
|
|
36
|
+
)
|
|
37
|
+
parser_cls = parser_registry[parser_type][provider]
|
|
38
|
+
return parser_cls
|
|
39
|
+
|
|
40
|
+
@classmethod
|
|
41
|
+
def _create_parser(
|
|
42
|
+
cls, parser_type: str, provider: str, **kwargs
|
|
43
|
+
) -> type["BaseParser"]:
|
|
44
|
+
parser_cls = cls._get_parser_class(parser_type, provider)
|
|
45
|
+
return parser_cls(**kwargs)
|
|
46
|
+
|
|
47
|
+
@classmethod
|
|
48
|
+
def from_serialized(
|
|
49
|
+
cls, provider: str, parser_type: str, params: Mapping[str, Any]
|
|
50
|
+
) -> type["BaseParser"]:
|
|
51
|
+
"""Creates a parser instance from serialized parameters.
|
|
52
|
+
|
|
53
|
+
Args:
|
|
54
|
+
provider:
|
|
55
|
+
The parser provider (e.g., "pypdf").
|
|
56
|
+
parser_type:
|
|
57
|
+
The type of parser (e.g., "pdf", "pptx").
|
|
58
|
+
params:
|
|
59
|
+
Dictionary containing the serialized parser parameters.
|
|
60
|
+
|
|
61
|
+
Returns:
|
|
62
|
+
An instance of the appropriate parser class with restored state.
|
|
63
|
+
"""
|
|
64
|
+
parser_cls = cls._get_parser_class(parser_type, provider)
|
|
65
|
+
# Create instance without calling __init__
|
|
66
|
+
instance = object.__new__(parser_cls)
|
|
67
|
+
# Restore the instance state
|
|
68
|
+
instance.from_serialized(params)
|
|
69
|
+
return instance
|
|
70
|
+
|
|
71
|
+
@classmethod
|
|
72
|
+
def pdf(cls, provider: str, **kwargs) -> PdfParser:
|
|
73
|
+
return cls._create_parser("pdf", provider, **kwargs)
|
|
74
|
+
|
|
75
|
+
@classmethod
|
|
76
|
+
def pptx(cls, provider: str, **kwargs) -> PptxParser:
|
|
77
|
+
return cls._create_parser("pptx", provider, **kwargs)
|
|
78
|
+
|
|
79
|
+
@classmethod
|
|
80
|
+
def xlsx(cls, provider: str, **kwargs) -> XlsxParser:
|
|
81
|
+
return cls._create_parser("xlsx", provider, **kwargs)
|
|
82
|
+
|
|
83
|
+
@classmethod
|
|
84
|
+
def docx(cls, provider: str, **kwargs) -> DocxParser:
|
|
85
|
+
return cls._create_parser("docx", provider, **kwargs)
|
|
86
|
+
|
|
87
|
+
@classmethod
|
|
88
|
+
def csv(cls, provider: str, **kwargs) -> CsvParser:
|
|
89
|
+
return cls._create_parser("csv", provider, **kwargs)
|
|
90
|
+
|
|
91
|
+
@classmethod
|
|
92
|
+
def html(cls, provider: str, **kwargs) -> HtmlParser:
|
|
93
|
+
return cls._create_parser("html", provider, **kwargs)
|
|
94
|
+
|
|
95
|
+
@classmethod
|
|
96
|
+
def markdown(cls, provider: str, **kwargs) -> MarkdownParser:
|
|
97
|
+
return cls._create_parser("markdown", provider, **kwargs)
|
|
98
|
+
|
|
99
|
+
@classmethod
|
|
100
|
+
def email(cls, provider: str, **kwargs) -> EmailParser:
|
|
101
|
+
return cls._create_parser("email", provider, **kwargs)
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
"""Parser providers package.
|
|
2
|
+
|
|
3
|
+
This package contains implementations of parsers for different file formats.
|
|
4
|
+
Each parser provider follows the standard msgflux parser interface.
|
|
5
|
+
|
|
6
|
+
Available parsers:
|
|
7
|
+
- PyPDFPdfParser: PDF parsing using pypdf
|
|
8
|
+
- OpenPyxlXlsxParser: Excel parsing using openpyxl
|
|
9
|
+
- PythonPptxPptxParser: PowerPoint parsing using python-pptx
|
|
10
|
+
- PythonDocxDocxParser: Word document parsing using python-docx
|
|
11
|
+
- StandardCsvParser: CSV/TSV parsing using Python csv module
|
|
12
|
+
- BeautifulSoupHtmlParser: HTML parsing using BeautifulSoup4
|
|
13
|
+
- StandardEmailParser: Email (.eml) parsing using Python email module
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
from msgflux.data.parsers.providers.csv_parser import StandardCsvParser
|
|
17
|
+
from msgflux.data.parsers.providers.email_parser import StandardEmailParser
|
|
18
|
+
from msgflux.data.parsers.providers.html_parser import BeautifulSoupHtmlParser
|
|
19
|
+
from msgflux.data.parsers.providers.openpyxl import OpenPyxlXlsxParser
|
|
20
|
+
from msgflux.data.parsers.providers.pypdf import PyPDFPdfParser
|
|
21
|
+
from msgflux.data.parsers.providers.python_docx import PythonDocxDocxParser
|
|
22
|
+
from msgflux.data.parsers.providers.python_pptx import PythonPptxPptxParser
|
|
23
|
+
|
|
24
|
+
__all__ = [
|
|
25
|
+
"BeautifulSoupHtmlParser",
|
|
26
|
+
"OpenPyxlXlsxParser",
|
|
27
|
+
"PyPDFPdfParser",
|
|
28
|
+
"PythonDocxDocxParser",
|
|
29
|
+
"PythonPptxPptxParser",
|
|
30
|
+
"StandardCsvParser",
|
|
31
|
+
"StandardEmailParser",
|
|
32
|
+
]
|