sectionminer 0.1.1__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.
@@ -0,0 +1,22 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 SectionMiner Contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
22
+
@@ -0,0 +1,385 @@
1
+ Metadata-Version: 2.4
2
+ Name: sectionminer
3
+ Version: 0.1.1
4
+ Summary: Extract sections and subsections from academic PDFs
5
+ Author: SectionMiner Contributors
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/ehodiogo/SectionMiner
8
+ Project-URL: Repository, https://github.com/ehodiogo/SectionMiner
9
+ Keywords: pdf,nlp,llm,sections,academic
10
+ Classifier: Development Status :: 3 - Alpha
11
+ Classifier: Intended Audience :: Developers
12
+ Classifier: Programming Language :: Python :: 3
13
+ Classifier: Programming Language :: Python :: 3.10
14
+ Classifier: Programming Language :: Python :: 3.11
15
+ Classifier: Programming Language :: Python :: 3.12
16
+ Classifier: Programming Language :: Python :: 3.13
17
+ Classifier: Topic :: Text Processing
18
+ Requires-Python: >=3.10
19
+ Description-Content-Type: text/markdown
20
+ License-File: LICENSE
21
+ Requires-Dist: pymupdf
22
+ Requires-Dist: langchain
23
+ Requires-Dist: langchain-openai
24
+ Requires-Dist: langchain-text-splitters
25
+ Requires-Dist: langchain-community
26
+ Requires-Dist: python-decouple
27
+ Requires-Dist: google-generativeai
28
+ Dynamic: license-file
29
+
30
+ # SectionMiner
31
+
32
+ Biblioteca Python para extrair secoes e subsecoes de PDFs academicos com heuristicas de layout + consolidacao por LLM.
33
+
34
+ Suporta dois backends de extracao de texto:
35
+
36
+ - `pymupdf` (local, via spans/layout do PDF)
37
+ - `gemini` (OCR/extracao via Google Gemini)
38
+
39
+ Em ambos os casos, a consolidacao final da arvore de secoes ainda e feita por OpenAI no estado atual do projeto.
40
+
41
+ ## Visao geral
42
+
43
+ O fluxo do projeto e:
44
+
45
+ 1. Ler spans do PDF com fonte/tamanho (`PyMuPDF`).
46
+ 2. Detectar titulos provaveis (heading).
47
+ 3. Montar secoes com intervalos de caracteres (`start`, `end`).
48
+ 4. Enviar um indice de headings para LLM consolidar a arvore final.
49
+ 5. Buscar texto de uma secao pelo titulo.
50
+
51
+ ## Requisitos
52
+
53
+ - Python 3.10+
54
+ - Dependencias em `requirements.txt`
55
+ - Chaves de API:
56
+ - `OPENAI_API_KEY` (obrigatoria para consolidacao LLM)
57
+ - `GEMINI_API_KEY` (obrigatoria quando usar `extraction_backend="gemini"`)
58
+
59
+ ## Instalacao (modo biblioteca)
60
+
61
+ ```bash
62
+ git clone https://github.com/ehodiogo/SectionMiner.git
63
+ cd SectionMiner
64
+ python3 -m venv .venv
65
+ source .venv/bin/activate
66
+ pip install -r requirements.txt
67
+ pip install -e .
68
+ ```
69
+
70
+ Depois disso, voce pode importar com `from sectionminer import SectionMiner` em qualquer script do ambiente.
71
+
72
+ Tambem instala a CLI `sectionminer`.
73
+
74
+ Instalacao direta do PyPI (apos publicacao):
75
+
76
+ ```bash
77
+ pip install sectionminer
78
+ ```
79
+
80
+ ## Configuracao das chaves
81
+
82
+ Os scripts usam `python-decouple` para ler variaveis de ambiente.
83
+
84
+ Opcao 1 (rapida, terminal atual):
85
+
86
+ ```bash
87
+ export OPENAI_API_KEY="sua-chave-aqui"
88
+ export GEMINI_API_KEY="sua-chave-aqui"
89
+ ```
90
+
91
+ Opcao 2 (`.env` na raiz do projeto):
92
+
93
+ ```env
94
+ OPENAI_API_KEY=sua-chave-aqui
95
+ GEMINI_API_KEY=sua-chave-aqui
96
+ ```
97
+
98
+ Se voce nao for usar Gemini, pode omitir `GEMINI_API_KEY`.
99
+
100
+ ## Backends de extracao
101
+
102
+ - `pymupdf` (padrao): extrai texto a partir do layout interno do PDF.
103
+ - `gemini`: envia o PDF para o Gemini e usa o texto retornado para o pipeline.
104
+
105
+ Exemplo de construtor com Gemini:
106
+
107
+ ```python
108
+ miner = SectionMiner(
109
+ "files/Artigo_Provatis.pdf",
110
+ api_key=openai_api_key,
111
+ extraction_backend="gemini",
112
+ gemini_api_key=gemini_api_key,
113
+ gemini_model="gemini-2.5-flash-lite",
114
+ )
115
+ ```
116
+
117
+ ## Fluxo principal (o que o `test.py` faz)
118
+
119
+ Arquivo: `test.py` (exemplo usando a biblioteca)
120
+
121
+ 1. Le a chave com `config("OPENAI_API_KEY")`.
122
+ 2. Cria `SectionMiner("files/Artigo_Provatis.pdf", api_key)`.
123
+ 3. Executa `extract_structure(return_tokens=True)` para obter:
124
+ - arvore de secoes/subsecoes
125
+ - uso de tokens/custo
126
+ 4. Consulta uma secao (ex.: `introducao`) por offsets com `get_section_start_and_end_chars`.
127
+ 5. Recupera texto completo com `get_full_text()` e fatia por `[start:end]`.
128
+ 6. Reexecuta pipeline manual (`extract_blocks`, `build_full_text`, `build_sections`) e imprime secoes com `get_sections()` e `get_section_text()`.
129
+ 7. Fecha o PDF com `close()` no `finally`.
130
+
131
+ Executar:
132
+
133
+ ```bash
134
+ python3 test.py
135
+ ```
136
+
137
+ Exemplo alternativo (arquivo dedicado em `examples/`):
138
+
139
+ ```bash
140
+ python3 examples/basic_usage.py
141
+ ```
142
+
143
+ ## Fluxo com Gemini (o que o `test_gemini.py` faz)
144
+
145
+ Arquivo: `test_gemini.py`
146
+
147
+ 1. Le `OPENAI_API_KEY` e `GEMINI_API_KEY`.
148
+ 2. Cria `SectionMiner(..., extraction_backend="gemini", gemini_api_key=...)`.
149
+ 3. Executa `extract_structure(return_tokens=True)` e imprime `usage`.
150
+ 4. Consulta secao por offsets com `get_section_start_and_end_chars`.
151
+ 5. Lista secoes com `get_sections()` e imprime com `get_section_text()`.
152
+
153
+ Executar:
154
+
155
+ ```bash
156
+ python3 test_gemini.py
157
+ ```
158
+
159
+ ## CLI inicial
160
+
161
+ Comando raiz:
162
+
163
+ ```bash
164
+ sectionminer --help
165
+ ```
166
+
167
+ Observacao importante: a CLI atual usa backend `pymupdf` (ou `--heuristic-only`) e nao expoe flag para `extraction_backend="gemini"` ainda.
168
+ Para Gemini, use a API Python (`test_gemini.py` como referencia).
169
+
170
+ Extrair estrutura (com LLM):
171
+
172
+ ```bash
173
+ sectionminer extract files/Artigo_Provatis.pdf --tokens --pretty
174
+ ```
175
+
176
+ Extrair estrutura e mostrar custo total da chamada:
177
+
178
+ ```bash
179
+ sectionminer extract files/Artigo_Provatis.pdf --show-cost --pretty
180
+ ```
181
+
182
+ Extrair estrutura heuristica (sem LLM/OpenAI):
183
+
184
+ ```bash
185
+ sectionminer extract files/Artigo_Provatis.pdf --heuristic-only --pretty
186
+ ```
187
+
188
+ Salvar saida JSON em arquivo:
189
+
190
+ ```bash
191
+ sectionminer extract files/Artigo_Provatis.pdf --heuristic-only --output out.json --pretty
192
+ ```
193
+
194
+ Buscar texto de secao por titulo:
195
+
196
+ ```bash
197
+ sectionminer section-text files/Artigo_Provatis.pdf "introducao"
198
+ ```
199
+
200
+ Buscar texto e mostrar custo total da chamada:
201
+
202
+ ```bash
203
+ sectionminer section-text files/Artigo_Provatis.pdf "introducao" --show-cost
204
+ ```
205
+
206
+ Buscar texto de secao sem LLM (heuristica):
207
+
208
+ ```bash
209
+ sectionminer section-text files/Artigo_Provatis.pdf "introducao" --heuristic-only
210
+ ```
211
+
212
+ Observacao: `--show-cost` imprime o resumo de custo no `stderr` (nao polui JSON de saida).
213
+
214
+ ## Exemplos de custo (extracao + obtencao dos textos)
215
+
216
+ Base de medicao local em `2026-03-21`, com modelo `gpt-4o-mini`, usando os PDFs em `files/`.
217
+
218
+ - `files/Artigo_Provatis.pdf`: 0.736 MB, 21 paginas
219
+ - Extracao da estrutura: 2297 tokens, `US$ 0.00047505`
220
+ - Obtencao dos textos das secoes no mesmo processo: `US$ 0.00` adicional (usa offsets locais)
221
+ - `files/Artigo_Mae.pdf`: 0.036 MB, 4 paginas
222
+ - Extracao da estrutura: 356 tokens, `US$ 0.00005970`
223
+ - Obtencao dos textos das secoes no mesmo processo: `US$ 0.00` adicional
224
+
225
+ Comando para reproduzir no seu ambiente:
226
+
227
+ ```bash
228
+ sectionminer extract files/Artigo_Provatis.pdf --show-cost --pretty
229
+ sectionminer section-text files/Artigo_Provatis.pdf "introducao" --show-cost
230
+ ```
231
+
232
+ ## Funcoes principais da API (`SectionMiner`)
233
+
234
+ Arquivo: `sectionminer/miner.py`
235
+
236
+ - `extract_structure(return_tokens=False)`
237
+ - Pipeline completo (extracao, deteccao, merge com LLM).
238
+ - Retorna a arvore final; com `return_tokens=True`, retorna `(arvore, usage)`.
239
+
240
+ - `get_section_start_and_end_chars(title)`
241
+ - Retorna `(start, end)` da secao localizada por titulo.
242
+ - Bom para recortar diretamente em `get_full_text()`.
243
+
244
+ - `get_full_text()`
245
+ - Retorna o texto linear completo do PDF processado.
246
+
247
+ - `get_section_text(title)`
248
+ - Busca no tree consolidado e devolve o texto da secao.
249
+
250
+ - `get_sections()`
251
+ - Retorna lista de titulos detectados a partir das estruturas internas.
252
+
253
+ - `extract_blocks()`, `build_full_text()`, `build_sections()`
254
+ - Etapas internas do pipeline usadas no `test.py` para depuracao/inspecao.
255
+
256
+ - `close()`
257
+ - Fecha o documento PDF aberto em memoria.
258
+
259
+ ## Exemplo minimo (mesma ideia do teste)
260
+
261
+ ```python
262
+ import json
263
+ from decouple import config
264
+ from sectionminer import SectionMiner
265
+
266
+ api_key = config("OPENAI_API_KEY")
267
+ miner = SectionMiner("files/Artigo_Provatis.pdf", api_key)
268
+
269
+ try:
270
+ structure, tokens = miner.extract_structure(return_tokens=True)
271
+ print(tokens)
272
+ print(json.dumps(structure, indent=2, ensure_ascii=False))
273
+
274
+ start, end = miner.get_section_start_and_end_chars("introducao")
275
+ if start is not None and end is not None:
276
+ print(miner.get_full_text()[start:end][:800])
277
+
278
+ print(miner.get_section_text("conclusao"))
279
+ finally:
280
+ miner.close()
281
+ ```
282
+
283
+ Exemplo minimo com Gemini:
284
+
285
+ ```python
286
+ from decouple import config
287
+ from sectionminer import SectionMiner
288
+
289
+ openai_api_key = config("OPENAI_API_KEY")
290
+ gemini_api_key = config("GEMINI_API_KEY")
291
+
292
+ miner = SectionMiner(
293
+ "files/Artigo_Provatis.pdf",
294
+ api_key=openai_api_key,
295
+ extraction_backend="gemini",
296
+ gemini_api_key=gemini_api_key,
297
+ )
298
+
299
+ try:
300
+ structure, usage = miner.extract_structure(return_tokens=True)
301
+ print(usage)
302
+ print(structure.get("title"))
303
+ finally:
304
+ miner.close()
305
+ ```
306
+
307
+ ## Estrutura do projeto
308
+
309
+ ```text
310
+ SectionMiner/
311
+ sectionminer/
312
+ __init__.py # API publica da biblioteca
313
+ miner.py # classe SectionMiner
314
+ client.py # cliente LLM e merge da arvore
315
+ prompts.py # prompt de consolidacao
316
+ base.py # compatibilidade com import legado
317
+ client.py # compatibilidade com import legado
318
+ prompts.py # compatibilidade com import legado
319
+ test.py # fluxo de uso principal
320
+ test_gemini.py # fluxo com backend Gemini
321
+ examples/ # exemplos prontos de execucao
322
+ files/ # PDFs de exemplo
323
+ ```
324
+
325
+ ## Problemas comuns
326
+
327
+ ### 1) "As secoes estao vindo quebradas"
328
+
329
+ - Revise filtros em `_is_noise_heading` e `_looks_like_heading` em `sectionminer/miner.py`.
330
+ - Ajuste threshold em `_detect_threshold` para o padrao do seu PDF.
331
+ - PDFs com layout irregular (duas colunas, rodape intrusivo, OCR ruim) tendem a piorar a deteccao.
332
+
333
+ ### 2) Secao nao encontrada por titulo
334
+
335
+ - Tente variacao sem acento/caixa (a busca normaliza texto).
336
+ - Verifique os titulos retornados por `get_sections()`.
337
+
338
+ ### 3) Erro de chave OpenAI
339
+
340
+ - Confirme `OPENAI_API_KEY` no mesmo ambiente da execucao.
341
+ - Se usar `.env`, confirme que esta na raiz do projeto.
342
+
343
+ ## TODO (coisas a fazer)
344
+
345
+ - [ ] Criar testes automatizados para `detect_headings`, `build_sections` e `get_section_text`.
346
+ - [ ] Adicionar modo sem LLM (somente heuristica local) para uso offline.
347
+ - [x] Criar CLI inicial: `sectionminer extract arquivo.pdf --output out.json`.
348
+ - [ ] Expor parametros de heuristica por configuracao (threshold, filtros de ruido).
349
+ - [ ] Melhorar merge para manter apenas secoes/subsecoes validas (sem fragmentos quebrados).
350
+
351
+ ## Publicacao (preparo inicial)
352
+
353
+ O projeto ja possui `pyproject.toml`, `LICENSE` e entrypoint de CLI.
354
+
355
+ Gerar artefatos:
356
+
357
+ ```bash
358
+ python3 -m pip install --upgrade build twine
359
+ python3 -m build
360
+ python3 -m twine check dist/*
361
+ ```
362
+
363
+ Teste local do wheel:
364
+
365
+ ```bash
366
+ python3 -m pip install dist/*.whl
367
+ sectionminer --help
368
+ ```
369
+
370
+ Publicar (TestPyPI primeiro, recomendado):
371
+
372
+ ```bash
373
+ python3 -m twine upload --repository testpypi dist/*
374
+ ```
375
+
376
+ Publicar no PyPI oficial:
377
+
378
+ ```bash
379
+ python3 -m twine upload dist/*
380
+ ```
381
+
382
+ ## Licenca
383
+
384
+ MIT (arquivo `LICENSE`).
385
+