HowdenParser 0.1.8__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.
@@ -0,0 +1,3 @@
1
+ from .parser import ParserFactory
2
+
3
+ __all__ = ["ParserFactory"]
@@ -0,0 +1,15 @@
1
+ import os
2
+ import importlib
3
+
4
+ # Get all .py files in this folder except __init__.py
5
+ current_dir = os.path.dirname(__file__)
6
+ for filename in os.listdir(current_dir):
7
+ if filename.endswith(".py") and filename != "__init__.py":
8
+ module_name = filename[:-3] # remove .py
9
+ module = importlib.import_module(f".{module_name}", package=__name__)
10
+
11
+ # Add all classes from the module to the package namespace
12
+ for attr_name in dir(module):
13
+ attr = getattr(module, attr_name)
14
+ if isinstance(attr, type): # only classes
15
+ globals()[attr_name] = attr
@@ -0,0 +1,6 @@
1
+ from typing import Literal
2
+ from pydantic import BaseModel
3
+
4
+ class Parameter(BaseModel):
5
+ model: str = "huggingface:HURIDOCS/pdf-segmentation"
6
+
@@ -0,0 +1,8 @@
1
+
2
+ from typing import Literal
3
+ from pydantic import BaseModel
4
+
5
+ class Parameter(BaseModel):
6
+ model: str = "llamaparser:"
7
+ result_type: Literal["md"] = "md"
8
+ mode: bool = False
@@ -0,0 +1,7 @@
1
+ from typing import Literal
2
+ from pydantic import BaseModel
3
+
4
+ class Parameter(BaseModel):
5
+ model: str = "mistralocr:"
6
+ result_type: Literal["md"] = "md"
7
+ mode: bool = False
HowdenParser/parser.py ADDED
@@ -0,0 +1,137 @@
1
+ from abc import ABC, abstractmethod
2
+ from dotenv import load_dotenv
3
+ import os
4
+ from pathlib import Path
5
+
6
+ load_dotenv()
7
+
8
+ class BaseParser(ABC):
9
+ @abstractmethod
10
+ def parse(self, text: str) -> dict:
11
+ pass
12
+
13
+
14
+ class MistralOCRParser(BaseParser):
15
+ def __init__(self, result_type: str, **kwargs) -> None:
16
+ from mistralai import Mistral
17
+ name = "MISTRAL-OCR-API-TOKEN"
18
+ api_key = os.getenv(name)
19
+ if not api_key:
20
+ raise EnvironmentError(f"Missing {name} in .env file.")
21
+
22
+ self.result_type = "md" if result_type.lower() in ("md", "markdown") else "text"
23
+ self.client = Mistral(api_key=api_key)
24
+
25
+ def parse(self, file_path: Path) -> str:
26
+ def upload_pdf(filename):
27
+ uploaded_pdf = self.client.files.upload(
28
+ file={
29
+ "file_name": filename,
30
+ "content": open(filename, "rb"),
31
+ },
32
+ purpose="ocr"
33
+ )
34
+ signed_url = self.client.files.get_signed_url(file_id=uploaded_pdf.id)
35
+ return signed_url.url
36
+
37
+ ocr_response = self.client.ocr.process(
38
+ model="mistral-ocr-latest",
39
+ document={
40
+ "type": "document_url",
41
+ "document_url": upload_pdf(file_path),
42
+ },
43
+ include_image_base64=True,
44
+ )
45
+
46
+ return "\n".join(doc.markdown for doc in ocr_response.pages)
47
+
48
+
49
+ class LangChainParser(BaseParser):
50
+ def __init__(self, model_name: str):
51
+ from langchain.llms import OpenAI
52
+ self.model_name = model_name
53
+ self.model = OpenAI(model_name=model_name)
54
+
55
+ def parse(self, text: str) -> dict:
56
+ response = self.model(text)
57
+ return {"source": "LangChain", "output": response}
58
+
59
+
60
+ class LlamaParser(BaseParser):
61
+ def __init__(self, result_type: str, mode: bool, **kwargs) -> None:
62
+ from llama_parse import LlamaParse, ResultType
63
+ if result_type.lower() == "md" or result_type.lower() == "markdown":
64
+ self.result_type = ResultType.MD
65
+ name = "LLAMA-PARSER-API-TOKEN"
66
+ self.api_key = os.getenv(name)
67
+ if not self.api_key:
68
+ raise EnvironmentError(f"Missing {name} in .env file.")
69
+
70
+ self.parser = LlamaParse(
71
+ api_key=self.api_key,
72
+ result_type=self.result_type,
73
+ premium_mode=mode
74
+ )
75
+
76
+ def parse(self, file_path: Path) -> str:
77
+ documents = self.parser.load_data(str(file_path))
78
+ return "\n".join(doc.text for doc in documents)
79
+
80
+ class HuggingFaceParser(BaseParser):
81
+ def __init__(self, result_type: str, mode: bool, **kwargs) -> None:
82
+ from transformers import pipeline
83
+
84
+ if result_type.lower() in ("md", "markdown"):
85
+ self.result_type = "markdown"
86
+ else:
87
+ self.result_type = "text"
88
+
89
+ name = "HF-API-TOKEN"
90
+ self.api_key = os.getenv(name)
91
+ if not self.api_key:
92
+ raise EnvironmentError(f"Missing {name} in .env file.")
93
+
94
+ # OCR + text extraction pipeline
95
+ # Example model: microsoft/layoutlmv3-base-finetuned-docvqa
96
+ self.parser = pipeline(
97
+ task="document-question-answering",
98
+ model="impira/layoutlm-document-qa",
99
+ use_auth_token=self.api_key
100
+ )
101
+
102
+ def parse(self, file_path: Path) -> str:
103
+ import fitz # PyMuPDF for PDF → images
104
+
105
+ pdf_doc = fitz.open(file_path)
106
+ output_parts = []
107
+
108
+ for page in pdf_doc:
109
+ pix = page.get_pixmap(dpi=200)
110
+ img_bytes = pix.tobytes("png")
111
+ response = self.parser(img_bytes, question="Extract all text")
112
+ if response and "answer" in response[0]:
113
+ output_parts.append(response[0]["answer"])
114
+
115
+ if self.result_type == "markdown":
116
+ # Here you could add rules to format into markdown
117
+ return "\n\n".join(output_parts)
118
+ else:
119
+ return " ".join(output_parts)
120
+
121
+ # === Step 3: Dynamic factory using string input ===
122
+ class ParserFactory:
123
+ @staticmethod
124
+ def get_parser(parser_type: str, **kwargs) -> BaseParser:
125
+ provider = parser_type.partition(":")[0].lower()
126
+ model = parser_type.partition(":")[2]
127
+ if provider == "langchain":
128
+ return LangChainParser(model_name=model)
129
+ elif provider == "llamaparser":
130
+ return LlamaParser(**kwargs)
131
+ elif provider == "huggingface":
132
+ return HuggingFaceParser(model_name=model, **kwargs)
133
+ elif provider == "mistralocr":
134
+ return MistralOCRParser(**kwargs)
135
+ else:
136
+ raise ValueError(f"Unknown parser type: {parser_type}")
137
+
@@ -0,0 +1,83 @@
1
+ Metadata-Version: 2.3
2
+ Name: HowdenParser
3
+ Version: 0.1.8
4
+ Summary: A simple configuration manager with Pydantic and JSON export.
5
+ License: MIT
6
+ Keywords: config,configuration,pydantic,json
7
+ Author: JesperThoftIllemannJ
8
+ Author-email: jesper.jaeger@howdendanmark.dk
9
+ Classifier: License :: OSI Approved :: MIT License
10
+ Classifier: Programming Language :: Python :: 2
11
+ Classifier: Programming Language :: Python :: 2.7
12
+ Classifier: Programming Language :: Python :: 3
13
+ Classifier: Programming Language :: Python :: 3.4
14
+ Classifier: Programming Language :: Python :: 3.5
15
+ Classifier: Programming Language :: Python :: 3.6
16
+ Classifier: Programming Language :: Python :: 3.7
17
+ Classifier: Programming Language :: Python :: 3.8
18
+ Classifier: Programming Language :: Python :: 3.9
19
+ Classifier: Programming Language :: Python :: 3.10
20
+ Classifier: Programming Language :: Python :: 3.11
21
+ Classifier: Programming Language :: Python :: 3.12
22
+ Classifier: Programming Language :: Python :: 3.13
23
+ Project-URL: Documentation, https://github.com/yourusername/config
24
+ Project-URL: Homepage, https://github.com/yourusername/config
25
+ Project-URL: Repository, https://github.com/yourusername/config
26
+ Description-Content-Type: text/markdown
27
+
28
+ # OCR & LLM Parser
29
+
30
+ A powerful Python package for parsing and processing documents using multiple providers:
31
+ - **Mistral OCR** — Extracts text from PDFs and images with high accuracy.
32
+ - **LangChain** — Processes or summarizes text using LLMs.
33
+ - **Llama Parser** — Advanced parsing with Markdown or text output.
34
+ - **HuggingFace** — OCR and document question answering with transformer models.
35
+
36
+ The package provides a **unified interface** so you can switch between providers easily using a **factory pattern**.
37
+
38
+ ---
39
+
40
+ ## 🚀 Features
41
+ - Extract text from PDFs or images
42
+ - Summarize or process text using LLMs
43
+ - Support for **Markdown** or **plain text** output
44
+ - Plug-and-play factory to switch providers without changing much code
45
+ - Handles environment variable loading for API keys automatically
46
+
47
+ ---
48
+
49
+ # 🔑 Tokens
50
+
51
+ Create a .env file in your project root and add the API keys for the services you want to use.
52
+
53
+ ### Mistral OCR
54
+ MISTRAL-OCR-API-TOKEN=your_mistral_api_key
55
+
56
+ ### Llama Parser
57
+ LLAMA-PARSER-API-TOKEN=your_llama_parser_api_key
58
+
59
+ ### HuggingFace
60
+ HF-API-TOKEN=your_huggingface_api_key
61
+
62
+ Only include the keys for the providers you plan to use.
63
+
64
+ ---
65
+
66
+ # 🛠️ Usage
67
+
68
+ from HowdenParser import ParserFactory
69
+
70
+ from pathlib import Path
71
+
72
+ parser = ParserFactory.get_parser("mistralocr:", result_type="md")
73
+ text = parser.parse(Path("document.pdf"))
74
+ print(text)
75
+
76
+ if HowdenConfig package being used
77
+
78
+
79
+ parser = ParserFactory.get_parser("mistralocr:", **config.parameter.dump_model())
80
+
81
+ text = parser.parse(Path("document.pdf"))
82
+
83
+
@@ -0,0 +1,9 @@
1
+ HowdenParser/__init__.py,sha256=y8Nntk25CoqKQXzlujosuOdEJtujaC2Ui-QCEn8doik,64
2
+ HowdenParser/parameter/__init__.py,sha256=KMPnWvqWoTjibBUo9Pxg9mgyhUDWrlkyKZ0ZQG86Rqo,618
3
+ HowdenParser/parameter/huggingface.py,sha256=aYT3fvnegkgYiayPbjC1fDFz86T2OeyHMZisKwZS5gg,151
4
+ HowdenParser/parameter/llamaparser.py,sha256=RTtNmTiBK2i5Y3ikGOR3J6RSoELtxlcWNx9FXijm7pM,187
5
+ HowdenParser/parameter/mistralocr.py,sha256=e7lgZrcvfloS6Df5_3DnWNU8MVOyl_VEHTaeox8WOfM,184
6
+ HowdenParser/parser.py,sha256=rwlqT0Ve27GSUPYFB65bkkRKMkNk2aNbMqfjYp1gbX8,4778
7
+ howdenparser-0.1.8.dist-info/METADATA,sha256=duOw1Br-viPqAbkDzapNl-2brcELXziKwr2mYvYP8xQ,2730
8
+ howdenparser-0.1.8.dist-info/WHEEL,sha256=9jVypgI8LevED4l0j3YDHs5IZdbkcE7aWw9FT9n2UzI,92
9
+ howdenparser-0.1.8.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: poetry-core 2.1.2
3
+ Root-Is-Purelib: true
4
+ Tag: py2.py3-none-any