vera-doc 0.2.0__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,12 @@
1
+ .venv/
2
+ __pycache__/
3
+ .pytest_cache/
4
+ *.pyc
5
+ *.vera
6
+ dist/
7
+ dist-electron/
8
+ release/
9
+ build/
10
+ node_modules/
11
+ *.egg-info/
12
+ examples/*.pdf
@@ -0,0 +1,510 @@
1
+ Metadata-Version: 2.4
2
+ Name: vera-doc
3
+ Version: 0.2.0
4
+ Summary: Vector-Embedded Retrieval Archive document and retrieval engine
5
+ Project-URL: Homepage, https://github.com/dkylewillis/vera
6
+ Project-URL: Repository, https://github.com/dkylewillis/vera
7
+ Project-URL: Specification, https://github.com/dkylewillis/vera/blob/main/docs/vera-spec-v0.2.md
8
+ Author: Kyle Willis
9
+ License: Apache-2.0
10
+ Keywords: embeddings,rag,semantic-search,sqlite,vector-database
11
+ Classifier: Development Status :: 3 - Alpha
12
+ Classifier: License :: OSI Approved :: Apache Software License
13
+ Classifier: Programming Language :: Python :: 3
14
+ Classifier: Topic :: Text Processing :: Indexing
15
+ Requires-Python: >=3.10
16
+ Requires-Dist: numpy>=1.24
17
+ Provides-Extra: ml
18
+ Requires-Dist: sentence-transformers>=2.7; extra == 'ml'
19
+ Description-Content-Type: text/markdown
20
+
21
+ # vera-doc
22
+
23
+ `vera-doc` is VERA's embedded storage and search engine. It stores ready-made
24
+ text chunks in a portable SQLite `.vera` file and provides transactional CRUD,
25
+ embeddings, metadata filters, keyword search, vector search, hybrid search,
26
+ corpus search, and rebuildable library indexes.
27
+
28
+ It intentionally contains no PDF parsing, OCR, source extraction, chunking,
29
+ MCP, CLI, or desktop dependencies. Applications extract and chunk content
30
+ before calling `vera-doc`. The separate `vera-extract` package provides the
31
+ standard PDF pipeline.
32
+
33
+ ## Install
34
+
35
+ ```bash
36
+ python -m pip install vera-doc
37
+ ```
38
+
39
+ Python 3.10 or newer is required. The default hashing embedder needs no model
40
+ download or API key.
41
+
42
+ ## Quick start
43
+
44
+ ```python
45
+ from vera import ChunkRecord, VeraDatabase
46
+
47
+ records = [
48
+ ChunkRecord(
49
+ id="pipe-requirement",
50
+ text="The minimum pipe diameter is 12 inches.",
51
+ metadata={
52
+ "source_filename": "manual.pdf",
53
+ "page_start": 42,
54
+ "heading_path": "Chapter 4 > Pipe Design",
55
+ },
56
+ )
57
+ ]
58
+
59
+ with VeraDatabase.create("manual.vera") as database:
60
+ database.add(records)
61
+
62
+ with VeraDatabase.open("manual.vera") as database:
63
+ results = database.search(
64
+ text="minimum pipe size",
65
+ mode="hybrid",
66
+ top_k=5,
67
+ )
68
+
69
+ for result in results:
70
+ print(result.score, result.record.text)
71
+ ```
72
+
73
+ `VeraDatabase.open()` is read-only by default. Use `mode="write"` when adding,
74
+ updating, or deleting records.
75
+
76
+ ## What is stored in a `.vera` file?
77
+
78
+ A VERA 0.2 file is one SQLite database containing:
79
+
80
+ ```text
81
+ manual.vera
82
+ ├── vera_metadata Format, embedding configuration, archive metadata
83
+ ├── chunks Final searchable text and JSON metadata
84
+ ├── embeddings One float32 vector per chunk
85
+ ├── chunks_fts SQLite FTS5 keyword index
86
+ ├── attachments Optional opaque binary payloads
87
+ └── chunk_attachments Typed links from chunks to attachments
88
+ ```
89
+
90
+ The core schema is conceptually:
91
+
92
+ ```sql
93
+ CREATE TABLE chunks (
94
+ chunk_id TEXT PRIMARY KEY,
95
+ text TEXT NOT NULL,
96
+ metadata_json TEXT NOT NULL,
97
+ created_at TEXT NOT NULL,
98
+ updated_at TEXT NOT NULL
99
+ );
100
+
101
+ CREATE TABLE embeddings (
102
+ chunk_id TEXT PRIMARY KEY REFERENCES chunks(chunk_id),
103
+ model_name TEXT NOT NULL,
104
+ model_dimension INTEGER NOT NULL,
105
+ vector BLOB NOT NULL,
106
+ vector_format TEXT NOT NULL,
107
+ created_at TEXT NOT NULL
108
+ );
109
+
110
+ CREATE TABLE attachments (
111
+ attachment_id TEXT PRIMARY KEY,
112
+ mime_type TEXT NOT NULL,
113
+ filename TEXT,
114
+ data BLOB NOT NULL,
115
+ hash TEXT NOT NULL,
116
+ metadata_json TEXT NOT NULL,
117
+ created_at TEXT NOT NULL
118
+ );
119
+ ```
120
+
121
+ Pages, headings, citations, bounding boxes, and source identity are optional
122
+ chunk metadata. Original files and extracted images may be stored as opaque
123
+ attachments. `vera-doc` stores these values but does not interpret or extract
124
+ them.
125
+
126
+ ## Public objects
127
+
128
+ ### `ChunkRecord`
129
+
130
+ The only indexed record type:
131
+
132
+ ```python
133
+ ChunkRecord(
134
+ id: str,
135
+ text: str,
136
+ metadata: Mapping[str, JSONValue] = {},
137
+ vector: Sequence[float] | None = None,
138
+ attachments: tuple[AttachmentRef, ...] = (),
139
+ )
140
+ ```
141
+
142
+ - `id` is a non-empty caller-controlled identifier.
143
+ - `text` is final chunk text. `vera-doc` never splits or cleans it.
144
+ - `metadata` may contain any JSON-compatible object.
145
+ - `vector` may contain a precomputed embedding. When omitted, the configured
146
+ embedding function embeds `text`.
147
+ - `attachments` links the chunk to stored attachments.
148
+
149
+ Records are immutable. IDs, text, metadata, vectors, and attachment references
150
+ are validated when the object is created or written.
151
+
152
+ ### `AttachmentRecord`
153
+
154
+ An optional opaque binary payload:
155
+
156
+ ```python
157
+ AttachmentRecord(
158
+ id: str,
159
+ data: bytes,
160
+ media_type: str,
161
+ filename: str | None = None,
162
+ checksum: str | None = None,
163
+ metadata: Mapping[str, JSONValue] = {},
164
+ )
165
+ ```
166
+
167
+ The SHA-256 checksum is computed automatically. If a checksum is supplied, it
168
+ must match the bytes. Attachments are not embedded or searchable.
169
+
170
+ ### `AttachmentRef`
171
+
172
+ Links a chunk to an attachment:
173
+
174
+ ```python
175
+ AttachmentRef(
176
+ attachment_id="source-pdf",
177
+ role="source",
178
+ )
179
+ ```
180
+
181
+ The role is caller-defined. Common roles include `source`, `figure`, and
182
+ `viewer_data`.
183
+
184
+ ### `QueryResult`
185
+
186
+ Returned by `VeraDatabase.search()`:
187
+
188
+ ```python
189
+ QueryResult(
190
+ record: ChunkRecord,
191
+ score: float,
192
+ semantic_score: float | None,
193
+ keyword_score: float | None,
194
+ )
195
+ ```
196
+
197
+ Call `result.as_dict()` for a JSON-compatible result without the raw vector.
198
+
199
+ ### `EmbeddingFunction`
200
+
201
+ A structural protocol for custom embedders:
202
+
203
+ ```python
204
+ class EmbeddingFunction:
205
+ model_name: str
206
+ dimension: int
207
+
208
+ def embed(self, texts: list[str]) -> numpy.ndarray:
209
+ ...
210
+ ```
211
+
212
+ The same model and dimension must be used for stored records and text queries.
213
+
214
+ ## `VeraDatabase` methods
215
+
216
+ ### Create and open
217
+
218
+ ```python
219
+ VeraDatabase.create(
220
+ path,
221
+ *,
222
+ embedding_function=None,
223
+ model="hashing",
224
+ metadata=None,
225
+ overwrite=False,
226
+ )
227
+
228
+ VeraDatabase.open(
229
+ path,
230
+ *,
231
+ mode="read",
232
+ embedding_function=None,
233
+ )
234
+ ```
235
+
236
+ `create()` publishes a valid database atomically. It raises `FileExistsError`
237
+ unless `overwrite=True`. Both methods return context managers.
238
+
239
+ ### Add records
240
+
241
+ ```python
242
+ database.add(records)
243
+ ```
244
+
245
+ Inserts an iterable of `ChunkRecord` objects. Existing IDs raise
246
+ `DuplicateRecordError`. The chunk row, embedding, FTS row, and attachment links
247
+ are written in one transaction.
248
+
249
+ ### Insert or replace records
250
+
251
+ ```python
252
+ database.upsert(records)
253
+ ```
254
+
255
+ Inserts new IDs and replaces existing records. Replacement updates text,
256
+ metadata, embedding, keyword index, and attachment links together.
257
+
258
+ ### Retrieve records
259
+
260
+ ```python
261
+ database.get(
262
+ ids=None,
263
+ *,
264
+ where=None,
265
+ limit=None,
266
+ )
267
+ ```
268
+
269
+ Returns `ChunkRecord` objects, including their vectors and attachment links.
270
+ `where` performs exact equality matching on top-level metadata keys:
271
+
272
+ ```python
273
+ records = database.get(where={"discipline": "civil"})
274
+ ```
275
+
276
+ ### Delete records
277
+
278
+ ```python
279
+ deleted_count = database.delete(
280
+ ids=None,
281
+ *,
282
+ where=None,
283
+ )
284
+ ```
285
+
286
+ Deleting a chunk also deletes its embedding, keyword-index row, and attachment
287
+ links. It does not delete the attachments themselves.
288
+
289
+ ### Search
290
+
291
+ ```python
292
+ database.search(
293
+ *,
294
+ text=None,
295
+ vector=None,
296
+ mode="hybrid",
297
+ where=None,
298
+ top_k=10,
299
+ )
300
+ ```
301
+
302
+ Supported modes:
303
+
304
+ - `keyword` uses SQLite FTS5 and BM25 ranking.
305
+ - `semantic` uses cosine similarity against stored vectors.
306
+ - `hybrid` independently normalizes semantic and keyword scores, then combines
307
+ them with equal weight.
308
+
309
+ Semantic search accepts query `text` or a compatible precomputed `vector`.
310
+ Keyword and hybrid search require text.
311
+
312
+ ### Attachments
313
+
314
+ ```python
315
+ database.put_attachments(attachments, upsert=False)
316
+ attachment = database.get_attachment("source-pdf")
317
+ database.delete_attachment("source-pdf")
318
+ ```
319
+
320
+ Referenced attachments cannot be deleted until their chunk links are removed.
321
+ Missing attachments raise `RecordNotFoundError`.
322
+
323
+ ### Archive metadata
324
+
325
+ ```python
326
+ metadata = database.metadata
327
+ database.set_metadata({"project": "stormwater"})
328
+ ```
329
+
330
+ Archive metadata is a JSON-compatible object separate from per-chunk metadata.
331
+
332
+ ### Transactions
333
+
334
+ ```python
335
+ with database.transaction():
336
+ database.put_attachments(attachments)
337
+ database.add(records)
338
+ ```
339
+
340
+ The entire block commits together. An exception rolls it back. Nested
341
+ transactions are intentionally rejected.
342
+
343
+ ### Inspection and validation
344
+
345
+ ```python
346
+ info = database.inspect()
347
+ report = database.validate()
348
+ ```
349
+
350
+ Inspection reports the format, model, dimension, counts, and archive metadata.
351
+ Validation checks SQLite integrity, required tables and metadata, embedding and
352
+ FTS parity, vector lengths, JSON payloads, foreign keys, and attachment hashes.
353
+
354
+ ### Close
355
+
356
+ ```python
357
+ database.close()
358
+ ```
359
+
360
+ Context managers call `close()` automatically.
361
+
362
+ ## Exceptions
363
+
364
+ - `DuplicateRecordError` — `add()` received an existing ID.
365
+ - `RecordNotFoundError` — a chunk references an unknown attachment or a
366
+ requested attachment does not exist.
367
+ - `ReadOnlyError` — a mutation was attempted after a read-only open.
368
+ - Standard `FileNotFoundError`, `FileExistsError`, `TypeError`, and
369
+ `ValueError` are used for ordinary path and validation failures.
370
+
371
+ ## Optional attachments example
372
+
373
+ ```python
374
+ from vera import (
375
+ AttachmentRecord,
376
+ AttachmentRef,
377
+ ChunkRecord,
378
+ VeraDatabase,
379
+ )
380
+
381
+ source = AttachmentRecord(
382
+ id="source-pdf",
383
+ data=pdf_bytes,
384
+ media_type="application/pdf",
385
+ filename="manual.pdf",
386
+ metadata={"role": "source"},
387
+ )
388
+
389
+ chunk = ChunkRecord(
390
+ id="chunk-1",
391
+ text="The final, already-extracted chunk.",
392
+ metadata={"page_start": 42},
393
+ attachments=(AttachmentRef("source-pdf", role="source"),),
394
+ )
395
+
396
+ with VeraDatabase.create("manual.vera") as database:
397
+ with database.transaction():
398
+ database.put_attachments([source])
399
+ database.add([chunk])
400
+ ```
401
+
402
+ ## Custom embeddings
403
+
404
+ ```python
405
+ import numpy as np
406
+
407
+ from vera import ChunkRecord, VeraDatabase
408
+
409
+
410
+ class MyEmbedder:
411
+ model_name = "example/my-embedder"
412
+ dimension = 2
413
+
414
+ def embed(self, texts: list[str]) -> np.ndarray:
415
+ return np.asarray([[1.0, 0.0] for _ in texts], dtype=np.float32)
416
+
417
+
418
+ embedder = MyEmbedder()
419
+
420
+ with VeraDatabase.create(
421
+ "custom.vera",
422
+ embedding_function=embedder,
423
+ ) as database:
424
+ database.add([ChunkRecord(id="one", text="Example text")])
425
+
426
+ with VeraDatabase.open(
427
+ "custom.vera",
428
+ embedding_function=embedder,
429
+ ) as database:
430
+ results = database.search(text="example", mode="semantic")
431
+ ```
432
+
433
+ Callers may instead provide `ChunkRecord.vector` and search with a query
434
+ vector.
435
+
436
+ ## Libraries of `.vera` files
437
+
438
+ `VeraCorpus` searches a directory of `.vera` files as one corpus:
439
+
440
+ ```python
441
+ from vera import VeraCorpus
442
+
443
+ with VeraCorpus.open("./library", recursive=True) as corpus:
444
+ results = corpus.search("detention requirements", top_k=5)
445
+ ```
446
+
447
+ For larger libraries, create a persistent derived index:
448
+
449
+ ```python
450
+ from vera import (
451
+ build_library_index,
452
+ library_index_status,
453
+ update_library_index,
454
+ )
455
+
456
+ build_library_index("./library", recursive=True)
457
+ print(library_index_status("./library"))
458
+ update_library_index("./library")
459
+ ```
460
+
461
+ The `.vera-index/` directory is rebuildable. Individual `.vera` files remain
462
+ the source of truth.
463
+
464
+ ## Legacy document API
465
+
466
+ `VeraDocument` remains a read-oriented compatibility facade for VERA 0.1,
467
+ the CLI, the desktop app, and citation-oriented workflows:
468
+
469
+ ```python
470
+ from vera import VeraDocument
471
+
472
+ with VeraDocument.open("manual.vera") as document:
473
+ results = document.search(
474
+ "detention requirements",
475
+ mode="hybrid",
476
+ top_k=5,
477
+ context_chunks=1,
478
+ )
479
+ ```
480
+
481
+ New applications that create or mutate databases should use `VeraDatabase`.
482
+
483
+ ## Package source structure
484
+
485
+ ```text
486
+ src/vera/
487
+ ├── __init__.py Public exports
488
+ ├── models.py Chunk, attachment, and query value objects
489
+ ├── database.py Transactional vector-database facade
490
+ ├── document.py Legacy/read-oriented compatibility facade
491
+ ├── corpus.py Multi-file corpus search
492
+ ├── collection.py Persistent library index
493
+ └── core/
494
+ ├── schema.py SQLite schema and format versions
495
+ ├── validation.py Integrity and contract validation
496
+ ├── embeddings.py Embedders and vector serialization
497
+ ├── search.py Legacy search implementation
498
+ ├── inspection.py Legacy inspection helpers
499
+ ├── access.py Legacy page/asset/region access
500
+ └── figures.py Legacy figure access
501
+ ```
502
+
503
+ Source extraction lives under `packages/vera-extract`, and MCP integration
504
+ lives under `packages/vera-mcp`.
505
+
506
+ ## Format and API references
507
+
508
+ - [VERA 0.2 specification](https://github.com/dkylewillis/vera/blob/main/docs/vera-spec-v0.2.md)
509
+ - [Full Python API guide](https://github.com/dkylewillis/vera/blob/main/docs/python-api.md)
510
+ - [Architecture](https://github.com/dkylewillis/vera/blob/main/docs/architecture.md)