Backups¶
Corpus backups (ADR-019): one zip
bundle per backup, written into the corpus folder's reserved
varagity-exports/ so it survives docker compose down -v beside the
sources. The container (bundle.json manifest, the all-or-nothing writer,
the one-member manifest read, the folder listing with each bundle's cheap
restore verdict — the graph engine's file format included — and the full
sha256 verification), the RAG export cut from one consistent corpus
snapshot, the RAG restore that turns a bundle back into rows and a rebuilt
BM25 index with no model call, the graph export that copies the engine's
working directory verbatim under the graph service's write lock, and the
graph restore that extracts it beside the live one and swaps it in. The
HTTP side (the corpus gate, the backup runner, the routes) is in the
HTTP API reference, the advisory corpus lock is on the vector
store in Stores, the swap itself
(GraphService.replace_workdir) is in GraphRAG, and the
rag-export / rag-restore / graph-export / graph-restore flows are in
Orchestration. The format, and how a restore reads it back,
are described in Data model.
varagity.backup
¶
Corpus backups: one zip bundle per backup, in each corpus's reserved folder (ADR-019).
docker compose down -v deletes every processed byte — the chunk rows,
their contextual blurbs and embeddings, the BM25 index, the extracted
message graph — while the source folders, being host binds, survive. A
bundle written into <corpus root>/varagity-exports/ survives with them,
and holds what a rebuild would otherwise have to recompute with the LLM and
the embedder.
- :mod:
varagity.backup.bundle— the container: thebundle.jsonmanifest, the all-or-nothing writer (.partial→os.replace, a sha256 per member), the one-member manifest read, the folder listing with each bundle's cheap restore verdict, and the full sha256 verification. - :mod:
varagity.backup.rag— what a RAG bundle holds, how it is cut from one consistent corpus snapshot, and how a restore turns it back into rows (and a rebuilt BM25 index) with no model call. - :mod:
varagity.backup.graph— what a graph bundle holds (the engine's working directory, verbatim), how it is copied under the graph service's write lock while chat keeps answering, and how a restore extracts it and swaps it in for the live workdir with no model call.
The package knows nothing about HTTP: the API's backup runner and routes
(:mod:varagity.api.backup_runner, :mod:varagity.api.routes.backups)
drive it through the rag-export, rag-restore, graph-export and
graph-restore Prefect flows.
BUNDLE_FORMAT = 'varagity-backup'
module-attribute
¶
The manifest's format marker: what makes a zip a varagity bundle.
BUNDLE_VERSION = 1
module-attribute
¶
The bundle layout this code writes (and, in time, the versions it reads).
Corpus = Literal['rag', 'graph']
module-attribute
¶
Which corpus a bundle backs up — ChatRequest.corpus's vocabulary.
MANIFEST_NAME = 'bundle.json'
module-attribute
¶
The manifest member — always the zip's last entry.
BM25RebuildFailed
¶
Bases: RuntimeError
The rows are restored, but the BM25 index could not be rebuilt.
The Postgres commit already happened, so the restore took effect; restoring the same bundle again rebuilds the index (and rewrites the identical rows on the way).
Attributes:
| Name | Type | Description |
|---|---|---|
manifest |
The restored bundle's manifest — the rows now in the
corpus are its rows, so the corpus-stale flag must still be
derived from it ( |
Source code in varagity/backup/rag.py
101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 | |
__init__(message, manifest=None)
¶
Word the failure and keep the manifest of the rows that did commit.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
message
|
str
|
What failed, and how to repair it. |
required |
manifest
|
BundleManifest | None
|
The restored bundle's manifest. |
None
|
Source code in varagity/backup/rag.py
115 116 117 118 119 120 121 122 123 | |
BundleDamaged
¶
Bases: BundleInvalid
A member's bytes don't match what its bundle recorded.
The one defect the cheap verdict can't see: the zip and its manifest are intact, but a member's sha256 (or the zip's own CRC-32, or its deflate stream) says the content changed after it was written.
Source code in varagity/backup/bundle.py
211 212 213 214 215 216 217 | |
BundleError
¶
Bases: Exception
Base class for a bundle that can't be read or used.
Source code in varagity/backup/bundle.py
203 204 | |
BundleIncompatible
¶
Bases: BundleError
A sound bundle this stack must not restore.
Another bundle format version, another corpus, vectors from another
embedding model or dimension — or, for a graph bundle, files another
graph engine (or another major.minor version of this one) wrote. Neither
store checks the embedding model (pgvector compares only the vector
size, LightRAG only embedding_dim), so restoring a mismatch would
succeed and then retrieve the wrong passages — it is refused instead,
never converted.
Source code in varagity/backup/bundle.py
220 221 222 223 224 225 226 227 228 229 230 | |
BundleInvalid
¶
Bases: BundleError
The file is not a readable varagity bundle (damaged, or not one at all).
Source code in varagity/backup/bundle.py
207 208 | |
BundleListing
dataclass
¶
One bundle file found in an exports folder.
Attributes:
| Name | Type | Description |
|---|---|---|
name |
str
|
The file name. |
path |
Path
|
Its resolved path. |
size_bytes |
int
|
Its size on disk. |
modified_at |
datetime
|
Its mtime (UTC). |
mtime_ns |
int
|
Its mtime in nanoseconds — with |
verdict |
BundleVerdict
|
The cheap inspection's verdict for the listing's corpus. |
Source code in varagity/backup/bundle.py
306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 | |
manifest
property
¶
The bundle's manifest.
Returns:
| Type | Description |
|---|---|
BundleManifest | None
|
The manifest, or |
BundleManifest | None
|
varagity bundle (copied in by hand, damaged, another format …). |
BundleManifest
¶
Bases: BaseModel
bundle.json: what a bundle holds and what it was made under.
Every later phase judges bundles against this record — a restore checks compatibility against it and derives the stale flag from it — so it carries the export-time facts no row records.
Attributes:
| Name | Type | Description |
|---|---|---|
format |
Literal['varagity-backup']
|
Always |
version |
int
|
The bundle layout version (:data: |
corpus |
Corpus
|
Which corpus the bundle backs up. |
created_at |
datetime
|
When the export's snapshot was taken (UTC). |
embedding_model |
str
|
|
embedding_dim |
int
|
|
stale |
bool
|
The corpus's stale flag at export ( |
counts |
dict[str, int]
|
What the bundle holds — rag: |
ingest_settings |
dict[str, str] | None
|
The reingest-affecting settings at export, in
env-string form (rag only; |
engine |
EngineFormat | None
|
The graph engine's file format at export — the workdir is
copied verbatim, so it is only as portable as the engine version
that wrote it (graph only; |
members |
list[BundleMember]
|
Every data member with its size and sha256, in write order. |
Source code in varagity/backup/bundle.py
161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 | |
BundleMember
¶
Bases: BaseModel
One data member of a bundle, as its manifest records it.
Attributes:
| Name | Type | Description |
|---|---|---|
name |
str
|
The member's path inside the zip. |
size_bytes |
int
|
Its uncompressed size. |
sha256 |
str
|
The hex sha256 of its uncompressed bytes — what a restore verifies before it touches a store. |
Source code in varagity/backup/bundle.py
146 147 148 149 150 151 152 153 154 155 156 157 158 | |
BundleSource
¶
An open bundle whose members are read through verifying streams.
Attributes:
| Name | Type | Description |
|---|---|---|
manifest |
The manifest the members are checked against. |
Source code in varagity/backup/bundle.py
1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 | |
__init__(archive, manifest)
¶
Bind the open zip to the manifest it is held to.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
archive
|
ZipFile
|
The open bundle. |
required |
manifest
|
BundleManifest
|
Its (already inspected) manifest. |
required |
Source code in varagity/backup/bundle.py
1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 | |
member(name)
¶
Open one data member for reading, verified when the block exits.
Several members may be open at once (zipfile reads each through its own position in the shared file), which is how a restore reads the chunk lines and their vectors side by side.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
name
|
str
|
The member's name. |
required |
Yields:
| Type | Description |
|---|---|
MemberStream
|
The member's verifying stream. When the |
MemberStream
|
cleanly, whatever the reader left is drained and the member's |
MemberStream
|
size and sha256 are checked against the manifest. |
Raises:
| Type | Description |
|---|---|
BundleInvalid
|
If the manifest records no such member, or the zip can't open it. |
BundleDamaged
|
If the member's bytes don't match its record. |
Source code in varagity/backup/bundle.py
1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 | |
BundleVerdict
dataclass
¶
What the cheap inspection concluded about one bundle.
Attributes:
| Name | Type | Description |
|---|---|---|
manifest |
BundleManifest | None
|
The bundle's manifest, or |
problem |
BundleError | None
|
Why the bundle can't be restored, or |
Source code in varagity/backup/bundle.py
241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 | |
reason
property
¶
The problem, worded for a person.
Returns:
| Type | Description |
|---|---|
str | None
|
The refusal's message, or |
restorable
property
¶
Whether nothing the inspection could see refuses the bundle.
Returns:
| Type | Description |
|---|---|
bool
|
|
manifest_or_raise()
¶
Return the manifest of a restorable bundle, or raise its problem.
Returns:
| Type | Description |
|---|---|
BundleManifest
|
The manifest. |
Raises:
| Type | Description |
|---|---|
BundleInvalid
|
If the bundle is not a sound bundle. |
BundleIncompatible
|
If this stack must not restore it. |
Source code in varagity/backup/bundle.py
272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 | |
BundleWriter
¶
Fills one bundle: data members first, then the sealed manifest.
Obtained from :func:write_bundle; :attr:result is available once its
with block exits cleanly.
Attributes:
| Name | Type | Description |
|---|---|---|
corpus |
The corpus being backed up. |
|
created_at |
The export's instant — the name's stamp and the
manifest's |
Source code in varagity/backup/bundle.py
532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 | |
manifest
property
¶
The sealed manifest (None until :meth:seal runs).
Returns:
| Type | Description |
|---|---|
BundleManifest | None
|
The manifest, or |
result
property
¶
The published bundle.
Returns:
| Name | Type | Description |
|---|---|---|
What |
WrittenBundle
|
func: |
Raises:
| Type | Description |
|---|---|
RuntimeError
|
If the bundle was not (yet) published. |
__init__(archive, corpus, created_at)
¶
Bind the writer to its open partial archive.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
archive
|
ZipFile
|
The partial zip, open for writing. |
required |
corpus
|
Corpus
|
The corpus being backed up. |
required |
created_at
|
datetime
|
The export's instant (UTC). |
required |
Source code in varagity/backup/bundle.py
544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 | |
member(name, *, compress=True)
¶
Stream one data member into the bundle.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
name
|
str
|
The member's path inside the zip. |
required |
compress
|
bool
|
Deflate it (text) or store it as-is (already-dense bytes like a float32 matrix, which deflate can't shrink). |
True
|
Yields:
| Type | Description |
|---|---|
MemberSink
|
The sink to write the member's bytes to; its size and sha256 are |
MemberSink
|
recorded for the manifest when the block exits. |
Raises:
| Type | Description |
|---|---|
ValueError
|
If the name is the manifest's or already used. |
RuntimeError
|
If the manifest is already sealed. |
Source code in varagity/backup/bundle.py
562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 | |
seal(*, embedding_model, embedding_dim, stale, counts, ingest_settings=None, engine=None)
¶
Write bundle.json as the bundle's last member.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
embedding_model
|
str
|
|
required |
embedding_dim
|
int
|
|
required |
stale
|
bool
|
The corpus's stale flag at export. |
required |
counts
|
dict[str, int]
|
What the bundle holds. |
required |
ingest_settings
|
dict[str, str] | None
|
The reingest-affecting settings at export, if the corpus has them. |
None
|
engine
|
EngineFormat | None
|
The graph engine's file format, if the corpus is the graph. |
None
|
Returns:
| Type | Description |
|---|---|
BundleManifest
|
The manifest, as written. |
Raises:
| Type | Description |
|---|---|
RuntimeError
|
If the manifest is already sealed. |
Source code in varagity/backup/bundle.py
595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 | |
GraphExportReport
dataclass
¶
What one graph export wrote.
Attributes:
| Name | Type | Description |
|---|---|---|
bundle |
WrittenBundle
|
The published bundle (name, path, size, manifest). |
files |
int
|
How many workdir files it holds. |
wall_clock_s |
float
|
Seconds from the export's start (the write lock is taken at once) to the published bundle. |
Source code in varagity/backup/graph.py
97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 | |
counts
property
¶
What the bundle holds, by the graph's own summary (see :func:graph_counts).
Returns:
| Type | Description |
|---|---|
dict[str, int]
|
The manifest's counts. |
GraphRestore
dataclass
¶
What one graph restore did.
Attributes:
| Name | Type | Description |
|---|---|---|
bundle_name |
str
|
The restored bundle's file name. |
size_bytes |
int
|
The bundle's size on disk. |
manifest |
BundleManifest
|
Its manifest. |
files |
int
|
How many workdir files it restored. |
stats |
GraphStats
|
The restored graph's size, as its freshly opened session reported it. |
wall_clock_s |
float
|
Seconds from validation to an open graph. |
stale |
bool | None
|
The graph-stale flag the restore left — the bundle's own,
written by the |
Source code in varagity/backup/graph.py
366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 | |
counts
property
¶
What the restored graph holds, by the bundle's manifest (its size summary at export).
Returns:
| Type | Description |
|---|---|
dict[str, int]
|
|
dict[str, int]
|
at export — |
MemberStream
¶
The read end of one bundle member, hashed and counted as it is read.
Every byte handed to the reader passes through :meth:read or
:meth:readline, so once the member is drained the digest covers it
whole; :meth:verify then holds it to the manifest's record.
Attributes:
| Name | Type | Description |
|---|---|---|
name |
The member's name. |
Source code in varagity/backup/bundle.py
1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 | |
__init__(raw, record)
¶
Wrap a member's decompressing read stream.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
raw
|
IO[bytes]
|
The stream |
required |
record
|
BundleMember
|
What the manifest recorded for the member. |
required |
Source code in varagity/backup/bundle.py
1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 | |
__iter__()
¶
Iterate the member's lines (a JSONL member's records).
Yields:
| Type | Description |
|---|---|
bytes
|
Each line, newline included. |
Source code in varagity/backup/bundle.py
1284 1285 1286 1287 1288 1289 1290 1291 | |
read(size=-1)
¶
Read up to size bytes (all that is left when negative).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
size
|
int
|
The most bytes to return. |
-1
|
Returns:
| Type | Description |
|---|---|
bytes
|
The bytes; |
Raises:
| Type | Description |
|---|---|
BundleDamaged
|
If the member's bytes fail the zip's own checks. |
BundleInvalid
|
If the member can't be read for another reason. |
Source code in varagity/backup/bundle.py
1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 | |
readline()
¶
Read one line, newline included.
Returns:
| Type | Description |
|---|---|
bytes
|
The line; |
Raises:
| Type | Description |
|---|---|
BundleDamaged
|
If the member's bytes fail the zip's own checks. |
BundleInvalid
|
If the member can't be read for another reason. |
Source code in varagity/backup/bundle.py
1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 | |
verify()
¶
Drain what the reader left, then hold the member to its record.
Raises:
| Type | Description |
|---|---|
BundleDamaged
|
If its size or sha256 differs from what the manifest recorded. |
Source code in varagity/backup/bundle.py
1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 | |
NothingToExport
¶
Bases: RuntimeError
Raised when a corpus holds nothing to back up — an empty bundle is not a backup.
The RAG corpus with no documents row, or a graph whose working
directory has no file to copy (nothing has ever been built).
Source code in varagity/backup/bundle.py
233 234 235 236 237 238 | |
RagExport
dataclass
¶
What one RAG export wrote.
Attributes:
| Name | Type | Description |
|---|---|---|
bundle |
WrittenBundle
|
The published bundle (name, path, size, manifest). |
wall_clock_s |
float
|
Seconds from snapshot to publish. |
Source code in varagity/backup/rag.py
126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 | |
counts
property
¶
The bundle's row counts (documents, chunks).
Returns:
| Type | Description |
|---|---|
dict[str, int]
|
The manifest's counts. |
RagRestore
dataclass
¶
What one RAG restore did.
Attributes:
| Name | Type | Description |
|---|---|---|
bundle_name |
str
|
The restored bundle's file name. |
size_bytes |
int
|
The bundle's size on disk. |
manifest |
BundleManifest
|
Its manifest. |
documents |
int
|
|
chunks |
int
|
|
bm25_indexed |
int
|
Chunks the rebuilt BM25 index holds. |
wall_clock_s |
float
|
Seconds from validation to a searchable index. |
stale |
bool | None
|
The corpus-stale flag the restore left — derived from the
bundle by the |
Source code in varagity/backup/rag.py
517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 | |
counts
property
¶
What the restore wrote (documents, chunks, bm25).
Returns:
| Type | Description |
|---|---|
dict[str, int]
|
The row counts, plus the chunks the BM25 index now holds. |
WrittenBundle
dataclass
¶
A bundle :func:write_bundle published.
Attributes:
| Name | Type | Description |
|---|---|---|
name |
str
|
The bundle's file name. |
path |
Path
|
Where it now lives. |
size_bytes |
int
|
Its size on disk. |
manifest |
BundleManifest
|
The manifest sealed into it. |
Source code in varagity/backup/bundle.py
289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 | |
bundle_name(directory, corpus, *, now=None)
¶
Choose the file name for a new bundle — never one already taken.
varagity-<corpus>-<UTC yyyymmdd-HHMM>.zip; a repeat within the same
minute gets a -2, -3, … suffix rather than replacing a backup.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
directory
|
Path
|
The exports folder the bundle will land in. |
required |
corpus
|
Corpus
|
The corpus being backed up. |
required |
now
|
datetime | None
|
The export's instant (defaults to now); rendered in UTC. |
None
|
Returns:
| Type | Description |
|---|---|
str
|
A name free in |
Source code in varagity/backup/bundle.py
423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 | |
corpus_root(corpus)
¶
The configured root directory of one corpus.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
corpus
|
Corpus
|
The corpus. |
required |
Returns:
| Type | Description |
|---|---|
Path
|
|
Source code in varagity/backup/bundle.py
338 339 340 341 342 343 344 345 346 347 348 | |
derive_corpus_stale(manifest, current)
¶
The corpus-stale flag a restored RAG bundle leaves.
Stale when the bundle was stale at export, or when the settings its rows were made under differ from this stack's — the same "the corpus may not match the current settings" the flag always means.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
manifest
|
BundleManifest
|
The restored bundle's manifest. |
required |
current
|
Mapping[str, str]
|
This stack's reingest-affecting settings, in env-string form. |
required |
Returns:
| Type | Description |
|---|---|
bool
|
Whether the restored corpus is stale. |
Source code in varagity/backup/rag.py
607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 | |
display_path(corpus, name=None)
¶
The human form of a bundle's location — the text "Copy path" copies.
In compose the API sees /app/docs while the user's checkout has
./docs (the bind's host side), so an absolute path would be wrong on
the one machine that matters. The corpus folder's own name, relative,
reads right from the checkout root in both host and container mode.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
corpus
|
Corpus
|
The corpus. |
required |
name
|
str | None
|
A bundle file name; |
None
|
Returns:
| Type | Description |
|---|---|
str
|
E.g. |
str
|
|
Source code in varagity/backup/bundle.py
363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 | |
export_graph(service, dest, *, read_stale, on_progress=None)
¶
Export the message graph's working directory into a new bundle in dest.
Holds the service's write lock for the whole copy — no build, resume or
delete can start until the bundle is published, while reads (graph chat,
/graph) are answered as usual. No model is called and no session is
opened: the files are copied as they are on disk, which is exactly what
an API restart would load.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
service
|
GraphService
|
The process's graph service (its workdir, write lock, and engine format). |
required |
dest
|
Path
|
The exports folder ( |
required |
read_stale
|
Callable[[], bool]
|
Reads the graph-stale flag (the flow passes a Postgres read). Called once, after the last file is copied and before the manifest is sealed, so a source deleted mid-export is recorded; if it raises, the export fails and leaves no bundle. |
required |
on_progress
|
GraphExportProgressCallback | None
|
Called with |
None
|
Returns:
| Type | Description |
|---|---|
GraphExportReport
|
The published bundle and how long it took. |
Raises:
| Type | Description |
|---|---|
GraphBuildInProgress
|
If a build, resume or delete holds the write lock. |
NothingToExport
|
If the workdir holds no file to copy (nothing has been built). |
ValueError
|
If the engine's name could not be the bundle's folder. |
OSError
|
If a file can't be read or the bundle can't be written. |
Source code in varagity/backup/graph.py
262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 | |
export_rag(dest, store, ingest_settings, *, stale, on_progress=None)
¶
Export the whole chunk corpus into a new bundle in dest.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
dest
|
Path
|
The exports folder ( |
required |
store
|
ContextualVectorDB
|
The vector store to snapshot. |
required |
ingest_settings
|
Mapping[str, str]
|
The reingest-affecting settings at export, in env-string form — the runner reads them, so this package never imports the API layer. |
required |
stale
|
bool
|
The corpus-stale flag at export. |
required |
on_progress
|
ProgressCallback | None
|
Called with |
None
|
Returns:
| Type | Description |
|---|---|
RagExport
|
The published bundle and how long it took. |
Raises:
| Type | Description |
|---|---|
NothingToExport
|
If the snapshot holds no documents. |
ValueError
|
If a stored vector's dimension isn't |
OSError
|
If the bundle can't be written. |
Source code in varagity/backup/rag.py
374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 | |
exports_dir(corpus)
¶
The reserved folder a corpus's bundles live in.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
corpus
|
Corpus
|
The corpus. |
required |
Returns:
| Type | Description |
|---|---|
Path
|
|
Source code in varagity/backup/bundle.py
351 352 353 354 355 356 357 358 359 360 | |
graph_counts(workdir)
¶
What a graph bundle holds, by the workdir's own size summary.
The summary sidecar is the graph's size cache (what the status route and the gauges read) — never the graphml, which a count would have to parse.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
workdir
|
Path
|
The engine's working directory. |
required |
Returns:
| Type | Description |
|---|---|
dict[str, int]
|
|
dict[str, int]
|
guids), plus |
dict[str, int]
|
them — or nothing at all when no build has written a summary yet (a |
dict[str, int]
|
first build killed partway leaves files, but no summary). |
Source code in varagity/backup/graph.py
194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 | |
graph_folder(manifest)
¶
The folder a graph bundle's files ride under: the name of the engine that wrote them.
Shared by the verdict's member rule and the restore, which extracts
<folder>/<file> into the workdir as <file>.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
manifest
|
BundleManifest
|
The graph bundle's manifest. |
required |
Returns:
| Type | Description |
|---|---|
str
|
The engine's registry name ( |
Raises:
| Type | Description |
|---|---|
BundleInvalid
|
If the manifest records no engine, or one whose name couldn't be a folder. |
Source code in varagity/backup/bundle.py
896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 | |
ingest_drift(manifest, current)
¶
Name the reingest-affecting settings a RAG bundle differs from this stack on.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
manifest
|
BundleManifest
|
The bundle's manifest. |
required |
current
|
Mapping[str, str]
|
This stack's reingest-affecting settings, in env-string
form ( |
required |
Returns:
| Type | Description |
|---|---|
list[str]
|
The differing setting names, sorted — a setting recorded on only one |
list[str]
|
side counts as differing, and a bundle that recorded no settings |
list[str]
|
differs on all of them. |
Source code in varagity/backup/rag.py
586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 | |
inspect_bundle(path, corpus)
¶
The cheap verdict the backups list shows for one bundle.
Reads the zip's central directory and bundle.json — never a data
member — and checks everything those can tell: the format version, the
corpus, the member allowlist and zip-slip rules, each member's recorded
size against the zip's, the embedding model and dimension against this
stack's settings, and for a graph bundle the engine that wrote its files
against this stack's (:func:running_engine_format). What it can't see
is a member damaged inside an intact zip with unchanged sizes: the
restore's verifying streams (:func:open_bundle) — or
:func:verify_bundle, standalone — hash for that.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
path
|
Path
|
The bundle file. |
required |
corpus
|
Corpus
|
The corpus it would restore. |
required |
Returns:
| Type | Description |
|---|---|
BundleVerdict
|
The verdict (it never raises for a bad file — the problem rides |
BundleVerdict
|
the verdict). |
Source code in varagity/backup/bundle.py
1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 | |
is_bundle_name(name)
¶
Report whether a file name is a listable, servable bundle name.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
name
|
str
|
A bare file name (no directories). |
required |
Returns:
| Type | Description |
|---|---|
bool
|
|
bool
|
in-flight |
bool
|
never listed or served. |
Source code in varagity/backup/bundle.py
385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 | |
is_graph_file_name(name)
¶
Report whether a working-directory file name may ride in a graph bundle.
The graph bundle's member rule (and the rule for the engine folder those members sit under), shared by the export — which must never write a member the restore's verdict would refuse — and the verdict itself.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
name
|
str
|
A bare file (or folder) name. |
required |
Returns:
| Type | Description |
|---|---|
bool
|
|
bool
|
|
bool
|
that could be a path, a hidden file, |
Source code in varagity/backup/bundle.py
405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 | |
list_bundles(directory, corpus)
¶
List the bundles in an exports folder, newest first, each with its verdict.
Hidden files (in-flight partials included) and non-.zip files
are skipped, and so is anything that resolves outside the folder (a
symlink pointing elsewhere is not a bundle this folder holds). A visible
zip that isn't a readable bundle is still listed — with no manifest and
its reason — because the user put it there and the panel should say what
it is.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
directory
|
Path
|
The exports folder (a missing folder lists nothing). |
required |
corpus
|
Corpus
|
The corpus the folder belongs to (what each bundle is judged against). |
required |
Returns:
| Type | Description |
|---|---|
list[BundleListing]
|
One listing per bundle file, newest mtime first. |
Source code in varagity/backup/bundle.py
1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 1459 1460 1461 1462 | |
open_bundle(path, manifest)
¶
Open a bundle to read its members against its manifest.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
path
|
Path
|
The bundle file. |
required |
manifest
|
BundleManifest
|
Its manifest (from :func: |
required |
Yields:
| Type | Description |
|---|---|
BundleSource
|
The open bundle. |
Raises:
| Type | Description |
|---|---|
BundleInvalid
|
If the file can't be opened as a zip. |
Source code in varagity/backup/bundle.py
1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 | |
read_manifest(path)
¶
Read a bundle's manifest — and only its manifest.
The zip's central directory locates bundle.json; no other member is
opened, so listing a folder of large bundles stays cheap.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
path
|
Path
|
The bundle file. |
required |
Returns:
| Type | Description |
|---|---|
BundleManifest
|
The validated manifest. |
Raises:
| Type | Description |
|---|---|
BundleInvalid
|
If the file is not a readable zip, has no
|
BundleIncompatible
|
If the manifest is of another format version. |
Source code in varagity/backup/bundle.py
821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 | |
restore_graph(service, bundle, *, run_id, on_progress=None)
¶
Replace the message graph's working directory with a bundle's.
No model is called and nothing is re-extracted — the bundle is a workdir:
validate— the cheap verdict (the route checked it before the run; this guards a direct call and a file swapped since);extract— every file into a fresh staging folder beside the workdir (:meth:~varagity.graph.service.GraphService.staging_dir, on the same volume), each member's sha256 checked as it is read — the restore's one read of the bundle, and its only hash — and each file synced to disk, so a damaged bundle can never go live;swapandopen— :meth:~varagity.graph.service.GraphService.replace_workdircloses the session, swaps the folders under the service's write and open locks, and opens the restored graph; if it won't open, the previous graph is put back.
Whatever is left of the staging folder is removed however the restore ends; until the swap, the live graph is untouched.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
service
|
GraphService
|
The process's graph service (the workdir's single writer). |
required |
bundle
|
Path
|
The bundle file. |
required |
run_id
|
str
|
The restore run's id (names the folders beside the workdir). |
required |
on_progress
|
GraphRestoreProgressCallback | None
|
Called with |
None
|
Returns:
| Type | Description |
|---|---|
GraphRestore
|
What was restored, and how long it took. |
Raises:
| Type | Description |
|---|---|
BundleInvalid
|
If the bundle is unsound or damaged (the live graph is untouched). |
BundleIncompatible
|
If this stack must not restore it (untouched). |
GraphBuildInProgress
|
If a write holds the graph's write lock at the swap (untouched). |
GraphUnavailable
|
If the restored graph would not open (the previous graph is back in place). |
OSError
|
If a file can't be extracted or a folder can't be moved (the previous graph stays, or is put back). |
Source code in varagity/backup/graph.py
401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 | |
restore_rag(bundle, store, bm25, docs_root, *, on_progress=None)
¶
Replace the chunk corpus with a bundle's rows, then rebuild the BM25 index.
No model is called — nothing is parsed, chunked, contextualized or embedded. Under the corpus's exclusive advisory lock, held until the BM25 rebuild ends (so no ingest in any process can write rows the rebuild would miss, or touch the index while it is gone):
validate— the cheap verdict (the route checked it before the run; this guards a direct call and a file swapped since).load— one transaction empties both tables and streams the bundle's lines into binaryCOPY, re-rooting eachsource; every member's sha256 is checked as it is read — the restore's one read of the bundle, and its only hash — so a damaged bundle (or one a line of which no row can be made from) can never commit.index— the HNSW index is rebuilt and the transaction commits: the restore has taken effect. Any failure before this rolled back, leaving the previous corpus untouched.bm25— the index is recreated and refilled from the restored rows.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
bundle
|
Path
|
The bundle file. |
required |
store
|
ContextualVectorDB
|
The vector store to replace (its connection holds the lock). |
required |
bm25
|
ElasticsearchBM25
|
The BM25 store to rebuild. |
required |
docs_root
|
Path
|
The restoring stack's |
required |
on_progress
|
RestoreProgressCallback | None
|
Called with |
None
|
Returns:
| Type | Description |
|---|---|
RagRestore
|
What was restored, and how long it took. |
Raises:
| Type | Description |
|---|---|
BundleInvalid
|
If the bundle is unsound or damaged (nothing was replaced — the transaction rolled back). |
BundleIncompatible
|
If this stack must not restore it (nothing was replaced). |
CorpusLocked
|
If an ingest holds the corpus lock (nothing was replaced). |
BM25RebuildFailed
|
If the rows were restored but the BM25 index could not be rebuilt. |
Source code in varagity/backup/rag.py
786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 | |
restored_source(source, relative_path, docs_root)
¶
Re-root one document's source onto the restoring stack's corpus directory.
doc_id already hashes the corpus-relative path, so the row's
identity is portable; source (the absolute path the readers resolve
— the document list, previews, "also remove the file") is rebuilt the
way ingest builds it, (DOCS_PATH / relative_path).resolve(). A
document exported from outside the corpus has no relative path and
keeps its original absolute source.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
source
|
str
|
The |
required |
relative_path
|
str | None
|
Its path relative to the exporting |
required |
docs_root
|
Path
|
The resolved |
required |
Returns:
| Type | Description |
|---|---|
str
|
The |
Raises:
| Type | Description |
|---|---|
BundleInvalid
|
If the relative path is absolute, empty, or climbs
out of the corpus ( |
Source code in varagity/backup/rag.py
552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 | |
running_engine_format()
¶
The file format this stack's graph engine reads — what a graph bundle is held to.
The engine reports its own format (GraphEngine.storage_format(), read
from package metadata, so the engine library is never imported), which
keeps the backup code from hardcoding any library's name or version.
Returns:
| Type | Description |
|---|---|
EngineFormat
|
|
Raises:
| Type | Description |
|---|---|
KeyError
|
If |
Source code in varagity/backup/bundle.py
1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 | |
verify_bundle(path, corpus)
¶
Run every check a restore makes, without restoring anything.
:func:inspect_bundle's verdict, then the sha256 of every member —
every byte of the bundle is read once. A restore makes the same checks
as it loads (through :func:open_bundle's verifying streams, so it
reads the bundle only once); this is the standalone form, for a caller
that wants the full verdict without a restore.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
path
|
Path
|
The bundle file. |
required |
corpus
|
Corpus
|
The corpus it would restore. |
required |
Returns:
| Type | Description |
|---|---|
BundleManifest
|
The bundle's manifest, now fully verified. |
Raises:
| Type | Description |
|---|---|
BundleIncompatible
|
If this stack must not restore the bundle. |
BundleDamaged
|
If a member's bytes don't match its recorded sha256. |
BundleInvalid
|
If the bundle is otherwise unsound. |
Source code in varagity/backup/bundle.py
1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 | |
workdir_files(workdir)
¶
List the files a graph export would copy.
What the export route's nothing_to_export check asks — the same
rule the export applies, so the route and the run can never disagree on
whether there is anything to back up.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
workdir
|
Path
|
The engine's working directory (a missing one holds none). |
required |
Returns:
| Type | Description |
|---|---|
list[Path]
|
The workdir's own files, in name order, temp files and anything a |
list[Path]
|
bundle can't carry left out. |
Source code in varagity/backup/graph.py
177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 | |
write_bundle(directory, corpus, *, created_at=None)
¶
Write a new bundle into directory, all-or-nothing.
Sweeps this corpus's stale partials, chooses a free name
(:func:bundle_name), and opens .{name}.partial. When the with
block exits cleanly and the writer was sealed, the partial is synced
and renamed into place with os.replace — same directory, so the
rename is atomic and a listing sees the whole bundle or nothing. Any
exception, or an unsealed writer, deletes the partial instead.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
directory
|
Path
|
The exports folder (created if missing). |
required |
corpus
|
Corpus
|
The corpus being backed up. |
required |
created_at
|
datetime | None
|
The export's instant (defaults to now). |
None
|
Yields:
| Type | Description |
|---|---|
BundleWriter
|
The writer to fill; read :attr: |
Raises:
| Type | Description |
|---|---|
RuntimeError
|
If the block exits without sealing the manifest (the partial is removed; nothing is published). |
OSError
|
If the folder or the bundle can't be written. |
Source code in varagity/backup/bundle.py
677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 | |
varagity.backup.bundle
¶
The backup bundle: one zip per backup, its manifest written last (ADR-019).
A bundle is an ordinary .zip in a corpus root's reserved
varagity-exports/ folder. Its data members stream in first, each hashed
as it is written, and bundle.json — the :class:BundleManifest — is the
last member, so it can record every data member's size and sha256. The
zip's central directory then lets :func:read_manifest open that one member
without touching the (possibly hundreds of megabytes of) rest: listing the
backups panel costs a directory read, not an unpack.
Writing never exposes a half-made file. :func:write_bundle writes into a
hidden .{name}.partial sibling and renames it into place
(os.replace) only once the manifest is sealed and the bytes are synced;
any failure removes the partial instead, and the next export sweeps whatever
a crash left behind.
Listings skip hidden files, so a bundle is either whole or invisible.
Reading has two depths, because hashing is the expensive part:
- :func:
inspect_bundleis the cheap verdict every listed bundle gets — the zip's central directory andbundle.jsonalone: the format version, the corpus, the member allowlist and safety rules, each member's recorded size against the zip's, the embedding model and dimension against this stack's, and for a graph bundle the engine format its files are in. No data member is opened, so a folder of large bundles still lists in milliseconds. - :func:
open_bundlehands out verifying streams: every byte a restore loads passes through a member's sha256, which is checked when the member is drained — the only check that sees a bundle damaged inside an intact zip. A restore reads the bundle exactly once this way, and its writes only take effect after every member checked out (the RAG restore's transaction commits, the graph restore's staged folder is swapped in), so a damaged bundle can never restore. :func:verify_bundleruns the same check standalone — every member hashed, nothing loaded.
Two refusals come out of both: :class:BundleInvalid (not a sound bundle;
:class:BundleDamaged when only its bytes are wrong) and
:class:BundleIncompatible (a sound bundle this stack must not restore —
another embedding space, or a graph engine format this stack's engine may
not read — refused, never converted).
This module knows the container, not the corpora: what goes inside a bundle
is :mod:varagity.backup.rag's and :mod:varagity.backup.graph's business —
except each corpus's member allowlist, which the verdict needs — and nothing
here knows about HTTP. The two allowlists differ in kind: a RAG bundle holds
exactly three named members, while a graph bundle holds whatever files its
engine's working directory held, so its rule is a pattern — flat files with
safe names, under one folder named for the engine (lightrag/…) — and its
manifest records that engine's file format (:attr:BundleManifest.engine).
BUNDLE_FORMAT = 'varagity-backup'
module-attribute
¶
The manifest's format marker: what makes a zip a varagity bundle.
BUNDLE_VERSION = 1
module-attribute
¶
The bundle layout this code writes (and, in time, the versions it reads).
Corpus = Literal['rag', 'graph']
module-attribute
¶
Which corpus a bundle backs up — ChatRequest.corpus's vocabulary.
MANIFEST_NAME = 'bundle.json'
module-attribute
¶
The manifest member — always the zip's last entry.
BundleDamaged
¶
Bases: BundleInvalid
A member's bytes don't match what its bundle recorded.
The one defect the cheap verdict can't see: the zip and its manifest are intact, but a member's sha256 (or the zip's own CRC-32, or its deflate stream) says the content changed after it was written.
Source code in varagity/backup/bundle.py
211 212 213 214 215 216 217 | |
BundleError
¶
Bases: Exception
Base class for a bundle that can't be read or used.
Source code in varagity/backup/bundle.py
203 204 | |
BundleIncompatible
¶
Bases: BundleError
A sound bundle this stack must not restore.
Another bundle format version, another corpus, vectors from another
embedding model or dimension — or, for a graph bundle, files another
graph engine (or another major.minor version of this one) wrote. Neither
store checks the embedding model (pgvector compares only the vector
size, LightRAG only embedding_dim), so restoring a mismatch would
succeed and then retrieve the wrong passages — it is refused instead,
never converted.
Source code in varagity/backup/bundle.py
220 221 222 223 224 225 226 227 228 229 230 | |
BundleInvalid
¶
Bases: BundleError
The file is not a readable varagity bundle (damaged, or not one at all).
Source code in varagity/backup/bundle.py
207 208 | |
BundleListing
dataclass
¶
One bundle file found in an exports folder.
Attributes:
| Name | Type | Description |
|---|---|---|
name |
str
|
The file name. |
path |
Path
|
Its resolved path. |
size_bytes |
int
|
Its size on disk. |
modified_at |
datetime
|
Its mtime (UTC). |
mtime_ns |
int
|
Its mtime in nanoseconds — with |
verdict |
BundleVerdict
|
The cheap inspection's verdict for the listing's corpus. |
Source code in varagity/backup/bundle.py
306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 | |
manifest
property
¶
The bundle's manifest.
Returns:
| Type | Description |
|---|---|
BundleManifest | None
|
The manifest, or |
BundleManifest | None
|
varagity bundle (copied in by hand, damaged, another format …). |
BundleManifest
¶
Bases: BaseModel
bundle.json: what a bundle holds and what it was made under.
Every later phase judges bundles against this record — a restore checks compatibility against it and derives the stale flag from it — so it carries the export-time facts no row records.
Attributes:
| Name | Type | Description |
|---|---|---|
format |
Literal['varagity-backup']
|
Always |
version |
int
|
The bundle layout version (:data: |
corpus |
Corpus
|
Which corpus the bundle backs up. |
created_at |
datetime
|
When the export's snapshot was taken (UTC). |
embedding_model |
str
|
|
embedding_dim |
int
|
|
stale |
bool
|
The corpus's stale flag at export ( |
counts |
dict[str, int]
|
What the bundle holds — rag: |
ingest_settings |
dict[str, str] | None
|
The reingest-affecting settings at export, in
env-string form (rag only; |
engine |
EngineFormat | None
|
The graph engine's file format at export — the workdir is
copied verbatim, so it is only as portable as the engine version
that wrote it (graph only; |
members |
list[BundleMember]
|
Every data member with its size and sha256, in write order. |
Source code in varagity/backup/bundle.py
161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 | |
BundleMember
¶
Bases: BaseModel
One data member of a bundle, as its manifest records it.
Attributes:
| Name | Type | Description |
|---|---|---|
name |
str
|
The member's path inside the zip. |
size_bytes |
int
|
Its uncompressed size. |
sha256 |
str
|
The hex sha256 of its uncompressed bytes — what a restore verifies before it touches a store. |
Source code in varagity/backup/bundle.py
146 147 148 149 150 151 152 153 154 155 156 157 158 | |
BundleSource
¶
An open bundle whose members are read through verifying streams.
Attributes:
| Name | Type | Description |
|---|---|---|
manifest |
The manifest the members are checked against. |
Source code in varagity/backup/bundle.py
1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 | |
__init__(archive, manifest)
¶
Bind the open zip to the manifest it is held to.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
archive
|
ZipFile
|
The open bundle. |
required |
manifest
|
BundleManifest
|
Its (already inspected) manifest. |
required |
Source code in varagity/backup/bundle.py
1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 | |
member(name)
¶
Open one data member for reading, verified when the block exits.
Several members may be open at once (zipfile reads each through its own position in the shared file), which is how a restore reads the chunk lines and their vectors side by side.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
name
|
str
|
The member's name. |
required |
Yields:
| Type | Description |
|---|---|
MemberStream
|
The member's verifying stream. When the |
MemberStream
|
cleanly, whatever the reader left is drained and the member's |
MemberStream
|
size and sha256 are checked against the manifest. |
Raises:
| Type | Description |
|---|---|
BundleInvalid
|
If the manifest records no such member, or the zip can't open it. |
BundleDamaged
|
If the member's bytes don't match its record. |
Source code in varagity/backup/bundle.py
1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 | |
BundleVerdict
dataclass
¶
What the cheap inspection concluded about one bundle.
Attributes:
| Name | Type | Description |
|---|---|---|
manifest |
BundleManifest | None
|
The bundle's manifest, or |
problem |
BundleError | None
|
Why the bundle can't be restored, or |
Source code in varagity/backup/bundle.py
241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 | |
reason
property
¶
The problem, worded for a person.
Returns:
| Type | Description |
|---|---|
str | None
|
The refusal's message, or |
restorable
property
¶
Whether nothing the inspection could see refuses the bundle.
Returns:
| Type | Description |
|---|---|
bool
|
|
manifest_or_raise()
¶
Return the manifest of a restorable bundle, or raise its problem.
Returns:
| Type | Description |
|---|---|
BundleManifest
|
The manifest. |
Raises:
| Type | Description |
|---|---|
BundleInvalid
|
If the bundle is not a sound bundle. |
BundleIncompatible
|
If this stack must not restore it. |
Source code in varagity/backup/bundle.py
272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 | |
BundleWriter
¶
Fills one bundle: data members first, then the sealed manifest.
Obtained from :func:write_bundle; :attr:result is available once its
with block exits cleanly.
Attributes:
| Name | Type | Description |
|---|---|---|
corpus |
The corpus being backed up. |
|
created_at |
The export's instant — the name's stamp and the
manifest's |
Source code in varagity/backup/bundle.py
532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 | |
manifest
property
¶
The sealed manifest (None until :meth:seal runs).
Returns:
| Type | Description |
|---|---|
BundleManifest | None
|
The manifest, or |
result
property
¶
The published bundle.
Returns:
| Name | Type | Description |
|---|---|---|
What |
WrittenBundle
|
func: |
Raises:
| Type | Description |
|---|---|
RuntimeError
|
If the bundle was not (yet) published. |
__init__(archive, corpus, created_at)
¶
Bind the writer to its open partial archive.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
archive
|
ZipFile
|
The partial zip, open for writing. |
required |
corpus
|
Corpus
|
The corpus being backed up. |
required |
created_at
|
datetime
|
The export's instant (UTC). |
required |
Source code in varagity/backup/bundle.py
544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 | |
member(name, *, compress=True)
¶
Stream one data member into the bundle.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
name
|
str
|
The member's path inside the zip. |
required |
compress
|
bool
|
Deflate it (text) or store it as-is (already-dense bytes like a float32 matrix, which deflate can't shrink). |
True
|
Yields:
| Type | Description |
|---|---|
MemberSink
|
The sink to write the member's bytes to; its size and sha256 are |
MemberSink
|
recorded for the manifest when the block exits. |
Raises:
| Type | Description |
|---|---|
ValueError
|
If the name is the manifest's or already used. |
RuntimeError
|
If the manifest is already sealed. |
Source code in varagity/backup/bundle.py
562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 | |
seal(*, embedding_model, embedding_dim, stale, counts, ingest_settings=None, engine=None)
¶
Write bundle.json as the bundle's last member.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
embedding_model
|
str
|
|
required |
embedding_dim
|
int
|
|
required |
stale
|
bool
|
The corpus's stale flag at export. |
required |
counts
|
dict[str, int]
|
What the bundle holds. |
required |
ingest_settings
|
dict[str, str] | None
|
The reingest-affecting settings at export, if the corpus has them. |
None
|
engine
|
EngineFormat | None
|
The graph engine's file format, if the corpus is the graph. |
None
|
Returns:
| Type | Description |
|---|---|
BundleManifest
|
The manifest, as written. |
Raises:
| Type | Description |
|---|---|
RuntimeError
|
If the manifest is already sealed. |
Source code in varagity/backup/bundle.py
595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 | |
MemberSink
¶
The write end of one bundle member, hashing and counting as it goes.
Attributes:
| Name | Type | Description |
|---|---|---|
size_bytes |
Bytes written so far (uncompressed). |
Source code in varagity/backup/bundle.py
491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 | |
__init__(raw)
¶
Wrap a zip member's write stream.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
raw
|
IO[bytes]
|
The stream |
required |
Source code in varagity/backup/bundle.py
498 499 500 501 502 503 504 505 506 | |
hexdigest()
¶
The sha256 of everything written so far.
Returns:
| Type | Description |
|---|---|
str
|
The hex digest. |
Source code in varagity/backup/bundle.py
523 524 525 526 527 528 529 | |
write(data)
¶
Append bytes to the member.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
data
|
bytes | bytearray | memoryview
|
The next slice of the member's content. |
required |
Returns:
| Type | Description |
|---|---|
int
|
The number of bytes written. |
Source code in varagity/backup/bundle.py
508 509 510 511 512 513 514 515 516 517 518 519 520 521 | |
MemberStream
¶
The read end of one bundle member, hashed and counted as it is read.
Every byte handed to the reader passes through :meth:read or
:meth:readline, so once the member is drained the digest covers it
whole; :meth:verify then holds it to the manifest's record.
Attributes:
| Name | Type | Description |
|---|---|---|
name |
The member's name. |
Source code in varagity/backup/bundle.py
1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 | |
__init__(raw, record)
¶
Wrap a member's decompressing read stream.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
raw
|
IO[bytes]
|
The stream |
required |
record
|
BundleMember
|
What the manifest recorded for the member. |
required |
Source code in varagity/backup/bundle.py
1207 1208 1209 1210 1211 1212 1213 1214 1215 1216 1217 1218 | |
__iter__()
¶
Iterate the member's lines (a JSONL member's records).
Yields:
| Type | Description |
|---|---|
bytes
|
Each line, newline included. |
Source code in varagity/backup/bundle.py
1284 1285 1286 1287 1288 1289 1290 1291 | |
read(size=-1)
¶
Read up to size bytes (all that is left when negative).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
size
|
int
|
The most bytes to return. |
-1
|
Returns:
| Type | Description |
|---|---|
bytes
|
The bytes; |
Raises:
| Type | Description |
|---|---|
BundleDamaged
|
If the member's bytes fail the zip's own checks. |
BundleInvalid
|
If the member can't be read for another reason. |
Source code in varagity/backup/bundle.py
1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 | |
readline()
¶
Read one line, newline included.
Returns:
| Type | Description |
|---|---|
bytes
|
The line; |
Raises:
| Type | Description |
|---|---|
BundleDamaged
|
If the member's bytes fail the zip's own checks. |
BundleInvalid
|
If the member can't be read for another reason. |
Source code in varagity/backup/bundle.py
1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 | |
verify()
¶
Drain what the reader left, then hold the member to its record.
Raises:
| Type | Description |
|---|---|
BundleDamaged
|
If its size or sha256 differs from what the manifest recorded. |
Source code in varagity/backup/bundle.py
1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 | |
NothingToExport
¶
Bases: RuntimeError
Raised when a corpus holds nothing to back up — an empty bundle is not a backup.
The RAG corpus with no documents row, or a graph whose working
directory has no file to copy (nothing has ever been built).
Source code in varagity/backup/bundle.py
233 234 235 236 237 238 | |
WrittenBundle
dataclass
¶
A bundle :func:write_bundle published.
Attributes:
| Name | Type | Description |
|---|---|---|
name |
str
|
The bundle's file name. |
path |
Path
|
Where it now lives. |
size_bytes |
int
|
Its size on disk. |
manifest |
BundleManifest
|
The manifest sealed into it. |
Source code in varagity/backup/bundle.py
289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 | |
bundle_name(directory, corpus, *, now=None)
¶
Choose the file name for a new bundle — never one already taken.
varagity-<corpus>-<UTC yyyymmdd-HHMM>.zip; a repeat within the same
minute gets a -2, -3, … suffix rather than replacing a backup.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
directory
|
Path
|
The exports folder the bundle will land in. |
required |
corpus
|
Corpus
|
The corpus being backed up. |
required |
now
|
datetime | None
|
The export's instant (defaults to now); rendered in UTC. |
None
|
Returns:
| Type | Description |
|---|---|
str
|
A name free in |
Source code in varagity/backup/bundle.py
423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 | |
corpus_root(corpus)
¶
The configured root directory of one corpus.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
corpus
|
Corpus
|
The corpus. |
required |
Returns:
| Type | Description |
|---|---|
Path
|
|
Source code in varagity/backup/bundle.py
338 339 340 341 342 343 344 345 346 347 348 | |
display_path(corpus, name=None)
¶
The human form of a bundle's location — the text "Copy path" copies.
In compose the API sees /app/docs while the user's checkout has
./docs (the bind's host side), so an absolute path would be wrong on
the one machine that matters. The corpus folder's own name, relative,
reads right from the checkout root in both host and container mode.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
corpus
|
Corpus
|
The corpus. |
required |
name
|
str | None
|
A bundle file name; |
None
|
Returns:
| Type | Description |
|---|---|
str
|
E.g. |
str
|
|
Source code in varagity/backup/bundle.py
363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 | |
exports_dir(corpus)
¶
The reserved folder a corpus's bundles live in.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
corpus
|
Corpus
|
The corpus. |
required |
Returns:
| Type | Description |
|---|---|
Path
|
|
Source code in varagity/backup/bundle.py
351 352 353 354 355 356 357 358 359 360 | |
graph_folder(manifest)
¶
The folder a graph bundle's files ride under: the name of the engine that wrote them.
Shared by the verdict's member rule and the restore, which extracts
<folder>/<file> into the workdir as <file>.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
manifest
|
BundleManifest
|
The graph bundle's manifest. |
required |
Returns:
| Type | Description |
|---|---|
str
|
The engine's registry name ( |
Raises:
| Type | Description |
|---|---|
BundleInvalid
|
If the manifest records no engine, or one whose name couldn't be a folder. |
Source code in varagity/backup/bundle.py
896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 | |
inspect_bundle(path, corpus)
¶
The cheap verdict the backups list shows for one bundle.
Reads the zip's central directory and bundle.json — never a data
member — and checks everything those can tell: the format version, the
corpus, the member allowlist and zip-slip rules, each member's recorded
size against the zip's, the embedding model and dimension against this
stack's settings, and for a graph bundle the engine that wrote its files
against this stack's (:func:running_engine_format). What it can't see
is a member damaged inside an intact zip with unchanged sizes: the
restore's verifying streams (:func:open_bundle) — or
:func:verify_bundle, standalone — hash for that.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
path
|
Path
|
The bundle file. |
required |
corpus
|
Corpus
|
The corpus it would restore. |
required |
Returns:
| Type | Description |
|---|---|
BundleVerdict
|
The verdict (it never raises for a bad file — the problem rides |
BundleVerdict
|
the verdict). |
Source code in varagity/backup/bundle.py
1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 | |
is_bundle_name(name)
¶
Report whether a file name is a listable, servable bundle name.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
name
|
str
|
A bare file name (no directories). |
required |
Returns:
| Type | Description |
|---|---|
bool
|
|
bool
|
in-flight |
bool
|
never listed or served. |
Source code in varagity/backup/bundle.py
385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 | |
is_graph_file_name(name)
¶
Report whether a working-directory file name may ride in a graph bundle.
The graph bundle's member rule (and the rule for the engine folder those members sit under), shared by the export — which must never write a member the restore's verdict would refuse — and the verdict itself.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
name
|
str
|
A bare file (or folder) name. |
required |
Returns:
| Type | Description |
|---|---|
bool
|
|
bool
|
|
bool
|
that could be a path, a hidden file, |
Source code in varagity/backup/bundle.py
405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 | |
list_bundles(directory, corpus)
¶
List the bundles in an exports folder, newest first, each with its verdict.
Hidden files (in-flight partials included) and non-.zip files
are skipped, and so is anything that resolves outside the folder (a
symlink pointing elsewhere is not a bundle this folder holds). A visible
zip that isn't a readable bundle is still listed — with no manifest and
its reason — because the user put it there and the panel should say what
it is.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
directory
|
Path
|
The exports folder (a missing folder lists nothing). |
required |
corpus
|
Corpus
|
The corpus the folder belongs to (what each bundle is judged against). |
required |
Returns:
| Type | Description |
|---|---|
list[BundleListing]
|
One listing per bundle file, newest mtime first. |
Source code in varagity/backup/bundle.py
1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 1459 1460 1461 1462 | |
open_bundle(path, manifest)
¶
Open a bundle to read its members against its manifest.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
path
|
Path
|
The bundle file. |
required |
manifest
|
BundleManifest
|
Its manifest (from :func: |
required |
Yields:
| Type | Description |
|---|---|
BundleSource
|
The open bundle. |
Raises:
| Type | Description |
|---|---|
BundleInvalid
|
If the file can't be opened as a zip. |
Source code in varagity/backup/bundle.py
1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 | |
read_manifest(path)
¶
Read a bundle's manifest — and only its manifest.
The zip's central directory locates bundle.json; no other member is
opened, so listing a folder of large bundles stays cheap.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
path
|
Path
|
The bundle file. |
required |
Returns:
| Type | Description |
|---|---|
BundleManifest
|
The validated manifest. |
Raises:
| Type | Description |
|---|---|
BundleInvalid
|
If the file is not a readable zip, has no
|
BundleIncompatible
|
If the manifest is of another format version. |
Source code in varagity/backup/bundle.py
821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 | |
running_engine_format()
¶
The file format this stack's graph engine reads — what a graph bundle is held to.
The engine reports its own format (GraphEngine.storage_format(), read
from package metadata, so the engine library is never imported), which
keeps the backup code from hardcoding any library's name or version.
Returns:
| Type | Description |
|---|---|
EngineFormat
|
|
Raises:
| Type | Description |
|---|---|
KeyError
|
If |
Source code in varagity/backup/bundle.py
1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 | |
sweep_partials(directory, corpus)
¶
Remove partial bundles an interrupted export of corpus left behind.
Safe because only one export per corpus runs at a time (the corpus gate), and only this corpus's prefix is touched — another corpus's export into the same folder is never disturbed.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
directory
|
Path
|
The exports folder. |
required |
corpus
|
Corpus
|
The corpus whose partials to sweep. |
required |
Returns:
| Type | Description |
|---|---|
int
|
How many partials were removed. |
Source code in varagity/backup/bundle.py
463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 | |
verify_bundle(path, corpus)
¶
Run every check a restore makes, without restoring anything.
:func:inspect_bundle's verdict, then the sha256 of every member —
every byte of the bundle is read once. A restore makes the same checks
as it loads (through :func:open_bundle's verifying streams, so it
reads the bundle only once); this is the standalone form, for a caller
that wants the full verdict without a restore.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
path
|
Path
|
The bundle file. |
required |
corpus
|
Corpus
|
The corpus it would restore. |
required |
Returns:
| Type | Description |
|---|---|
BundleManifest
|
The bundle's manifest, now fully verified. |
Raises:
| Type | Description |
|---|---|
BundleIncompatible
|
If this stack must not restore the bundle. |
BundleDamaged
|
If a member's bytes don't match its recorded sha256. |
BundleInvalid
|
If the bundle is otherwise unsound. |
Source code in varagity/backup/bundle.py
1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 | |
write_bundle(directory, corpus, *, created_at=None)
¶
Write a new bundle into directory, all-or-nothing.
Sweeps this corpus's stale partials, chooses a free name
(:func:bundle_name), and opens .{name}.partial. When the with
block exits cleanly and the writer was sealed, the partial is synced
and renamed into place with os.replace — same directory, so the
rename is atomic and a listing sees the whole bundle or nothing. Any
exception, or an unsealed writer, deletes the partial instead.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
directory
|
Path
|
The exports folder (created if missing). |
required |
corpus
|
Corpus
|
The corpus being backed up. |
required |
created_at
|
datetime | None
|
The export's instant (defaults to now). |
None
|
Yields:
| Type | Description |
|---|---|
BundleWriter
|
The writer to fill; read :attr: |
Raises:
| Type | Description |
|---|---|
RuntimeError
|
If the block exits without sealing the manifest (the partial is removed; nothing is published). |
OSError
|
If the folder or the bundle can't be written. |
Source code in varagity/backup/bundle.py
677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 | |
varagity.backup.rag
¶
The RAG bundle: every documents and chunks row, with its vector (ADR-019).
A RAG backup is the smallest thing that restores the chunk corpus with no
model call: the rows pgvector holds (contextual blurbs included) and their
embeddings. The BM25 index is deliberately absent — every field of an
Elasticsearch document is a copy of a chunks column, so a restore
rebuilds it from these same rows.
The bundle's data members, in write order::
documents.jsonl one documents row per line, plus its relative_path deflated
chunks.jsonl one chunks row per line, every column but the vector deflated
embeddings.f32 N × EMBEDDING_DIM little-endian float32 — row i is stored
the vector of chunks.jsonl line i
relative_path is each document's source relative to DOCS_PATH
(null for a document ingested from elsewhere): doc_id already hashes
it, and it is what lets a restore re-root source onto whatever corpus
directory the restoring stack has. Everything is read from one corpus
snapshot (:meth:~varagity.stores.vector_store.ContextualVectorDB.iter_corpus),
so the three members always describe the same moment.
A restore (:func:restore_rag) turns a bundle back into rows without a
model call. Under the corpus's exclusive advisory lock, one Postgres
transaction replaces both tables — the bundle's lines streamed straight into
binary COPY, each member's sha256 checked again as it is read — and its
commit is where the restore takes effect. The BM25 index is then rebuilt
from the restored rows. The corpus-stale flag it leaves comes from the
bundle (:func:derive_corpus_stale).
ExportStage = Literal['snapshot', 'documents', 'chunks', 'finalize']
module-attribute
¶
The export's progress stages, in order.
ProgressCallback = Callable[[ExportStage, int | None, int | None], None]
module-attribute
¶
(stage, current, total) — how an export reports where it is.
RestoreProgressCallback = Callable[[RestoreStage, int | None, int | None], None]
module-attribute
¶
(stage, current, total) — how a restore reports where it is.
RestoreStage = Literal['validate', 'load', 'index', 'bm25']
module-attribute
¶
The restore's progress stages, in order.
BM25RebuildFailed
¶
Bases: RuntimeError
The rows are restored, but the BM25 index could not be rebuilt.
The Postgres commit already happened, so the restore took effect; restoring the same bundle again rebuilds the index (and rewrites the identical rows on the way).
Attributes:
| Name | Type | Description |
|---|---|---|
manifest |
The restored bundle's manifest — the rows now in the
corpus are its rows, so the corpus-stale flag must still be
derived from it ( |
Source code in varagity/backup/rag.py
101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 | |
__init__(message, manifest=None)
¶
Word the failure and keep the manifest of the rows that did commit.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
message
|
str
|
What failed, and how to repair it. |
required |
manifest
|
BundleManifest | None
|
The restored bundle's manifest. |
None
|
Source code in varagity/backup/rag.py
115 116 117 118 119 120 121 122 123 | |
RagExport
dataclass
¶
What one RAG export wrote.
Attributes:
| Name | Type | Description |
|---|---|---|
bundle |
WrittenBundle
|
The published bundle (name, path, size, manifest). |
wall_clock_s |
float
|
Seconds from snapshot to publish. |
Source code in varagity/backup/rag.py
126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 | |
counts
property
¶
The bundle's row counts (documents, chunks).
Returns:
| Type | Description |
|---|---|
dict[str, int]
|
The manifest's counts. |
RagRestore
dataclass
¶
What one RAG restore did.
Attributes:
| Name | Type | Description |
|---|---|---|
bundle_name |
str
|
The restored bundle's file name. |
size_bytes |
int
|
The bundle's size on disk. |
manifest |
BundleManifest
|
Its manifest. |
documents |
int
|
|
chunks |
int
|
|
bm25_indexed |
int
|
Chunks the rebuilt BM25 index holds. |
wall_clock_s |
float
|
Seconds from validation to a searchable index. |
stale |
bool | None
|
The corpus-stale flag the restore left — derived from the
bundle by the |
Source code in varagity/backup/rag.py
517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 | |
counts
property
¶
What the restore wrote (documents, chunks, bm25).
Returns:
| Type | Description |
|---|---|
dict[str, int]
|
The row counts, plus the chunks the BM25 index now holds. |
chunk_record(row)
¶
Render one chunks row as its chunks.jsonl object.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
row
|
CorpusChunkRow
|
The snapshot row. |
required |
Returns:
| Type | Description |
|---|---|
dict[str, Any]
|
Every column except the embedding (which rides |
dict[str, Any]
|
at the same row index). |
Source code in varagity/backup/rag.py
237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 | |
derive_corpus_stale(manifest, current)
¶
The corpus-stale flag a restored RAG bundle leaves.
Stale when the bundle was stale at export, or when the settings its rows were made under differ from this stack's — the same "the corpus may not match the current settings" the flag always means.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
manifest
|
BundleManifest
|
The restored bundle's manifest. |
required |
current
|
Mapping[str, str]
|
This stack's reingest-affecting settings, in env-string form. |
required |
Returns:
| Type | Description |
|---|---|
bool
|
Whether the restored corpus is stale. |
Source code in varagity/backup/rag.py
607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 | |
document_record(row, docs_root)
¶
Render one documents row as its documents.jsonl object.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
row
|
CorpusDocumentRow
|
The snapshot row. |
required |
docs_root
|
Path
|
The resolved |
required |
Returns:
| Type | Description |
|---|---|
dict[str, Any]
|
Every column, plus |
dict[str, Any]
|
outside the corpus directory). |
Source code in varagity/backup/rag.py
214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 | |
export_rag(dest, store, ingest_settings, *, stale, on_progress=None)
¶
Export the whole chunk corpus into a new bundle in dest.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
dest
|
Path
|
The exports folder ( |
required |
store
|
ContextualVectorDB
|
The vector store to snapshot. |
required |
ingest_settings
|
Mapping[str, str]
|
The reingest-affecting settings at export, in env-string form — the runner reads them, so this package never imports the API layer. |
required |
stale
|
bool
|
The corpus-stale flag at export. |
required |
on_progress
|
ProgressCallback | None
|
Called with |
None
|
Returns:
| Type | Description |
|---|---|
RagExport
|
The published bundle and how long it took. |
Raises:
| Type | Description |
|---|---|
NothingToExport
|
If the snapshot holds no documents. |
ValueError
|
If a stored vector's dimension isn't |
OSError
|
If the bundle can't be written. |
Source code in varagity/backup/rag.py
374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 | |
ingest_drift(manifest, current)
¶
Name the reingest-affecting settings a RAG bundle differs from this stack on.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
manifest
|
BundleManifest
|
The bundle's manifest. |
required |
current
|
Mapping[str, str]
|
This stack's reingest-affecting settings, in env-string
form ( |
required |
Returns:
| Type | Description |
|---|---|
list[str]
|
The differing setting names, sorted — a setting recorded on only one |
list[str]
|
side counts as differing, and a bundle that recorded no settings |
list[str]
|
differs on all of them. |
Source code in varagity/backup/rag.py
586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 | |
restore_rag(bundle, store, bm25, docs_root, *, on_progress=None)
¶
Replace the chunk corpus with a bundle's rows, then rebuild the BM25 index.
No model is called — nothing is parsed, chunked, contextualized or embedded. Under the corpus's exclusive advisory lock, held until the BM25 rebuild ends (so no ingest in any process can write rows the rebuild would miss, or touch the index while it is gone):
validate— the cheap verdict (the route checked it before the run; this guards a direct call and a file swapped since).load— one transaction empties both tables and streams the bundle's lines into binaryCOPY, re-rooting eachsource; every member's sha256 is checked as it is read — the restore's one read of the bundle, and its only hash — so a damaged bundle (or one a line of which no row can be made from) can never commit.index— the HNSW index is rebuilt and the transaction commits: the restore has taken effect. Any failure before this rolled back, leaving the previous corpus untouched.bm25— the index is recreated and refilled from the restored rows.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
bundle
|
Path
|
The bundle file. |
required |
store
|
ContextualVectorDB
|
The vector store to replace (its connection holds the lock). |
required |
bm25
|
ElasticsearchBM25
|
The BM25 store to rebuild. |
required |
docs_root
|
Path
|
The restoring stack's |
required |
on_progress
|
RestoreProgressCallback | None
|
Called with |
None
|
Returns:
| Type | Description |
|---|---|
RagRestore
|
What was restored, and how long it took. |
Raises:
| Type | Description |
|---|---|
BundleInvalid
|
If the bundle is unsound or damaged (nothing was replaced — the transaction rolled back). |
BundleIncompatible
|
If this stack must not restore it (nothing was replaced). |
CorpusLocked
|
If an ingest holds the corpus lock (nothing was replaced). |
BM25RebuildFailed
|
If the rows were restored but the BM25 index could not be rebuilt. |
Source code in varagity/backup/rag.py
786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 | |
restored_source(source, relative_path, docs_root)
¶
Re-root one document's source onto the restoring stack's corpus directory.
doc_id already hashes the corpus-relative path, so the row's
identity is portable; source (the absolute path the readers resolve
— the document list, previews, "also remove the file") is rebuilt the
way ingest builds it, (DOCS_PATH / relative_path).resolve(). A
document exported from outside the corpus has no relative path and
keeps its original absolute source.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
source
|
str
|
The |
required |
relative_path
|
str | None
|
Its path relative to the exporting |
required |
docs_root
|
Path
|
The resolved |
required |
Returns:
| Type | Description |
|---|---|
str
|
The |
Raises:
| Type | Description |
|---|---|
BundleInvalid
|
If the relative path is absolute, empty, or climbs
out of the corpus ( |
Source code in varagity/backup/rag.py
552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 | |
vector_bytes(vector, dim, chunk_id)
¶
Encode one vector as a little-endian float32 matrix row.
pgvector's binary form is a >HH header (dimensions, unused) and
big-endian float4s; one byte swap turns that payload into the
little-endian row the bundle stores, on any host, without a decimal
round trip — the bits are the stored bits.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
vector
|
Vector
|
The stored vector. |
required |
dim
|
int
|
The dimension the bundle declares ( |
required |
chunk_id
|
str
|
The owning chunk (for the error message). |
required |
Returns:
| Type | Description |
|---|---|
bytes
|
|
Raises:
| Type | Description |
|---|---|
ValueError
|
If the vector's dimension isn't |
Source code in varagity/backup/rag.py
260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 | |
vector_from_bytes(raw, dim)
¶
Decode one little-endian float32 matrix row back into a vector, bit-exact.
The inverse of :func:vector_bytes: one byte swap turns the row into
pgvector's big-endian payload, on any host.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
raw
|
bytes
|
|
required |
dim
|
int
|
The vector's dimension. |
required |
Returns:
| Type | Description |
|---|---|
Vector
|
The vector. |
Source code in varagity/backup/rag.py
625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 | |
varagity.backup.graph
¶
The graph bundle: the engine's working directory, verbatim (ADR-019).
A graph backup is the smallest thing that restores the message graph with no
model call — and for the graph that is the whole working directory. Its
files are the extracted graph itself, its vector indexes, the extracted text
and chunk tracking, the doc-status resume ledger, and the LLM response cache
that a later incremental build rebuilds shared entities from; beside them sit
the repo's two sidecars, the build manifest that keeps the next build a
diffing upsert and the size summary. Nothing is converted or re-derived —
every file goes into the bundle byte for byte — so a bundle is tied to the
engine version that wrote it, which is why its manifest records that
engine's file format (:attr:~varagity.backup.bundle.BundleManifest.engine).
The bundle's data members, under one folder named for the engine::
lightrag/ the workdir verbatim, *.tmp* skipped deflated
├── kv_store_*.json LightRAG's key-value stores (doc status, the LLM cache …)
├── vdb_*.json its vector indexes
├── graph_chunk_entity_relation.graphml
├── varagity_manifest.json the build diff
└── varagity_graph_summary.json the size cache — the bundle's counts
The write lock does the hard part. :func:export_graph copies the files
under :meth:~varagity.graph.service.GraphService.writing, the lock every
build, resume and delete takes, so no build can change the graph mid-copy —
while reads take no lock, so chat and /graph keep answering throughout.
A read does write one file: LightRAG flushes its LLM response cache to disk
after every query. That is harmless, because the engine writes each file to
a *.tmp* sibling and renames it into place: every member is a whole
file, from before a query or after it, and a cache one query newer than the
rest of the graph is still a valid cache. The *.tmp* siblings are never
copied.
The stale flag is read last. Deleting a graph source flags the graph stale without taking any lock, so the flag is read once every file is copied, just before the manifest is sealed: a source deleted mid-export is then recorded. Only a completed rebuild clears the flag, and the corpus gate keeps rebuilds out for the whole export, so a late read is never less accurate than an early one.
A restore is extract, swap and open (:func:restore_graph), with no
model call. The bundle's files are extracted — each member's sha256 checked
again as it is read — into a staging folder beside the workdir, on the same
volume, and :meth:~varagity.graph.service.GraphService.replace_workdir
swaps it in under the service's write and open locks, opens it, and puts
the previous graph back if it won't open. The graph-stale flag the restore
leaves is the bundle's own (the graph-restore flow writes it).
GraphExportProgressCallback = Callable[[GraphExportStage, int | None, int | None], None]
module-attribute
¶
(stage, current, total) — how a graph export reports where it is (pack counts bytes).
GraphExportStage = Literal['snapshot', 'pack', 'finalize']
module-attribute
¶
The graph export's progress stages, in order.
GraphRestoreProgressCallback = Callable[[GraphRestoreStage, int | None, int | None], None]
module-attribute
¶
(stage, current, total) — how a graph restore reports progress (extract counts bytes).
GraphRestoreStage = Literal['validate', 'extract', 'swap', 'open']
module-attribute
¶
The graph restore's progress stages, in order.
GraphExportReport
dataclass
¶
What one graph export wrote.
Attributes:
| Name | Type | Description |
|---|---|---|
bundle |
WrittenBundle
|
The published bundle (name, path, size, manifest). |
files |
int
|
How many workdir files it holds. |
wall_clock_s |
float
|
Seconds from the export's start (the write lock is taken at once) to the published bundle. |
Source code in varagity/backup/graph.py
97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 | |
counts
property
¶
What the bundle holds, by the graph's own summary (see :func:graph_counts).
Returns:
| Type | Description |
|---|---|
dict[str, int]
|
The manifest's counts. |
GraphRestore
dataclass
¶
What one graph restore did.
Attributes:
| Name | Type | Description |
|---|---|---|
bundle_name |
str
|
The restored bundle's file name. |
size_bytes |
int
|
The bundle's size on disk. |
manifest |
BundleManifest
|
Its manifest. |
files |
int
|
How many workdir files it restored. |
stats |
GraphStats
|
The restored graph's size, as its freshly opened session reported it. |
wall_clock_s |
float
|
Seconds from validation to an open graph. |
stale |
bool | None
|
The graph-stale flag the restore left — the bundle's own,
written by the |
Source code in varagity/backup/graph.py
366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 | |
counts
property
¶
What the restored graph holds, by the bundle's manifest (its size summary at export).
Returns:
| Type | Description |
|---|---|
dict[str, int]
|
|
dict[str, int]
|
at export — |
export_graph(service, dest, *, read_stale, on_progress=None)
¶
Export the message graph's working directory into a new bundle in dest.
Holds the service's write lock for the whole copy — no build, resume or
delete can start until the bundle is published, while reads (graph chat,
/graph) are answered as usual. No model is called and no session is
opened: the files are copied as they are on disk, which is exactly what
an API restart would load.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
service
|
GraphService
|
The process's graph service (its workdir, write lock, and engine format). |
required |
dest
|
Path
|
The exports folder ( |
required |
read_stale
|
Callable[[], bool]
|
Reads the graph-stale flag (the flow passes a Postgres read). Called once, after the last file is copied and before the manifest is sealed, so a source deleted mid-export is recorded; if it raises, the export fails and leaves no bundle. |
required |
on_progress
|
GraphExportProgressCallback | None
|
Called with |
None
|
Returns:
| Type | Description |
|---|---|
GraphExportReport
|
The published bundle and how long it took. |
Raises:
| Type | Description |
|---|---|
GraphBuildInProgress
|
If a build, resume or delete holds the write lock. |
NothingToExport
|
If the workdir holds no file to copy (nothing has been built). |
ValueError
|
If the engine's name could not be the bundle's folder. |
OSError
|
If a file can't be read or the bundle can't be written. |
Source code in varagity/backup/graph.py
262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 | |
graph_counts(workdir)
¶
What a graph bundle holds, by the workdir's own size summary.
The summary sidecar is the graph's size cache (what the status route and the gauges read) — never the graphml, which a count would have to parse.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
workdir
|
Path
|
The engine's working directory. |
required |
Returns:
| Type | Description |
|---|---|
dict[str, int]
|
|
dict[str, int]
|
guids), plus |
dict[str, int]
|
them — or nothing at all when no build has written a summary yet (a |
dict[str, int]
|
first build killed partway leaves files, but no summary). |
Source code in varagity/backup/graph.py
194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 | |
is_temp_file(name)
¶
Report whether a workdir file is an engine's in-flight write.
LightRAG writes <file>.tmp.<pid>.<thread>.<ns> beside a file and
renames it into place; the repo's sidecars use <file>.<pid>.tmp. A
leftover one is a crash's debris, never part of the graph.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
name
|
str
|
A file name in the workdir. |
required |
Returns:
| Type | Description |
|---|---|
bool
|
|
Source code in varagity/backup/graph.py
122 123 124 125 126 127 128 129 130 131 132 133 134 135 | |
restore_graph(service, bundle, *, run_id, on_progress=None)
¶
Replace the message graph's working directory with a bundle's.
No model is called and nothing is re-extracted — the bundle is a workdir:
validate— the cheap verdict (the route checked it before the run; this guards a direct call and a file swapped since);extract— every file into a fresh staging folder beside the workdir (:meth:~varagity.graph.service.GraphService.staging_dir, on the same volume), each member's sha256 checked as it is read — the restore's one read of the bundle, and its only hash — and each file synced to disk, so a damaged bundle can never go live;swapandopen— :meth:~varagity.graph.service.GraphService.replace_workdircloses the session, swaps the folders under the service's write and open locks, and opens the restored graph; if it won't open, the previous graph is put back.
Whatever is left of the staging folder is removed however the restore ends; until the swap, the live graph is untouched.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
service
|
GraphService
|
The process's graph service (the workdir's single writer). |
required |
bundle
|
Path
|
The bundle file. |
required |
run_id
|
str
|
The restore run's id (names the folders beside the workdir). |
required |
on_progress
|
GraphRestoreProgressCallback | None
|
Called with |
None
|
Returns:
| Type | Description |
|---|---|
GraphRestore
|
What was restored, and how long it took. |
Raises:
| Type | Description |
|---|---|
BundleInvalid
|
If the bundle is unsound or damaged (the live graph is untouched). |
BundleIncompatible
|
If this stack must not restore it (untouched). |
GraphBuildInProgress
|
If a write holds the graph's write lock at the swap (untouched). |
GraphUnavailable
|
If the restored graph would not open (the previous graph is back in place). |
OSError
|
If a file can't be extracted or a folder can't be moved (the previous graph stays, or is put back). |
Source code in varagity/backup/graph.py
401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 | |
workdir_files(workdir)
¶
List the files a graph export would copy.
What the export route's nothing_to_export check asks — the same
rule the export applies, so the route and the run can never disagree on
whether there is anything to back up.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
workdir
|
Path
|
The engine's working directory (a missing one holds none). |
required |
Returns:
| Type | Description |
|---|---|
list[Path]
|
The workdir's own files, in name order, temp files and anything a |
list[Path]
|
bundle can't carry left out. |
Source code in varagity/backup/graph.py
177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 | |