docqwise 0.2.1__tar.gz → 0.3.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.
Files changed (137) hide show
  1. {docqwise-0.2.1 → docqwise-0.3.1}/PKG-INFO +92 -2
  2. {docqwise-0.2.1 → docqwise-0.3.1}/README.md +91 -1
  3. docqwise-0.3.1/docqwise/_version.py +1 -0
  4. {docqwise-0.2.1 → docqwise-0.3.1}/docqwise/engine.py +94 -3
  5. {docqwise-0.2.1 → docqwise-0.3.1}/docqwise/extractors/field_extractor.py +1 -1
  6. {docqwise-0.2.1 → docqwise-0.3.1}/docqwise/extractors/llm_extractor.py +3 -3
  7. docqwise-0.3.1/docqwise/extractors/multimodal_extractor.py +589 -0
  8. {docqwise-0.2.1 → docqwise-0.3.1}/docqwise/extractors/rag_extractor.py +10 -7
  9. {docqwise-0.2.1 → docqwise-0.3.1}/docqwise/factory.py +4 -4
  10. {docqwise-0.2.1 → docqwise-0.3.1}/docqwise/llm/hf_llm.py +3 -2
  11. docqwise-0.3.1/docqwise/llm/ollama.py +36 -0
  12. docqwise-0.3.1/docqwise/llm/openai_llm.py +51 -0
  13. docqwise-0.3.1/docqwise/retrieval/graphrag.py +430 -0
  14. docqwise-0.3.1/docqwise/retrieval/qa_engine.py +265 -0
  15. docqwise-0.3.1/docqwise/retrieval/rag_strategy.py +104 -0
  16. {docqwise-0.2.1 → docqwise-0.3.1}/docqwise.egg-info/PKG-INFO +92 -2
  17. {docqwise-0.2.1 → docqwise-0.3.1}/docqwise.egg-info/SOURCES.txt +2 -0
  18. {docqwise-0.2.1 → docqwise-0.3.1}/pyproject.toml +1 -1
  19. docqwise-0.2.1/docqwise/_version.py +0 -1
  20. docqwise-0.2.1/docqwise/extractors/multimodal_extractor.py +0 -240
  21. docqwise-0.2.1/docqwise/llm/ollama.py +0 -23
  22. docqwise-0.2.1/docqwise/llm/openai_llm.py +0 -26
  23. docqwise-0.2.1/docqwise/retrieval/qa_engine.py +0 -103
  24. {docqwise-0.2.1 → docqwise-0.3.1}/LICENSE +0 -0
  25. {docqwise-0.2.1 → docqwise-0.3.1}/docqwise/__init__.py +0 -0
  26. {docqwise-0.2.1 → docqwise-0.3.1}/docqwise/agent/__init__.py +0 -0
  27. {docqwise-0.2.1 → docqwise-0.3.1}/docqwise/benchmark/__init__.py +0 -0
  28. {docqwise-0.2.1 → docqwise-0.3.1}/docqwise/benchmark/runner.py +0 -0
  29. {docqwise-0.2.1 → docqwise-0.3.1}/docqwise/bridges/__init__.py +0 -0
  30. {docqwise-0.2.1 → docqwise-0.3.1}/docqwise/bridges/adaptive_bridge.py +0 -0
  31. {docqwise-0.2.1 → docqwise-0.3.1}/docqwise/bridges/evalkit_bridge.py +0 -0
  32. {docqwise-0.2.1 → docqwise-0.3.1}/docqwise/bridges/sightrag_bridge.py +0 -0
  33. {docqwise-0.2.1 → docqwise-0.3.1}/docqwise/bridges/sonarwise_bridge.py +0 -0
  34. {docqwise-0.2.1 → docqwise-0.3.1}/docqwise/chunkers/__init__.py +0 -0
  35. {docqwise-0.2.1 → docqwise-0.3.1}/docqwise/chunkers/base.py +0 -0
  36. {docqwise-0.2.1 → docqwise-0.3.1}/docqwise/chunkers/fixed_chunker.py +0 -0
  37. {docqwise-0.2.1 → docqwise-0.3.1}/docqwise/chunkers/sentence_chunker.py +0 -0
  38. {docqwise-0.2.1 → docqwise-0.3.1}/docqwise/chunkers/structure_chunker.py +0 -0
  39. {docqwise-0.2.1 → docqwise-0.3.1}/docqwise/classifier/__init__.py +0 -0
  40. {docqwise-0.2.1 → docqwise-0.3.1}/docqwise/classifier/zero_shot.py +0 -0
  41. {docqwise-0.2.1 → docqwise-0.3.1}/docqwise/cli/__init__.py +0 -0
  42. {docqwise-0.2.1 → docqwise-0.3.1}/docqwise/cli/main.py +0 -0
  43. {docqwise-0.2.1 → docqwise-0.3.1}/docqwise/comparator/__init__.py +0 -0
  44. {docqwise-0.2.1 → docqwise-0.3.1}/docqwise/comparator/text_diff.py +0 -0
  45. {docqwise-0.2.1 → docqwise-0.3.1}/docqwise/config.py +0 -0
  46. {docqwise-0.2.1 → docqwise-0.3.1}/docqwise/connectors/__init__.py +0 -0
  47. {docqwise-0.2.1 → docqwise-0.3.1}/docqwise/connectors/base.py +0 -0
  48. {docqwise-0.2.1 → docqwise-0.3.1}/docqwise/connectors/filesystem.py +0 -0
  49. {docqwise-0.2.1 → docqwise-0.3.1}/docqwise/core/__init__.py +0 -0
  50. {docqwise-0.2.1 → docqwise-0.3.1}/docqwise/core/bases.py +0 -0
  51. {docqwise-0.2.1 → docqwise-0.3.1}/docqwise/core/chunk.py +0 -0
  52. {docqwise-0.2.1 → docqwise-0.3.1}/docqwise/core/document.py +0 -0
  53. {docqwise-0.2.1 → docqwise-0.3.1}/docqwise/core/element.py +0 -0
  54. {docqwise-0.2.1 → docqwise-0.3.1}/docqwise/core/field.py +0 -0
  55. {docqwise-0.2.1 → docqwise-0.3.1}/docqwise/databases/__init__.py +0 -0
  56. {docqwise-0.2.1 → docqwise-0.3.1}/docqwise/databases/base.py +0 -0
  57. {docqwise-0.2.1 → docqwise-0.3.1}/docqwise/databases/sqlite_db.py +0 -0
  58. {docqwise-0.2.1 → docqwise-0.3.1}/docqwise/embedders/__init__.py +0 -0
  59. {docqwise-0.2.1 → docqwise-0.3.1}/docqwise/embedders/base.py +0 -0
  60. {docqwise-0.2.1 → docqwise-0.3.1}/docqwise/embedders/sentence_transformer.py +0 -0
  61. {docqwise-0.2.1 → docqwise-0.3.1}/docqwise/exceptions.py +0 -0
  62. {docqwise-0.2.1 → docqwise-0.3.1}/docqwise/export/__init__.py +0 -0
  63. {docqwise-0.2.1 → docqwise-0.3.1}/docqwise/export/csv_exporter.py +0 -0
  64. {docqwise-0.2.1 → docqwise-0.3.1}/docqwise/export/json_exporter.py +0 -0
  65. {docqwise-0.2.1 → docqwise-0.3.1}/docqwise/extractors/__init__.py +0 -0
  66. {docqwise-0.2.1 → docqwise-0.3.1}/docqwise/extractors/base.py +0 -0
  67. {docqwise-0.2.1 → docqwise-0.3.1}/docqwise/extractors/entity_extractor.py +0 -0
  68. {docqwise-0.2.1 → docqwise-0.3.1}/docqwise/extractors/image_extractor.py +0 -0
  69. {docqwise-0.2.1 → docqwise-0.3.1}/docqwise/extractors/metadata_extractor.py +0 -0
  70. {docqwise-0.2.1 → docqwise-0.3.1}/docqwise/extractors/table_extractor.py +0 -0
  71. {docqwise-0.2.1 → docqwise-0.3.1}/docqwise/extractors/text_extractor.py +0 -0
  72. {docqwise-0.2.1 → docqwise-0.3.1}/docqwise/graph/__init__.py +0 -0
  73. {docqwise-0.2.1 → docqwise-0.3.1}/docqwise/graph/backends/__init__.py +0 -0
  74. {docqwise-0.2.1 → docqwise-0.3.1}/docqwise/graph/document_graph.py +0 -0
  75. {docqwise-0.2.1 → docqwise-0.3.1}/docqwise/incremental/__init__.py +0 -0
  76. {docqwise-0.2.1 → docqwise-0.3.1}/docqwise/incremental/change_detector.py +0 -0
  77. {docqwise-0.2.1 → docqwise-0.3.1}/docqwise/incremental/watcher.py +0 -0
  78. {docqwise-0.2.1 → docqwise-0.3.1}/docqwise/layout/__init__.py +0 -0
  79. {docqwise-0.2.1 → docqwise-0.3.1}/docqwise/learning/__init__.py +0 -0
  80. {docqwise-0.2.1 → docqwise-0.3.1}/docqwise/learning/correction.py +0 -0
  81. {docqwise-0.2.1 → docqwise-0.3.1}/docqwise/llm/__init__.py +0 -0
  82. {docqwise-0.2.1 → docqwise-0.3.1}/docqwise/llm/base.py +0 -0
  83. {docqwise-0.2.1 → docqwise-0.3.1}/docqwise/ocr/__init__.py +0 -0
  84. {docqwise-0.2.1 → docqwise-0.3.1}/docqwise/ocr/base.py +0 -0
  85. {docqwise-0.2.1 → docqwise-0.3.1}/docqwise/ocr/easyocr_engine.py +0 -0
  86. {docqwise-0.2.1 → docqwise-0.3.1}/docqwise/ocr/tesseract_engine.py +0 -0
  87. {docqwise-0.2.1 → docqwise-0.3.1}/docqwise/pipeline/__init__.py +0 -0
  88. {docqwise-0.2.1 → docqwise-0.3.1}/docqwise/pipeline/pipeline.py +0 -0
  89. {docqwise-0.2.1 → docqwise-0.3.1}/docqwise/pipeline/prebuilt/__init__.py +0 -0
  90. {docqwise-0.2.1 → docqwise-0.3.1}/docqwise/readers/__init__.py +0 -0
  91. {docqwise-0.2.1 → docqwise-0.3.1}/docqwise/readers/auto_reader.py +0 -0
  92. {docqwise-0.2.1 → docqwise-0.3.1}/docqwise/readers/base.py +0 -0
  93. {docqwise-0.2.1 → docqwise-0.3.1}/docqwise/readers/csv_reader.py +0 -0
  94. {docqwise-0.2.1 → docqwise-0.3.1}/docqwise/readers/docx_reader.py +0 -0
  95. {docqwise-0.2.1 → docqwise-0.3.1}/docqwise/readers/email_reader.py +0 -0
  96. {docqwise-0.2.1 → docqwise-0.3.1}/docqwise/readers/excel_reader.py +0 -0
  97. {docqwise-0.2.1 → docqwise-0.3.1}/docqwise/readers/html_reader.py +0 -0
  98. {docqwise-0.2.1 → docqwise-0.3.1}/docqwise/readers/image_reader.py +0 -0
  99. {docqwise-0.2.1 → docqwise-0.3.1}/docqwise/readers/json_reader.py +0 -0
  100. {docqwise-0.2.1 → docqwise-0.3.1}/docqwise/readers/parquet_reader.py +0 -0
  101. {docqwise-0.2.1 → docqwise-0.3.1}/docqwise/readers/pdf_reader.py +0 -0
  102. {docqwise-0.2.1 → docqwise-0.3.1}/docqwise/readers/pptx_reader.py +0 -0
  103. {docqwise-0.2.1 → docqwise-0.3.1}/docqwise/readers/text_reader.py +0 -0
  104. {docqwise-0.2.1 → docqwise-0.3.1}/docqwise/retrieval/__init__.py +0 -0
  105. {docqwise-0.2.1 → docqwise-0.3.1}/docqwise/retrieval/hybrid_search.py +0 -0
  106. {docqwise-0.2.1 → docqwise-0.3.1}/docqwise/retrieval/query_router.py +0 -0
  107. {docqwise-0.2.1 → docqwise-0.3.1}/docqwise/retrieval/retriever.py +0 -0
  108. {docqwise-0.2.1 → docqwise-0.3.1}/docqwise/schema/__init__.py +0 -0
  109. {docqwise-0.2.1 → docqwise-0.3.1}/docqwise/schema/detector.py +0 -0
  110. {docqwise-0.2.1 → docqwise-0.3.1}/docqwise/schema/quality.py +0 -0
  111. {docqwise-0.2.1 → docqwise-0.3.1}/docqwise/schema/validator.py +0 -0
  112. {docqwise-0.2.1 → docqwise-0.3.1}/docqwise/security/__init__.py +0 -0
  113. {docqwise-0.2.1 → docqwise-0.3.1}/docqwise/security/pii_detector.py +0 -0
  114. {docqwise-0.2.1 → docqwise-0.3.1}/docqwise/server/__init__.py +0 -0
  115. {docqwise-0.2.1 → docqwise-0.3.1}/docqwise/server/api.py +0 -0
  116. {docqwise-0.2.1 → docqwise-0.3.1}/docqwise/server/mcp_server.py +0 -0
  117. {docqwise-0.2.1 → docqwise-0.3.1}/docqwise/server/routes/__init__.py +0 -0
  118. {docqwise-0.2.1 → docqwise-0.3.1}/docqwise/speed/__init__.py +0 -0
  119. {docqwise-0.2.1 → docqwise-0.3.1}/docqwise/speed/dispatcher.py +0 -0
  120. {docqwise-0.2.1 → docqwise-0.3.1}/docqwise/speed/progress.py +0 -0
  121. {docqwise-0.2.1 → docqwise-0.3.1}/docqwise/stores/__init__.py +0 -0
  122. {docqwise-0.2.1 → docqwise-0.3.1}/docqwise/stores/base.py +0 -0
  123. {docqwise-0.2.1 → docqwise-0.3.1}/docqwise/stores/faiss_store.py +0 -0
  124. {docqwise-0.2.1 → docqwise-0.3.1}/docqwise/stores/sqlite_store.py +0 -0
  125. {docqwise-0.2.1 → docqwise-0.3.1}/docqwise/strategy/__init__.py +0 -0
  126. {docqwise-0.2.1 → docqwise-0.3.1}/docqwise/strategy/profiler.py +0 -0
  127. {docqwise-0.2.1 → docqwise-0.3.1}/docqwise/strategy/router.py +0 -0
  128. {docqwise-0.2.1 → docqwise-0.3.1}/docqwise/templates/__init__.py +0 -0
  129. {docqwise-0.2.1 → docqwise-0.3.1}/docqwise/templates/contract.py +0 -0
  130. {docqwise-0.2.1 → docqwise-0.3.1}/docqwise/templates/invoice.py +0 -0
  131. {docqwise-0.2.1 → docqwise-0.3.1}/docqwise/templates/receipt.py +0 -0
  132. {docqwise-0.2.1 → docqwise-0.3.1}/docqwise/templates/resume.py +0 -0
  133. {docqwise-0.2.1 → docqwise-0.3.1}/docqwise.egg-info/dependency_links.txt +0 -0
  134. {docqwise-0.2.1 → docqwise-0.3.1}/docqwise.egg-info/entry_points.txt +0 -0
  135. {docqwise-0.2.1 → docqwise-0.3.1}/docqwise.egg-info/requires.txt +0 -0
  136. {docqwise-0.2.1 → docqwise-0.3.1}/docqwise.egg-info/top_level.txt +0 -0
  137. {docqwise-0.2.1 → docqwise-0.3.1}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: docqwise
3
- Version: 0.2.1
3
+ Version: 0.3.1
4
4
  Summary: Read, Extract, Retrieve: Document intelligence that adapts, accelerates, and scales.
5
5
  Author: Venkatkumar Rajan
6
6
  License: Apache-2.0
@@ -95,7 +95,7 @@ Dynamic: license-file
95
95
  </p>
96
96
 
97
97
  <p align="center">
98
- <a href="https://pypi.org/project/docqwise/"><img src="https://img.shields.io/badge/PyPI-docqwise-blue" alt="PyPI"></a>
98
+ <a href="https://pypi.org/project/docqwise/"><img src="https://img.shields.io/badge/PyPI-v0.3.1-green" alt="PyPI"></a>
99
99
  <a href="https://github.com/VK-Ant/docqwise/blob/main/LICENSE"><img src="https://img.shields.io/badge/License-Apache%202.0-blue.svg" alt="License"></a>
100
100
  <a href="https://github.com/VK-Ant/docqwise/blob/main/notebooks/docqwise_getting_started.ipynb">
101
101
  <img src="https://colab.research.google.com/assets/colab-badge.svg" alt="Open In Colab"></a>
@@ -241,6 +241,38 @@ Return JSON matching the schema exactly.
241
241
  """)
242
242
  ```
243
243
 
244
+ ## System Prompt
245
+
246
+ Set the LLM's role before extraction. Works with all backends and all RAG modes.
247
+
248
+ ```python
249
+ # Set the role — LLM behaves as this specialist
250
+ dq.extract_fields("invoice.pdf",
251
+ system_prompt="You are a maritime document specialist with 20 years experience.",
252
+ prompt="Extract vessel name, port, and total cost.
253
+
254
+ Document:
255
+ {context}
256
+
257
+ JSON:",
258
+ )
259
+
260
+ # System prompt + schema + template — full control
261
+ dq.extract_fields("contract.pdf",
262
+ system_prompt="You are a legal analyst specializing in shipping law.",
263
+ schema={"parties": {"type": "array"}, "liability": {"type": "number"}},
264
+ prompt_template="Extract {schema} from:
265
+ {context}
266
+ JSON:",
267
+ )
268
+
269
+ # System prompt with GraphRAG
270
+ dq.ask_rag("What is the liability cap?",
271
+ mode="graphrag",
272
+ system_prompt="You are a risk analyst. Be precise with numbers.",
273
+ )
274
+ ```
275
+
244
276
  ## Self-Improving Corrections
245
277
 
246
278
  ```python
@@ -258,6 +290,64 @@ dq.ask("Which vendor has highest sales?") # GROUP BY + MAX
258
290
  dq.ask("How many invoices are overdue?") # COUNT + WHERE
259
291
  ```
260
292
 
293
+ ## Source Attribution
294
+
295
+ Every answer tells you which document it came from.
296
+
297
+ ```python
298
+ # Simple ask — shows source in output
299
+ answer = dq.ask("What is the total amount?")
300
+ print(answer)
301
+ # 2,18,300.00
302
+ # Source: sample_invoice.pdf
303
+
304
+ # Detailed answer with metadata
305
+ result = dq.ask_with_source("What is the governing law?")
306
+ print(result.answer) # "laws of India"
307
+ print(result.source_name) # "sample_contract.pdf"
308
+ print(result.confidence) # 0.85
309
+ print(result.method) # "llm"
310
+ ```
311
+
312
+ ## RAG Modes
313
+
314
+ Choose your retrieval strategy:
315
+
316
+ ```python
317
+ dq = Docqwise()
318
+ dq.ingest("documents/")
319
+
320
+ # General RAG (default) — chunk → embed → retrieve → LLM
321
+ result = dq.ask_rag("total amount?", mode="general")
322
+
323
+ # GraphRAG — entity graph → graph traversal → cross-document answers
324
+ result = dq.ask_rag("Who is the vendor for PO-2012?", mode="graphrag")
325
+ print(result["answer"]) # "SuperStore"
326
+ print(result["source"]) # "invoice_001.pdf"
327
+ print(result["evidence"]) # [{entity, relation, target}, ...]
328
+
329
+ # Multimodal RAG — text + images → vision LLM
330
+ result = dq.ask_rag("What is in this scan?", mode="multimodal", source="scan.jpg")
331
+ ```
332
+
333
+ ## Graph Visualization
334
+
335
+ Build and visualize document knowledge graphs:
336
+
337
+ ```python
338
+ dq = Docqwise()
339
+ dq.ingest("documents/")
340
+
341
+ # Build knowledge graph from all documents
342
+ graphrag = dq.build_document_graph()
343
+ graph = graphrag.get_graph()
344
+ print(f"Nodes: {graph.node_count}, Edges: {graph.edge_count}")
345
+
346
+ # Generate interactive HTML visualization
347
+ dq.visualize_graph(output="my_graph.html")
348
+ # Open my_graph.html in browser — interactive, color-coded, draggable nodes
349
+ ```
350
+
261
351
  ## All Features
262
352
 
263
353
  ```python
@@ -7,7 +7,7 @@
7
7
  </p>
8
8
 
9
9
  <p align="center">
10
- <a href="https://pypi.org/project/docqwise/"><img src="https://img.shields.io/badge/PyPI-docqwise-blue" alt="PyPI"></a>
10
+ <a href="https://pypi.org/project/docqwise/"><img src="https://img.shields.io/badge/PyPI-v0.3.1-green" alt="PyPI"></a>
11
11
  <a href="https://github.com/VK-Ant/docqwise/blob/main/LICENSE"><img src="https://img.shields.io/badge/License-Apache%202.0-blue.svg" alt="License"></a>
12
12
  <a href="https://github.com/VK-Ant/docqwise/blob/main/notebooks/docqwise_getting_started.ipynb">
13
13
  <img src="https://colab.research.google.com/assets/colab-badge.svg" alt="Open In Colab"></a>
@@ -153,6 +153,38 @@ Return JSON matching the schema exactly.
153
153
  """)
154
154
  ```
155
155
 
156
+ ## System Prompt
157
+
158
+ Set the LLM's role before extraction. Works with all backends and all RAG modes.
159
+
160
+ ```python
161
+ # Set the role — LLM behaves as this specialist
162
+ dq.extract_fields("invoice.pdf",
163
+ system_prompt="You are a maritime document specialist with 20 years experience.",
164
+ prompt="Extract vessel name, port, and total cost.
165
+
166
+ Document:
167
+ {context}
168
+
169
+ JSON:",
170
+ )
171
+
172
+ # System prompt + schema + template — full control
173
+ dq.extract_fields("contract.pdf",
174
+ system_prompt="You are a legal analyst specializing in shipping law.",
175
+ schema={"parties": {"type": "array"}, "liability": {"type": "number"}},
176
+ prompt_template="Extract {schema} from:
177
+ {context}
178
+ JSON:",
179
+ )
180
+
181
+ # System prompt with GraphRAG
182
+ dq.ask_rag("What is the liability cap?",
183
+ mode="graphrag",
184
+ system_prompt="You are a risk analyst. Be precise with numbers.",
185
+ )
186
+ ```
187
+
156
188
  ## Self-Improving Corrections
157
189
 
158
190
  ```python
@@ -170,6 +202,64 @@ dq.ask("Which vendor has highest sales?") # GROUP BY + MAX
170
202
  dq.ask("How many invoices are overdue?") # COUNT + WHERE
171
203
  ```
172
204
 
205
+ ## Source Attribution
206
+
207
+ Every answer tells you which document it came from.
208
+
209
+ ```python
210
+ # Simple ask — shows source in output
211
+ answer = dq.ask("What is the total amount?")
212
+ print(answer)
213
+ # 2,18,300.00
214
+ # Source: sample_invoice.pdf
215
+
216
+ # Detailed answer with metadata
217
+ result = dq.ask_with_source("What is the governing law?")
218
+ print(result.answer) # "laws of India"
219
+ print(result.source_name) # "sample_contract.pdf"
220
+ print(result.confidence) # 0.85
221
+ print(result.method) # "llm"
222
+ ```
223
+
224
+ ## RAG Modes
225
+
226
+ Choose your retrieval strategy:
227
+
228
+ ```python
229
+ dq = Docqwise()
230
+ dq.ingest("documents/")
231
+
232
+ # General RAG (default) — chunk → embed → retrieve → LLM
233
+ result = dq.ask_rag("total amount?", mode="general")
234
+
235
+ # GraphRAG — entity graph → graph traversal → cross-document answers
236
+ result = dq.ask_rag("Who is the vendor for PO-2012?", mode="graphrag")
237
+ print(result["answer"]) # "SuperStore"
238
+ print(result["source"]) # "invoice_001.pdf"
239
+ print(result["evidence"]) # [{entity, relation, target}, ...]
240
+
241
+ # Multimodal RAG — text + images → vision LLM
242
+ result = dq.ask_rag("What is in this scan?", mode="multimodal", source="scan.jpg")
243
+ ```
244
+
245
+ ## Graph Visualization
246
+
247
+ Build and visualize document knowledge graphs:
248
+
249
+ ```python
250
+ dq = Docqwise()
251
+ dq.ingest("documents/")
252
+
253
+ # Build knowledge graph from all documents
254
+ graphrag = dq.build_document_graph()
255
+ graph = graphrag.get_graph()
256
+ print(f"Nodes: {graph.node_count}, Edges: {graph.edge_count}")
257
+
258
+ # Generate interactive HTML visualization
259
+ dq.visualize_graph(output="my_graph.html")
260
+ # Open my_graph.html in browser — interactive, color-coded, draggable nodes
261
+ ```
262
+
173
263
  ## All Features
174
264
 
175
265
  ```python
@@ -0,0 +1 @@
1
+ __version__ = "0.3.1"
@@ -226,7 +226,8 @@ class Docqwise:
226
226
  def extract_fields(self, source: str, schema: dict = None,
227
227
  template: str = None, method: str = "auto",
228
228
  llm=None, prompt: str = None,
229
- prompt_template: str = None, **kwargs) -> ExtractionResult:
229
+ prompt_template: str = None,
230
+ system_prompt: str = None, **kwargs) -> ExtractionResult:
230
231
  """Extract fields from a document.
231
232
 
232
233
  Args:
@@ -235,8 +236,9 @@ class Docqwise:
235
236
  template: pre-built template (invoice, contract, resume, receipt)
236
237
  method: auto | rag | llm | vision | regex
237
238
  llm: your own LLM instance
238
- prompt: your own extraction prompt (full prompt text)
239
- prompt_template: your prompt template with {text} and {schema} placeholders
239
+ prompt: your extraction prompt — use {context} for document text
240
+ prompt_template: your template — use {context} and {schema} placeholders
241
+ system_prompt: LLM system role (e.g. "You are a maritime document specialist")
240
242
  model: Ollama model name
241
243
  """
242
244
  self._ensure_initialized()
@@ -269,6 +271,7 @@ class Docqwise:
269
271
  result = extractor.extract_fields(
270
272
  doc, schema=target_schema,
271
273
  prompt=prompt, prompt_template=prompt_template,
274
+ system_prompt=system_prompt,
272
275
  )
273
276
 
274
277
  result._engine = self
@@ -365,10 +368,98 @@ class Docqwise:
365
368
  filters=kwargs.get("filters"))
366
369
  return []
367
370
 
371
+ def _ensure_qa_embedder(self):
372
+ if self._qa_engine._embedder is None:
373
+ from docqwise.factory import EmbedderFactory
374
+ embedder = EmbedderFactory.get(self._user_embedder)
375
+ if embedder:
376
+ self._qa_engine._embedder = embedder
377
+
368
378
  def ask(self, question: str, source: str = None, **kwargs) -> str:
369
379
  self._ensure_initialized()
380
+ self._ensure_qa_embedder()
370
381
  return self._qa_engine.ask(question, source=source)
371
382
 
383
+ def ask_with_source(self, question: str, source: str = None, **kwargs):
384
+ """Ask a question and get answer with source document attribution.
385
+
386
+ Returns:
387
+ QAAnswer with .answer, .source, .source_name, .confidence, .method
388
+ """
389
+ self._ensure_initialized()
390
+ self._ensure_qa_embedder()
391
+ return self._qa_engine.ask_with_source(question, source=source)
392
+
393
+ def ask_rag(self, question: str, mode: str = "general",
394
+ source: str = None, prompt: str = None,
395
+ system_prompt: str = None, **kwargs) -> dict:
396
+ """Ask using specific RAG strategy.
397
+
398
+ Modes:
399
+ general — chunk → embed → retrieve → LLM (default)
400
+ graphrag — extract entities → build graph → graph retrieval → LLM
401
+ multimodal — text + images → vision LLM → combined
402
+
403
+ Args:
404
+ question: your question
405
+ mode: general | graphrag | multimodal
406
+ source: specific document path (optional)
407
+ prompt: custom LLM prompt — use {context} for document text, {question} for the question
408
+ """
409
+ self._ensure_initialized()
410
+ self._ensure_qa_embedder()
411
+ from docqwise.retrieval.rag_strategy import RAGStrategy
412
+ strategy = RAGStrategy(engine=self, mode=mode)
413
+ return strategy.ask(question, source=source, prompt=prompt, **kwargs)
414
+
415
+ def build_document_graph(self, sources: list[str] = None) -> Any:
416
+ """Build knowledge graph from documents for GraphRAG."""
417
+ self._ensure_initialized()
418
+ from docqwise.retrieval.graphrag import GraphRAGEngine
419
+ graphrag = GraphRAGEngine(self)
420
+ graphrag.build_from_ingested(sources=sources)
421
+ self._doc_graph = graphrag
422
+ return graphrag
423
+
424
+ def visualize_graph(self, output: str = "docqwise_graph.png",
425
+ sources: list[str] = None) -> str:
426
+ """Generate graph visualization as image or HTML.
427
+
428
+ Args:
429
+ output: file path — .png, .svg, .pdf, .jpg, or .html
430
+ sources: list of document paths (builds from these, or uses all ingested)
431
+
432
+ Returns:
433
+ path to saved file
434
+ """
435
+ self._ensure_initialized()
436
+ if not hasattr(self, '_doc_graph') or self._doc_graph is None:
437
+ self.build_document_graph(sources=sources)
438
+
439
+ from docqwise.retrieval.graphrag import GraphVisualizer
440
+ graph = self._doc_graph.get_graph()
441
+ return GraphVisualizer.save(graph, output=output)
442
+
443
+ def visualize_query(self, question: str, output: str = "docqwise_query_graph.png",
444
+ sources: list[str] = None) -> dict:
445
+ """Ask a question with GraphRAG and visualize the answer path.
446
+
447
+ Returns:
448
+ dict with "answer", "source", "evidence", "graph_file"
449
+ """
450
+ self._ensure_initialized()
451
+ if not hasattr(self, '_doc_graph') or self._doc_graph is None:
452
+ self.build_document_graph(sources=sources)
453
+
454
+ result = self._doc_graph.query(question)
455
+
456
+ from docqwise.retrieval.graphrag import GraphVisualizer
457
+ graph = self._doc_graph.get_graph()
458
+ GraphVisualizer.from_query(graph, result, output=output)
459
+
460
+ result["graph_file"] = output
461
+ return result
462
+
372
463
  def sql(self, query: str) -> list[dict]:
373
464
  from docqwise.databases.sqlite_db import SQLiteDB
374
465
  db = SQLiteDB(os.path.join(self._store_path, "structured.db"))
@@ -20,7 +20,7 @@ class RegexFieldExtractor(BaseFieldExtractor):
20
20
  }
21
21
 
22
22
  def extract_fields(self, document: DocqwiseDocument, schema: dict = None,
23
- prompt: str = None, prompt_template: str = None) -> ExtractionResult:
23
+ prompt: str = None, prompt_template: str = None, system_prompt: str = None) -> ExtractionResult:
24
24
  text = document.text
25
25
  fields = {}
26
26
 
@@ -30,8 +30,8 @@ class LLMFieldExtractor(BaseFieldExtractor):
30
30
 
31
31
  # Priority 1: Ollama
32
32
  try:
33
- import requests
34
- requests.get("http://localhost:11434/api/tags", timeout=2)
33
+ from urllib.request import urlopen
34
+ urlopen("http://localhost:11434/api/tags", timeout=2)
35
35
  from docqwise.llm.ollama import OllamaLLM
36
36
  model = self._model or "nemotron-mini"
37
37
  self._llm = OllamaLLM(model=model)
@@ -64,7 +64,7 @@ class LLMFieldExtractor(BaseFieldExtractor):
64
64
 
65
65
  def extract_fields(self, document: DocqwiseDocument,
66
66
  schema: dict = None, prompt: str = None,
67
- prompt_template: str = None) -> ExtractionResult:
67
+ prompt_template: str = None, system_prompt: str = None) -> ExtractionResult:
68
68
  llm = self._get_llm()
69
69
 
70
70
  if llm is None: