docqwise 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.
Files changed (63) hide show
  1. docqwise/__init__.py +9 -0
  2. docqwise/_version.py +1 -0
  3. docqwise/agent/__init__.py +0 -0
  4. docqwise/benchmark/__init__.py +0 -0
  5. docqwise/bridges/__init__.py +0 -0
  6. docqwise/chunkers/__init__.py +0 -0
  7. docqwise/classifier/__init__.py +0 -0
  8. docqwise/cli/__init__.py +0 -0
  9. docqwise/cli/main.py +79 -0
  10. docqwise/comparator/__init__.py +0 -0
  11. docqwise/config.py +130 -0
  12. docqwise/connectors/__init__.py +0 -0
  13. docqwise/core/__init__.py +13 -0
  14. docqwise/core/bases.py +180 -0
  15. docqwise/core/chunk.py +80 -0
  16. docqwise/core/document.py +101 -0
  17. docqwise/core/element.py +163 -0
  18. docqwise/core/field.py +72 -0
  19. docqwise/databases/__init__.py +0 -0
  20. docqwise/embedders/__init__.py +0 -0
  21. docqwise/engine.py +377 -0
  22. docqwise/exceptions.py +49 -0
  23. docqwise/export/__init__.py +0 -0
  24. docqwise/extractors/__init__.py +0 -0
  25. docqwise/graph/__init__.py +0 -0
  26. docqwise/graph/backends/__init__.py +0 -0
  27. docqwise/incremental/__init__.py +0 -0
  28. docqwise/layout/__init__.py +0 -0
  29. docqwise/learning/__init__.py +0 -0
  30. docqwise/learning/correction.py +94 -0
  31. docqwise/llm/__init__.py +0 -0
  32. docqwise/ocr/__init__.py +0 -0
  33. docqwise/pipeline/__init__.py +0 -0
  34. docqwise/pipeline/prebuilt/__init__.py +0 -0
  35. docqwise/readers/__init__.py +3 -0
  36. docqwise/readers/auto_reader.py +127 -0
  37. docqwise/readers/base.py +21 -0
  38. docqwise/readers/csv_reader.py +3 -0
  39. docqwise/readers/docx_reader.py +3 -0
  40. docqwise/readers/email_reader.py +3 -0
  41. docqwise/readers/excel_reader.py +3 -0
  42. docqwise/readers/html_reader.py +3 -0
  43. docqwise/readers/image_reader.py +3 -0
  44. docqwise/readers/json_reader.py +3 -0
  45. docqwise/readers/parquet_reader.py +3 -0
  46. docqwise/readers/pdf_reader.py +79 -0
  47. docqwise/readers/pptx_reader.py +3 -0
  48. docqwise/readers/text_reader.py +3 -0
  49. docqwise/retrieval/__init__.py +0 -0
  50. docqwise/schema/__init__.py +0 -0
  51. docqwise/security/__init__.py +0 -0
  52. docqwise/server/__init__.py +0 -0
  53. docqwise/server/routes/__init__.py +0 -0
  54. docqwise/speed/__init__.py +0 -0
  55. docqwise/stores/__init__.py +0 -0
  56. docqwise/strategy/__init__.py +0 -0
  57. docqwise/templates/__init__.py +0 -0
  58. docqwise-0.1.0.dist-info/METADATA +338 -0
  59. docqwise-0.1.0.dist-info/RECORD +63 -0
  60. docqwise-0.1.0.dist-info/WHEEL +5 -0
  61. docqwise-0.1.0.dist-info/entry_points.txt +2 -0
  62. docqwise-0.1.0.dist-info/licenses/LICENSE +189 -0
  63. docqwise-0.1.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,163 @@
1
+ """Document element types."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from dataclasses import dataclass, field
6
+ from typing import Any, Optional
7
+
8
+ from docqwise.core.chunk import BoundingBox, DocqwiseChunk, ChunkMetadata, ElementType
9
+
10
+
11
+ @dataclass
12
+ class TableCell:
13
+ """A single cell in a table."""
14
+
15
+ text: str
16
+ row: int
17
+ col: int
18
+ row_span: int = 1
19
+ col_span: int = 1
20
+ is_header: bool = False
21
+ bbox: Optional[BoundingBox] = None
22
+ confidence: float = 1.0
23
+
24
+
25
+ @dataclass
26
+ class Table:
27
+ """Extracted table from a document."""
28
+
29
+ table_id: str
30
+ cells: list[TableCell]
31
+ rows: int
32
+ cols: int
33
+ page: int = 0
34
+ bbox: Optional[BoundingBox] = None
35
+ caption: str = ""
36
+ confidence: float = 1.0
37
+ context: str = ""
38
+ has_merged_cells: bool = False
39
+ is_cross_page: bool = False
40
+
41
+ def to_dataframe(self):
42
+ """Convert to pandas DataFrame."""
43
+ import pandas as pd
44
+
45
+ grid = [["" for _ in range(self.cols)] for _ in range(self.rows)]
46
+ headers = []
47
+
48
+ for cell in self.cells:
49
+ if cell.row < self.rows and cell.col < self.cols:
50
+ grid[cell.row][cell.col] = cell.text
51
+ if cell.is_header:
52
+ headers.append(cell.text)
53
+
54
+ if headers:
55
+ return pd.DataFrame(grid[1:], columns=grid[0])
56
+ return pd.DataFrame(grid)
57
+
58
+ def to_csv(self, path: str = "") -> str:
59
+ """Export as CSV."""
60
+ df = self.to_dataframe()
61
+ if path:
62
+ df.to_csv(path, index=False)
63
+ return path
64
+ return df.to_csv(index=False)
65
+
66
+ def to_json(self) -> list[dict]:
67
+ """Export as list of row dicts."""
68
+ df = self.to_dataframe()
69
+ return df.to_dict(orient="records")
70
+
71
+ def to_excel(self, path: str) -> None:
72
+ """Export as Excel file."""
73
+ df = self.to_dataframe()
74
+ df.to_excel(path, index=False)
75
+
76
+ def to_markdown(self) -> str:
77
+ """Export as markdown table."""
78
+ df = self.to_dataframe()
79
+ return df.to_markdown(index=False)
80
+
81
+ def to_chunk(self) -> DocqwiseChunk:
82
+ """Convert to retrieval chunk with table context."""
83
+ text = f"Table: {self.caption}\n{self.to_markdown()}" if self.caption else self.to_markdown()
84
+ return DocqwiseChunk(
85
+ chunk_id=f"table_{self.table_id}",
86
+ text=text,
87
+ metadata=ChunkMetadata(
88
+ page_num=self.page,
89
+ element_type=ElementType.TABLE,
90
+ bbox=self.bbox,
91
+ confidence=self.confidence,
92
+ ),
93
+ table_data=self.to_json(),
94
+ )
95
+
96
+
97
+ @dataclass
98
+ class ExtractedImage:
99
+ """Image or figure extracted from a document."""
100
+
101
+ image_id: str
102
+ page: int = 0
103
+ bbox: Optional[BoundingBox] = None
104
+ caption: str = ""
105
+ alt_text: str = ""
106
+ image_type: str = "" # photo | chart | diagram | logo | signature
107
+ chart_type: str = "" # bar | line | pie | scatter
108
+ chart_data: Any = None
109
+ image_bytes: Optional[bytes] = None
110
+ image_path: Optional[str] = None
111
+ resolution: tuple[int, int] = (0, 0)
112
+ confidence: float = 1.0
113
+
114
+ def save(self, directory: str, filename: str = "") -> str:
115
+ """Save extracted image to file."""
116
+ import os
117
+
118
+ if not filename:
119
+ filename = f"image_{self.image_id}.png"
120
+ path = os.path.join(directory, filename)
121
+ if self.image_bytes:
122
+ os.makedirs(directory, exist_ok=True)
123
+ with open(path, "wb") as f:
124
+ f.write(self.image_bytes)
125
+ return path
126
+
127
+
128
+ @dataclass
129
+ class Entity:
130
+ """Named entity extracted from a document."""
131
+
132
+ text: str
133
+ entity_type: str # PERSON, ORG, MONEY, DATE, LOCATION, custom
134
+ page: int = 0
135
+ bbox: Optional[BoundingBox] = None
136
+ confidence: float = 1.0
137
+ normalized_value: Any = None
138
+
139
+
140
+ @dataclass
141
+ class Relation:
142
+ """Relation between two entities."""
143
+
144
+ subject: str
145
+ predicate: str
146
+ object: str
147
+ confidence: float = 1.0
148
+ page: int = 0
149
+ source_entities: list[Entity] = field(default_factory=list)
150
+
151
+
152
+ @dataclass
153
+ class FormField:
154
+ """Form field extracted from a document."""
155
+
156
+ field_name: str
157
+ field_type: str # text | checkbox | radio | dropdown | signature
158
+ value: Any = None
159
+ is_checked: Optional[bool] = None
160
+ options: list[str] = field(default_factory=list)
161
+ bbox: Optional[BoundingBox] = None
162
+ page: int = 0
163
+ confidence: float = 1.0
docqwise/core/field.py ADDED
@@ -0,0 +1,72 @@
1
+ """Field extraction result models."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from dataclasses import dataclass, field
6
+ from typing import Any, Optional
7
+
8
+ from docqwise.core.chunk import BoundingBox
9
+
10
+
11
+ @dataclass
12
+ class FieldResult:
13
+ """Result of a single field extraction."""
14
+
15
+ value: Any
16
+ confidence: float
17
+ raw_text: str = ""
18
+ bbox: Optional[BoundingBox] = None
19
+ page: int = 0
20
+ extraction_method: str = ""
21
+ is_corrected: bool = False
22
+
23
+
24
+ @dataclass
25
+ class ExtractionResult:
26
+ """Result of extracting fields from a document."""
27
+
28
+ doc_id: str
29
+ source_path: str
30
+ fields: dict[str, FieldResult]
31
+ confidence: float
32
+ strategy_used: str = ""
33
+ processing_time_ms: float = 0.0
34
+ warnings: list[str] = field(default_factory=list)
35
+
36
+ _engine: Any = field(default=None, repr=False)
37
+
38
+ def correct(self, corrections: dict[str, Any], scope: str = "global") -> None:
39
+ """Apply user corrections and store for future use."""
40
+ for field_name, correct_value in corrections.items():
41
+ if field_name in self.fields:
42
+ self.fields[field_name] = FieldResult(
43
+ value=correct_value,
44
+ confidence=1.0,
45
+ raw_text=self.fields[field_name].raw_text,
46
+ bbox=self.fields[field_name].bbox,
47
+ page=self.fields[field_name].page,
48
+ extraction_method="user_correction",
49
+ is_corrected=True,
50
+ )
51
+ else:
52
+ self.fields[field_name] = FieldResult(
53
+ value=correct_value,
54
+ confidence=1.0,
55
+ extraction_method="user_correction",
56
+ is_corrected=True,
57
+ )
58
+
59
+ if self._engine is not None:
60
+ self._engine._correction_store.save(
61
+ doc_id=self.doc_id,
62
+ source_path=self.source_path,
63
+ corrections=corrections,
64
+ scope=scope,
65
+ )
66
+
67
+ def to_dict(self) -> dict[str, Any]:
68
+ return {k: v.value for k, v in self.fields.items()}
69
+
70
+ def to_json(self) -> str:
71
+ import json
72
+ return json.dumps(self.to_dict(), default=str, indent=2)
File without changes
File without changes
docqwise/engine.py ADDED
@@ -0,0 +1,377 @@
1
+ """Main Docqwise engine class."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import logging
6
+ from typing import Any, Optional, Union
7
+
8
+ from docqwise.config import DocqwiseConfig
9
+ from docqwise.core.document import DocqwiseDocument
10
+ from docqwise.core.chunk import DocqwiseChunk
11
+ from docqwise.core.field import ExtractionResult
12
+ from docqwise.core.element import Table, ExtractedImage, Entity, Relation
13
+
14
+ logger = logging.getLogger("docqwise")
15
+
16
+
17
+ class Docqwise:
18
+ """
19
+ DocQWise: Read. Extract. Retrieve.
20
+
21
+ Document intelligence engine that reads any format, extracts exact
22
+ structured data, and retrieves with semantic search.
23
+
24
+ Usage:
25
+ from docqwise import Docqwise
26
+
27
+ dq = Docqwise()
28
+ dq.ingest("documents/")
29
+ results = dq.retrieve("payment terms", top_k=5)
30
+ answer = dq.ask("What is the total amount?")
31
+ """
32
+
33
+ def __init__(
34
+ self,
35
+ # Strategy
36
+ strategy: str = "auto",
37
+ confidence_threshold: float = 0.85,
38
+ max_api_cost_per_doc: float = 0.05,
39
+ # Speed
40
+ workers: Union[int, str] = 1,
41
+ gpu_workers: int = 0,
42
+ threads: int = 4,
43
+ batch_size: int = 32,
44
+ prefetch: bool = False,
45
+ async_mode: bool = False,
46
+ # Models
47
+ ocr: Any = "easyocr",
48
+ layout_model: Any = "yolov8",
49
+ embedder: Any = "all-MiniLM-L6-v2",
50
+ llm: Any = None,
51
+ reranker: Any = None,
52
+ # Storage
53
+ vector_store: Any = "sqlite",
54
+ database: Any = None,
55
+ graph_store: Any = None,
56
+ store_path: str = "./docqwise_db",
57
+ # Search
58
+ search_mode: str = "vector",
59
+ # Incremental
60
+ incremental: bool = True,
61
+ cache: bool = True,
62
+ cache_dir: str = ".docqwise_cache",
63
+ # Learning
64
+ learning_mode: str = "exact",
65
+ # Config override
66
+ config: Optional[Union[str, DocqwiseConfig]] = None,
67
+ ):
68
+ if isinstance(config, str):
69
+ self._config = DocqwiseConfig.from_yaml(config)
70
+ elif isinstance(config, DocqwiseConfig):
71
+ self._config = config
72
+ else:
73
+ self._config = DocqwiseConfig()
74
+ self._config.strategy.mode = strategy
75
+ self._config.strategy.confidence_threshold = confidence_threshold
76
+ self._config.speed.workers = workers
77
+ self._config.speed.gpu_workers = gpu_workers
78
+ self._config.speed.threads = threads
79
+ self._config.speed.batch_size = batch_size
80
+ self._config.speed.prefetch = prefetch
81
+ self._config.speed.async_mode = async_mode
82
+ self._config.storage.vector_store = vector_store if isinstance(vector_store, str) else "custom"
83
+ self._config.storage.store_path = store_path
84
+ self._config.storage.cache = cache
85
+ self._config.storage.cache_dir = cache_dir
86
+ self._config.search.mode = search_mode
87
+ self._config.incremental.enabled = incremental
88
+
89
+ # Store raw component references for lazy init
90
+ self._ocr_ref = ocr
91
+ self._layout_ref = layout_model
92
+ self._embedder_ref = embedder
93
+ self._llm_ref = llm
94
+ self._reranker_ref = reranker
95
+ self._vector_store_ref = vector_store
96
+ self._database_ref = database
97
+ self._graph_store_ref = graph_store
98
+ self._learning_mode = learning_mode
99
+
100
+ # Lazy-initialized components
101
+ self._reader = None
102
+ self._ocr_engine = None
103
+ self._layout_model = None
104
+ self._embedder = None
105
+ self._llm = None
106
+ self._reranker = None
107
+ self._vector_store = None
108
+ self._database = None
109
+ self._graph_store = None
110
+ self._correction_store = None
111
+ self._strategy_router = None
112
+ self._change_detector = None
113
+
114
+ self._initialized = False
115
+ logger.info("DocQWise engine created (lazy init)")
116
+
117
+ def _ensure_initialized(self) -> None:
118
+ """Lazy initialization of components on first use."""
119
+ if self._initialized:
120
+ return
121
+ self._init_reader()
122
+ self._init_store()
123
+ self._init_correction_store()
124
+ self._initialized = True
125
+ logger.info("DocQWise engine initialized")
126
+
127
+ def _init_reader(self) -> None:
128
+ from docqwise.readers.auto_reader import AutoReader
129
+ self._reader = AutoReader()
130
+
131
+ def _init_store(self) -> None:
132
+ if isinstance(self._vector_store_ref, str) and self._vector_store_ref == "sqlite":
133
+ from docqwise.stores.sqlite_store import SQLiteVectorStore
134
+ self._vector_store = SQLiteVectorStore(self._config.storage.store_path)
135
+ elif hasattr(self._vector_store_ref, "insert"):
136
+ self._vector_store = self._vector_store_ref
137
+
138
+ def _init_embedder(self) -> None:
139
+ if self._embedder is not None:
140
+ return
141
+ if isinstance(self._embedder_ref, str):
142
+ from docqwise.embedders.sentence_transformer import SentenceTransformerEmbedder
143
+ self._embedder = SentenceTransformerEmbedder(self._embedder_ref)
144
+ elif hasattr(self._embedder_ref, "embed"):
145
+ self._embedder = self._embedder_ref
146
+
147
+ def _init_correction_store(self) -> None:
148
+ from docqwise.learning.correction import CorrectionStore
149
+ self._correction_store = CorrectionStore(self._config.storage.store_path)
150
+
151
+ # ═══════════════════════════════════════
152
+ # INGESTION
153
+ # ═══════════════════════════════════════
154
+
155
+ def ingest(self, source: str, **kwargs) -> dict:
156
+ """Ingest documents from file, folder, URL, or database connection string."""
157
+ self._ensure_initialized()
158
+ # TODO: implement full ingestion pipeline
159
+ logger.info(f"Ingesting from: {source}")
160
+ raise NotImplementedError("Ingestion pipeline coming in v0.1.0")
161
+
162
+ async def ingest_async(self, source: str, **kwargs) -> dict:
163
+ """Async ingestion with parallel processing."""
164
+ self._ensure_initialized()
165
+ raise NotImplementedError("Async ingestion coming in v0.1.0")
166
+
167
+ def sync(self, source: str = None, mode: str = "incremental",
168
+ interval: int = 300) -> None:
169
+ """Sync with database — incremental by default."""
170
+ raise NotImplementedError("Database sync coming in v0.1.0")
171
+
172
+ def watch(self, path: str, poll_interval: int = 60,
173
+ on_new: Any = None) -> None:
174
+ """Watch folder for new documents."""
175
+ raise NotImplementedError("File watching coming in v0.1.0")
176
+
177
+ # ═══════════════════════════════════════
178
+ # EXTRACTION
179
+ # ═══════════════════════════════════════
180
+
181
+ def extract_all(self, source: str, **kwargs) -> DocqwiseDocument:
182
+ """Extract everything from a document."""
183
+ self._ensure_initialized()
184
+ raise NotImplementedError("Full extraction coming in v0.1.0")
185
+
186
+ def extract_text(self, source: str, **kwargs) -> str:
187
+ """Extract text from a document."""
188
+ self._ensure_initialized()
189
+ raise NotImplementedError("Text extraction coming in v0.1.0")
190
+
191
+ def extract_fields(self, source: str, schema: dict = None,
192
+ template: str = None, **kwargs) -> ExtractionResult:
193
+ """Extract fields — schema-driven or auto-detect."""
194
+ self._ensure_initialized()
195
+ raise NotImplementedError("Field extraction coming in v0.1.0")
196
+
197
+ def extract_fields_batch(self, source: str, **kwargs) -> list[ExtractionResult]:
198
+ """Batch field extraction across multiple documents."""
199
+ self._ensure_initialized()
200
+ raise NotImplementedError("Batch extraction coming in v0.1.0")
201
+
202
+ def extract_tables(self, source: str, **kwargs) -> list[Table]:
203
+ """Extract tables from a document."""
204
+ self._ensure_initialized()
205
+ raise NotImplementedError("Table extraction coming in v0.1.0")
206
+
207
+ def extract_images(self, source: str, **kwargs) -> list[ExtractedImage]:
208
+ """Extract images and figures from a document."""
209
+ self._ensure_initialized()
210
+ raise NotImplementedError("Image extraction coming in v0.1.0")
211
+
212
+ def extract_form(self, source: str) -> dict:
213
+ """Extract form fields."""
214
+ self._ensure_initialized()
215
+ raise NotImplementedError("Form extraction coming in v0.1.0")
216
+
217
+ def extract_entities(self, source: str, types: list[str] = None,
218
+ custom_types: dict = None) -> list[Entity]:
219
+ """Extract named entities."""
220
+ self._ensure_initialized()
221
+ raise NotImplementedError("Entity extraction coming in v0.1.0")
222
+
223
+ def extract_relations(self, source: str) -> list[Relation]:
224
+ """Extract entity relations."""
225
+ self._ensure_initialized()
226
+ raise NotImplementedError("Relation extraction coming in v0.1.0")
227
+
228
+ def extract_layout(self, source: str) -> dict:
229
+ """Extract full layout map with bounding boxes."""
230
+ self._ensure_initialized()
231
+ raise NotImplementedError("Layout extraction coming in v0.1.0")
232
+
233
+ def auto_extract(self, source: str) -> ExtractionResult:
234
+ """Auto-detect document type and extract all relevant fields."""
235
+ self._ensure_initialized()
236
+ raise NotImplementedError("Auto extraction coming in v0.1.0")
237
+
238
+ # ═══════════════════════════════════════
239
+ # RETRIEVAL + Q&A
240
+ # ═══════════════════════════════════════
241
+
242
+ def retrieve(self, query: str, top_k: int = 5,
243
+ mode: str = None, **kwargs) -> list[dict]:
244
+ """Semantic search across ingested corpus."""
245
+ self._ensure_initialized()
246
+ raise NotImplementedError("Retrieval coming in v0.1.0")
247
+
248
+ def ask(self, question: str, source: str = None, **kwargs) -> str:
249
+ """Natural language Q&A."""
250
+ self._ensure_initialized()
251
+ raise NotImplementedError("Q&A coming in v0.1.0")
252
+
253
+ def ask_batch(self, question: str = None, questions: list = None,
254
+ documents: str = None, **kwargs) -> list:
255
+ """Batch Q&A across documents."""
256
+ raise NotImplementedError("Batch Q&A coming in v0.1.0")
257
+
258
+ def sql(self, query: str) -> list[dict]:
259
+ """Run SQL directly against ingested structured data."""
260
+ raise NotImplementedError("SQL queries coming in v0.1.0")
261
+
262
+ # ═══════════════════════════════════════
263
+ # CLASSIFICATION + COMPARISON
264
+ # ═══════════════════════════════════════
265
+
266
+ def classify(self, source: str, **kwargs) -> list[dict]:
267
+ """Classify document type."""
268
+ raise NotImplementedError("Classification coming in v0.1.0")
269
+
270
+ def classify_batch(self, source: str, **kwargs) -> list:
271
+ """Batch classification."""
272
+ raise NotImplementedError("Batch classification coming in v0.1.0")
273
+
274
+ def set_taxonomy(self, labels: list[str]) -> None:
275
+ """Set custom classification taxonomy."""
276
+ raise NotImplementedError("Taxonomy coming in v0.1.0")
277
+
278
+ def train_classifier(self, examples: dict) -> None:
279
+ """Few-shot classifier training."""
280
+ raise NotImplementedError("Classifier training coming in v0.1.0")
281
+
282
+ def compare(self, source_a: str, source_b: str) -> dict:
283
+ """Compare two documents."""
284
+ raise NotImplementedError("Comparison coming in v0.1.0")
285
+
286
+ def compare_batch(self, pairs: list, **kwargs) -> list:
287
+ """Batch comparison."""
288
+ raise NotImplementedError("Batch comparison coming in v0.1.0")
289
+
290
+ def find_duplicates(self, source: str, threshold: float = 0.85) -> list:
291
+ """Find near-duplicate documents."""
292
+ raise NotImplementedError("Deduplication coming in v0.1.0")
293
+
294
+ # ═══════════════════════════════════════
295
+ # SCHEMA
296
+ # ═══════════════════════════════════════
297
+
298
+ def detect_schema(self, source: str) -> dict:
299
+ """Auto-detect schema from structured data."""
300
+ raise NotImplementedError("Schema detection coming in v0.1.0")
301
+
302
+ def validate(self, source: str, schema: dict) -> dict:
303
+ """Validate data against schema."""
304
+ raise NotImplementedError("Validation coming in v0.1.0")
305
+
306
+ def discover_schema(self, connection_string: str) -> dict:
307
+ """Discover database schema."""
308
+ raise NotImplementedError("Schema discovery coming in v0.1.0")
309
+
310
+ # ═══════════════════════════════════════
311
+ # GRAPH
312
+ # ═══════════════════════════════════════
313
+
314
+ def build_graph(self, source: str = None) -> Any:
315
+ """Build document graph from ingested corpus."""
316
+ raise NotImplementedError("GraphRAG coming in v0.1.0")
317
+
318
+ # ═══════════════════════════════════════
319
+ # LEARNING
320
+ # ═══════════════════════════════════════
321
+
322
+ def learn_from_labels(self, documents: str, labels: str) -> None:
323
+ """Bulk learning from labeled dataset."""
324
+ raise NotImplementedError("Bulk learning coming in v0.1.0")
325
+
326
+ def learning_report(self) -> dict:
327
+ """Report on corrections and accuracy improvements."""
328
+ raise NotImplementedError("Learning report coming in v0.1.0")
329
+
330
+ def export_learnings(self, path: str) -> None:
331
+ """Export learned corrections."""
332
+ raise NotImplementedError("Export learnings coming in v0.1.0")
333
+
334
+ def import_learnings(self, path: str) -> None:
335
+ """Import corrections from another instance."""
336
+ raise NotImplementedError("Import learnings coming in v0.1.0")
337
+
338
+ # ═══════════════════════════════════════
339
+ # UTILITIES
340
+ # ═══════════════════════════════════════
341
+
342
+ def to_dataframe(self, source: str, **kwargs):
343
+ """Convert structured source to DataFrame."""
344
+ raise NotImplementedError("DataFrame conversion coming in v0.1.0")
345
+
346
+ def detect_pii(self, source: str) -> list:
347
+ """Detect PII in document."""
348
+ raise NotImplementedError("PII detection coming in v0.1.0")
349
+
350
+ def redact(self, source: str, output: str, redact_types: list = None) -> None:
351
+ """Redact PII."""
352
+ raise NotImplementedError("PII redaction coming in v0.1.0")
353
+
354
+ def checkpoint(self) -> None:
355
+ """Save current processing state."""
356
+ raise NotImplementedError("Checkpointing coming in v0.1.0")
357
+
358
+ def metrics(self) -> dict:
359
+ """Pipeline health and performance metrics."""
360
+ return {
361
+ "version": "0.1.0",
362
+ "store_path": self._config.storage.store_path,
363
+ "strategy": self._config.strategy.mode,
364
+ "workers": self._config.speed.workers,
365
+ "initialized": self._initialized,
366
+ }
367
+
368
+ def export(self, path: str, format: str = "json", **kwargs) -> None:
369
+ """Export processed data."""
370
+ raise NotImplementedError("Export coming in v0.1.0")
371
+
372
+ def __repr__(self) -> str:
373
+ return (
374
+ f"Docqwise(strategy='{self._config.strategy.mode}', "
375
+ f"store='{self._config.storage.vector_store}', "
376
+ f"workers={self._config.speed.workers})"
377
+ )
docqwise/exceptions.py ADDED
@@ -0,0 +1,49 @@
1
+ """DocQWise exceptions."""
2
+
3
+
4
+ class DocqwiseError(Exception):
5
+ """Base exception for docqwise."""
6
+
7
+
8
+ class ReaderError(DocqwiseError):
9
+ """Error reading a document."""
10
+
11
+
12
+ class UnsupportedFormatError(ReaderError):
13
+ """Document format is not supported."""
14
+
15
+
16
+ class ExtractionError(DocqwiseError):
17
+ """Error extracting content from a document."""
18
+
19
+
20
+ class OCRError(ExtractionError):
21
+ """Error during OCR processing."""
22
+
23
+
24
+ class StoreError(DocqwiseError):
25
+ """Error with vector/database store operations."""
26
+
27
+
28
+ class ConnectionError(DocqwiseError):
29
+ """Error connecting to external service."""
30
+
31
+
32
+ class SchemaError(DocqwiseError):
33
+ """Error with schema detection or validation."""
34
+
35
+
36
+ class PipelineError(DocqwiseError):
37
+ """Error in pipeline execution."""
38
+
39
+
40
+ class CycleDetectedError(PipelineError):
41
+ """Pipeline DAG contains a cycle."""
42
+
43
+
44
+ class ConfigError(DocqwiseError):
45
+ """Invalid configuration."""
46
+
47
+
48
+ class CorrectionError(DocqwiseError):
49
+ """Error applying or storing corrections."""
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes