beaver-db 0.18.3__py3-none-any.whl → 0.18.5__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of beaver-db might be problematic. Click here for more details.

beaver/__init__.py CHANGED
@@ -2,4 +2,4 @@ from .core import BeaverDB
2
2
  from .types import Model
3
3
  from .collections import Document, WalkDirection
4
4
 
5
- __version__ = "0.18.3"
5
+ __version__ = "0.18.4"
beaver/collections.py CHANGED
@@ -15,6 +15,7 @@ except ImportError:
15
15
 
16
16
  # --- Fuzzy Search Helper Functions ---
17
17
 
18
+
18
19
  def _levenshtein_distance(s1: str, s2: str) -> int:
19
20
  """Calculates the Levenshtein distance between two strings."""
20
21
  if len(s1) < len(s2):
@@ -38,7 +39,7 @@ def _get_trigrams(text: str) -> set[str]:
38
39
  """Generates a set of 3-character trigrams from a string."""
39
40
  if not text or len(text) < 3:
40
41
  return set()
41
- return {text[i:i+3] for i in range(len(text) - 2)}
42
+ return {text[i : i + 3] for i in range(len(text) - 2)}
42
43
 
43
44
 
44
45
  def _sliding_window_levenshtein(query: str, content: str, fuzziness: int) -> int:
@@ -52,7 +53,7 @@ def _sliding_window_levenshtein(query: str, content: str, fuzziness: int) -> int
52
53
  if query_len == 0:
53
54
  return 0
54
55
 
55
- min_dist = float('inf')
56
+ min_dist = float("inf")
56
57
  query_norm = " ".join(query_tokens)
57
58
 
58
59
  # The window size can be slightly smaller or larger than the query length
@@ -61,7 +62,7 @@ def _sliding_window_levenshtein(query: str, content: str, fuzziness: int) -> int
61
62
  if window_size > len(content_tokens):
62
63
  continue
63
64
  for i in range(len(content_tokens) - window_size + 1):
64
- window_text = " ".join(content_tokens[i:i+window_size])
65
+ window_text = " ".join(content_tokens[i : i + window_size])
65
66
  dist = _levenshtein_distance(query_norm, window_text)
66
67
  if dist < min_dist:
67
68
  min_dist = dist
@@ -93,10 +94,18 @@ class Document(Model):
93
94
 
94
95
  super().__init__(**metadata)
95
96
 
96
- def to_dict(self) -> dict[str, Any]:
97
+ def to_dict(self, *, metadata_only: bool = True) -> dict[str, Any]:
97
98
  """Serializes the document's metadata to a dictionary."""
98
99
  metadata = self.__dict__.copy()
99
- metadata["embedding"] = self.embedding.tolist() if self.embedding is not None else None
100
+
101
+ if metadata_only:
102
+ metadata.pop("embedding")
103
+ metadata.pop("id")
104
+ else:
105
+ metadata["embedding"] = (
106
+ self.embedding.tolist() if self.embedding is not None else None
107
+ )
108
+
100
109
  return metadata
101
110
 
102
111
  def __repr__(self):
@@ -105,11 +114,9 @@ class Document(Model):
105
114
  metadata_str = ", ".join(f"{k}={v!r}" for k, v in self.to_dict().items())
106
115
  return f"Document({metadata_str})"
107
116
 
108
- def model_dump_json(self) -> str:
109
- d = self.to_dict()
110
- d.pop("embedding")
111
- d.pop("id")
112
- return json.dumps(d)
117
+ def model_dump_json(self, metadata_only=False) -> str:
118
+ metadata = self.to_dict(m)
119
+ return json.dumps(metadata)
113
120
 
114
121
 
115
122
  class CollectionManager[D: Document]:
@@ -144,12 +151,12 @@ class CollectionManager[D: Document]:
144
151
  cursor = self._db.connection.cursor()
145
152
  cursor.execute(
146
153
  "SELECT COUNT(*) FROM _beaver_ann_pending_log WHERE collection_name = ?",
147
- (self._name,)
154
+ (self._name,),
148
155
  )
149
156
  pending_count = cursor.fetchone()[0]
150
157
  cursor.execute(
151
158
  "SELECT COUNT(*) FROM _beaver_ann_deletions_log WHERE collection_name = ?",
152
- (self._name,)
159
+ (self._name,),
153
160
  )
154
161
  deletion_count = cursor.fetchone()[0]
155
162
  return (pending_count + deletion_count) >= threshold
@@ -181,7 +188,7 @@ class CollectionManager[D: Document]:
181
188
  # If we get the lock, start a new background thread.
182
189
  self._compaction_thread = threading.Thread(
183
190
  target=self._run_compaction_and_release_lock,
184
- daemon=True # Daemon threads don't block program exit.
191
+ daemon=True, # Daemon threads don't block program exit.
185
192
  )
186
193
  self._compaction_thread.start()
187
194
  if block:
@@ -192,13 +199,7 @@ class CollectionManager[D: Document]:
192
199
  raise
193
200
  # If acquire fails, it means another thread holds the lock, so we do nothing.
194
201
 
195
- def index(
196
- self,
197
- document: D,
198
- *,
199
- fts: bool | list[str] = True,
200
- fuzzy: bool = False
201
- ):
202
+ def index(self, document: D, *, fts: bool | list[str] = True, fuzzy: bool = False):
202
203
  """
203
204
  Indexes a Document, including vector, FTS, and fuzzy search data.
204
205
  The entire operation is performed in a single atomic transaction.
@@ -215,7 +216,11 @@ class CollectionManager[D: Document]:
215
216
  (
216
217
  self._name,
217
218
  document.id,
218
- document.embedding.tobytes() if document.embedding is not None else None,
219
+ (
220
+ document.embedding.tobytes()
221
+ if document.embedding is not None
222
+ else None
223
+ ),
219
224
  json.dumps(document.to_dict()),
220
225
  ),
221
226
  )
@@ -225,26 +230,49 @@ class CollectionManager[D: Document]:
225
230
  self._vector_index.index(document.id, document.embedding, cursor)
226
231
 
227
232
  # Step 3: FTS and Fuzzy Indexing
228
- cursor.execute("DELETE FROM beaver_fts_index WHERE collection = ? AND item_id = ?", (self._name, document.id))
229
- cursor.execute("DELETE FROM beaver_trigrams WHERE collection = ? AND item_id = ?", (self._name, document.id))
233
+ cursor.execute(
234
+ "DELETE FROM beaver_fts_index WHERE collection = ? AND item_id = ?",
235
+ (self._name, document.id),
236
+ )
237
+ cursor.execute(
238
+ "DELETE FROM beaver_trigrams WHERE collection = ? AND item_id = ?",
239
+ (self._name, document.id),
240
+ )
230
241
 
231
242
  flat_metadata = self._flatten_metadata(document.to_dict())
232
243
  fields_to_index: dict[str, str] = {}
233
244
  if isinstance(fts, list):
234
- fields_to_index = {k: v for k, v in flat_metadata.items() if k in fts and isinstance(v, str)}
245
+ fields_to_index = {
246
+ k: v
247
+ for k, v in flat_metadata.items()
248
+ if k in fts and isinstance(v, str)
249
+ }
235
250
  elif fts:
236
- fields_to_index = {k: v for k, v in flat_metadata.items() if isinstance(v, str)}
251
+ fields_to_index = {
252
+ k: v for k, v in flat_metadata.items() if isinstance(v, str)
253
+ }
237
254
 
238
255
  if fields_to_index:
239
- fts_data = [(self._name, document.id, path, content) for path, content in fields_to_index.items()]
240
- cursor.executemany("INSERT INTO beaver_fts_index (collection, item_id, field_path, field_content) VALUES (?, ?, ?, ?)", fts_data)
256
+ fts_data = [
257
+ (self._name, document.id, path, content)
258
+ for path, content in fields_to_index.items()
259
+ ]
260
+ cursor.executemany(
261
+ "INSERT INTO beaver_fts_index (collection, item_id, field_path, field_content) VALUES (?, ?, ?, ?)",
262
+ fts_data,
263
+ )
241
264
  if fuzzy:
242
265
  trigram_data = []
243
266
  for path, content in fields_to_index.items():
244
267
  for trigram in _get_trigrams(content.lower()):
245
- trigram_data.append((self._name, document.id, path, trigram))
268
+ trigram_data.append(
269
+ (self._name, document.id, path, trigram)
270
+ )
246
271
  if trigram_data:
247
- cursor.executemany("INSERT INTO beaver_trigrams (collection, item_id, field_path, trigram) VALUES (?, ?, ?, ?)", trigram_data)
272
+ cursor.executemany(
273
+ "INSERT INTO beaver_trigrams (collection, item_id, field_path, trigram) VALUES (?, ?, ?, ?)",
274
+ trigram_data,
275
+ )
248
276
 
249
277
  # Step 4: Update Collection Version to signal a change.
250
278
  cursor.execute(
@@ -262,10 +290,22 @@ class CollectionManager[D: Document]:
262
290
  raise TypeError("Item to drop must be a Document object.")
263
291
  with self._db.connection:
264
292
  cursor = self._db.connection.cursor()
265
- cursor.execute("DELETE FROM beaver_collections WHERE collection = ? AND item_id = ?", (self._name, document.id))
266
- cursor.execute("DELETE FROM beaver_fts_index WHERE collection = ? AND item_id = ?", (self._name, document.id))
267
- cursor.execute("DELETE FROM beaver_trigrams WHERE collection = ? AND item_id = ?", (self._name, document.id))
268
- cursor.execute("DELETE FROM beaver_edges WHERE collection = ? AND (source_item_id = ? OR target_item_id = ?)", (self._name, document.id, document.id))
293
+ cursor.execute(
294
+ "DELETE FROM beaver_collections WHERE collection = ? AND item_id = ?",
295
+ (self._name, document.id),
296
+ )
297
+ cursor.execute(
298
+ "DELETE FROM beaver_fts_index WHERE collection = ? AND item_id = ?",
299
+ (self._name, document.id),
300
+ )
301
+ cursor.execute(
302
+ "DELETE FROM beaver_trigrams WHERE collection = ? AND item_id = ?",
303
+ (self._name, document.id),
304
+ )
305
+ cursor.execute(
306
+ "DELETE FROM beaver_edges WHERE collection = ? AND (source_item_id = ? OR target_item_id = ?)",
307
+ (self._name, document.id, document.id),
308
+ )
269
309
  self._vector_index.drop(document.id, cursor)
270
310
  cursor.execute(
271
311
  "INSERT INTO beaver_collection_versions (collection_name, version) VALUES (?, 1) ON CONFLICT(collection_name) DO UPDATE SET version = version + 1",
@@ -294,9 +334,7 @@ class CollectionManager[D: Document]:
294
334
  )
295
335
  cursor.close()
296
336
 
297
- def search(
298
- self, vector: list[float], top_k: int = 10
299
- ) -> List[Tuple[D, float]]:
337
+ def search(self, vector: list[float], top_k: int = 10) -> List[Tuple[D, float]]:
300
338
  """Performs a fast, persistent approximate nearest neighbor search."""
301
339
  if not isinstance(vector, list):
302
340
  raise TypeError("Search vector must be a list of floats.")
@@ -319,7 +357,11 @@ class CollectionManager[D: Document]:
319
357
  doc_map = {
320
358
  row["item_id"]: self._model(
321
359
  id=row["item_id"],
322
- embedding=(np.frombuffer(row["item_vector"], dtype=np.float32).tolist() if row["item_vector"] else None),
360
+ embedding=(
361
+ np.frombuffer(row["item_vector"], dtype=np.float32).tolist()
362
+ if row["item_vector"]
363
+ else None
364
+ ),
323
365
  **json.loads(row["metadata"]),
324
366
  )
325
367
  for row in rows
@@ -340,7 +382,7 @@ class CollectionManager[D: Document]:
340
382
  *,
341
383
  on: str | list[str] | None = None,
342
384
  top_k: int = 10,
343
- fuzziness: int = 0
385
+ fuzziness: int = 0,
344
386
  ) -> list[tuple[D, float]]:
345
387
  """
346
388
  Performs a full-text or fuzzy search on indexed string fields.
@@ -374,14 +416,19 @@ class CollectionManager[D: Document]:
374
416
  params.extend(on)
375
417
 
376
418
  params.extend([top_k, self._name])
377
- rows = cursor.execute(sql_query.format(field_filter_sql), tuple(params)).fetchall()
419
+ rows = cursor.execute(
420
+ sql_query.format(field_filter_sql), tuple(params)
421
+ ).fetchall()
378
422
  results = []
379
423
  for row in rows:
380
424
  embedding = (
381
425
  np.frombuffer(row["item_vector"], dtype=np.float32).tolist()
382
- if row["item_vector"] else None
426
+ if row["item_vector"]
427
+ else None
428
+ )
429
+ doc = self._model(
430
+ id=row["item_id"], embedding=embedding, **json.loads(row["metadata"])
383
431
  )
384
- doc = self._model(id=row["item_id"], embedding=embedding, **json.loads(row["metadata"]))
385
432
  results.append((doc, row["rank"]))
386
433
  return results
387
434
 
@@ -415,8 +462,10 @@ class CollectionManager[D: Document]:
415
462
  params.extend(on)
416
463
 
417
464
  params.append(similarity_threshold)
418
- cursor.execute(sql.format(trigram_placeholders, field_filter_sql), tuple(params))
419
- return {row['item_id'] for row in cursor.fetchall()}
465
+ cursor.execute(
466
+ sql.format(trigram_placeholders, field_filter_sql), tuple(params)
467
+ )
468
+ return {row["item_id"] for row in cursor.fetchall()}
420
469
 
421
470
  def _perform_fuzzy_search(
422
471
  self, query: str, on: list[str] | None, top_k: int, fuzziness: int
@@ -441,10 +490,10 @@ class CollectionManager[D: Document]:
441
490
  cursor.execute(sql_text, tuple(params_text))
442
491
  candidate_texts: dict[str, dict[str, str]] = {}
443
492
  for row in cursor.fetchall():
444
- item_id = row['item_id']
493
+ item_id = row["item_id"]
445
494
  if item_id not in candidate_texts:
446
495
  candidate_texts[item_id] = {}
447
- candidate_texts[item_id][row['field_path']] = row['field_content']
496
+ candidate_texts[item_id][row["field_path"]] = row["field_content"]
448
497
 
449
498
  scored_candidates = []
450
499
  fts_rank_map = {doc.id: rank for doc, rank in fts_results}
@@ -452,17 +501,19 @@ class CollectionManager[D: Document]:
452
501
  for item_id in candidate_ids:
453
502
  if item_id not in candidate_texts:
454
503
  continue
455
- min_dist = float('inf')
504
+ min_dist = float("inf")
456
505
  for content in candidate_texts[item_id].values():
457
506
  dist = _sliding_window_levenshtein(query, content, fuzziness)
458
507
  if dist < min_dist:
459
508
  min_dist = dist
460
509
  if min_dist <= fuzziness:
461
- scored_candidates.append({
462
- "id": item_id,
463
- "distance": min_dist,
464
- "fts_rank": fts_rank_map.get(item_id, 0)
465
- })
510
+ scored_candidates.append(
511
+ {
512
+ "id": item_id,
513
+ "distance": min_dist,
514
+ "fts_rank": fts_rank_map.get(item_id, 0),
515
+ }
516
+ )
466
517
 
467
518
  scored_candidates.sort(key=lambda x: (x["distance"], x["fts_rank"]))
468
519
  top_ids = [c["id"] for c in scored_candidates[:top_k]]
@@ -472,7 +523,18 @@ class CollectionManager[D: Document]:
472
523
  id_placeholders = ",".join("?" for _ in top_ids)
473
524
  sql_docs = f"SELECT item_id, item_vector, metadata FROM beaver_collections WHERE collection = ? AND item_id IN ({id_placeholders})"
474
525
  cursor.execute(sql_docs, (self._name, *top_ids))
475
- doc_map = {row["item_id"]: self._model(id=row["item_id"], embedding=(np.frombuffer(row["item_vector"], dtype=np.float32).tolist() if row["item_vector"] else None), **json.loads(row["metadata"])) for row in cursor.fetchall()}
526
+ doc_map = {
527
+ row["item_id"]: self._model(
528
+ id=row["item_id"],
529
+ embedding=(
530
+ np.frombuffer(row["item_vector"], dtype=np.float32).tolist()
531
+ if row["item_vector"]
532
+ else None
533
+ ),
534
+ **json.loads(row["metadata"]),
535
+ )
536
+ for row in cursor.fetchall()
537
+ }
476
538
 
477
539
  final_results = []
478
540
  distance_map = {c["id"]: c["distance"] for c in scored_candidates}
@@ -583,9 +645,7 @@ class CollectionManager[D: Document]:
583
645
 
584
646
 
585
647
  def rerank[D: Document](
586
- *results: list[D],
587
- weights: list[float] | None = None,
588
- k: int = 60
648
+ *results: list[D], weights: list[float] | None = None, k: int = 60
589
649
  ) -> list[D]:
590
650
  """
591
651
  Reranks documents from multiple search result lists using Reverse Rank Fusion (RRF).
@@ -610,5 +670,7 @@ def rerank[D: Document](
610
670
  score = weight * (1 / (k + rank))
611
671
  rrf_scores[doc_id] = rrf_scores.get(doc_id, 0.0) + score
612
672
 
613
- sorted_doc_ids = sorted(rrf_scores.keys(), key=lambda k: rrf_scores[k], reverse=True)
673
+ sorted_doc_ids = sorted(
674
+ rrf_scores.keys(), key=lambda k: rrf_scores[k], reverse=True
675
+ )
614
676
  return [doc_store[doc_id] for doc_id in sorted_doc_ids]
beaver/server.py CHANGED
@@ -270,7 +270,7 @@ def build(db: BeaverDB) -> FastAPI:
270
270
  def get_all_documents(name: str) -> List[dict]:
271
271
  """Retrieves all documents in the collection."""
272
272
  collection = db.collection(name)
273
- return [doc.to_dict() for doc in collection]
273
+ return [doc.to_dict(metadata_only=False) for doc in collection]
274
274
 
275
275
  @app.post("/collections/{name}/index", tags=["Collections"])
276
276
  def index_document(name: str, req: IndexRequest):
@@ -291,7 +291,7 @@ def build(db: BeaverDB) -> FastAPI:
291
291
  collection = db.collection(name)
292
292
  try:
293
293
  results = collection.search(vector=req.vector, top_k=req.top_k)
294
- return [{"document": doc.to_dict(), "distance": dist} for doc, dist in results]
294
+ return [{"document": doc.to_dict(metadata_only=False), "distance": dist} for doc, dist in results]
295
295
  except TypeError as e:
296
296
  if "vector" in str(e):
297
297
  raise HTTPException(status_code=501, detail="Vector search requires the '[vector]' extra. Install with: pip install \"beaver-db[vector]\"")
@@ -302,7 +302,7 @@ def build(db: BeaverDB) -> FastAPI:
302
302
  """Performs a full-text or fuzzy search on the collection."""
303
303
  collection = db.collection(name)
304
304
  results = collection.match(query=req.query, on=req.on, top_k=req.top_k, fuzziness=req.fuzziness)
305
- return [{"document": doc.to_dict(), "score": score} for doc, score in results]
305
+ return [{"document": doc.to_dict(metadata_only=False), "score": score} for doc, score in results]
306
306
 
307
307
  @app.post("/collections/{name}/connect", tags=["Collections"])
308
308
  def connect_documents(name: str, req: ConnectRequest):
@@ -319,7 +319,7 @@ def build(db: BeaverDB) -> FastAPI:
319
319
  collection = db.collection(name)
320
320
  doc = Document(id=doc_id)
321
321
  neighbors = collection.neighbors(doc, label=label)
322
- return [n.to_dict() for n in neighbors]
322
+ return [n.to_dict(metadata_only=False) for n in neighbors]
323
323
 
324
324
  @app.post("/collections/{name}/{doc_id}/walk", tags=["Collections"])
325
325
  def walk_graph(name: str, doc_id: str, req: WalkRequest) -> List[dict]:
@@ -327,7 +327,7 @@ def build(db: BeaverDB) -> FastAPI:
327
327
  collection = db.collection(name)
328
328
  source_doc = Document(id=doc_id)
329
329
  results = collection.walk(source=source_doc, labels=req.labels, depth=req.depth, direction=req.direction)
330
- return [doc.to_dict() for doc in results]
330
+ return [doc.to_dict(metadata_only=False) for doc in results]
331
331
 
332
332
  return app
333
333
 
beaver/vectors.py CHANGED
@@ -11,7 +11,7 @@ try:
11
11
 
12
12
  HAVE_FAISS = True
13
13
  except ImportError:
14
- raise TypeError("This feature requires to install beaver-db[faiss]")
14
+ raise ImportError("This feature requires to install beaver-db[vector]")
15
15
 
16
16
 
17
17
  class VectorIndex:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: beaver-db
3
- Version: 0.18.3
3
+ Version: 0.18.5
4
4
  Summary: Fast, embedded, and multi-modal DB based on SQLite for AI-powered applications.
5
5
  License-File: LICENSE
6
6
  Classifier: License :: OSI Approved :: MIT License
@@ -1,18 +1,18 @@
1
- beaver/__init__.py,sha256=89oJ_luJjiKUvw5D0h70YlMbcly60gzp-1gHGMrbYcY,125
1
+ beaver/__init__.py,sha256=8lfevL_d014rDr943JJ97-KLuCCMYQfWkX6P1Hbj-aA,125
2
2
  beaver/blobs.py,sha256=YkIEskHD6oHRaJTF0P25HrTT8LqM-REyV_UBPVQxeqQ,4055
3
3
  beaver/channels.py,sha256=kIuwKMDBdDQObaKT23znsMXzfpKfE7pXSxvf-u4LlpY,9554
4
4
  beaver/cli.py,sha256=Sxm-mYU3LGd4tIqw-5LHb0ektWebjV9vn51hm-CMJD0,2232
5
- beaver/collections.py,sha256=CBJSBmJ0LuVI068iRJ2HCXdivxjw51FJOu2BC_54Lfw,24825
5
+ beaver/collections.py,sha256=Q4pSM3Laci8jAjHCeKRJyv9PTr7LkFf0EOyC4e2N3p4,26105
6
6
  beaver/core.py,sha256=d37hnD1xaYdZFKf1z0sDAoGwYwuPU8ETrOotso7aHfk,15209
7
7
  beaver/dicts.py,sha256=Xp8lPfQt08O8zCbptQLWQLO79OxG6uAVER6ryj3SScQ,5495
8
8
  beaver/lists.py,sha256=rfJ8uTNLkMREYc0uGx0z1VKt2m3eR9hvbdvDD58EbmQ,10140
9
9
  beaver/logs.py,sha256=a5xenwl5NZeegIU0dWVEs67lvaHzzw-JRAZtEzNNO3E,9529
10
10
  beaver/queues.py,sha256=Fr3oie63EtceSoiC8EOEDSLu1tDI8q2MYLXd8MEeC3g,6476
11
- beaver/server.py,sha256=ZYbzcar_xQP3vCgvmrRobIx0L8dfcHtvxRAQNcniJwo,13547
11
+ beaver/server.py,sha256=WoNcPXU9oh6hcHtb60IbEk5DfZT5J4Fb-yubJE3YLIc,13642
12
12
  beaver/types.py,sha256=WZLINf7hy6zdKdAFQK0EVMSl5vnY_KnrHXNdXgAKuPg,1582
13
- beaver/vectors.py,sha256=qvI6RwUOGrhVH5d6PUmI3jKDaoDotMy0iy-bHyvmXks,18496
14
- beaver_db-0.18.3.dist-info/METADATA,sha256=5p2CxB2TU1daEjBIXmhbZVwU_arYPRIU16x3azWHe9Q,21232
15
- beaver_db-0.18.3.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
16
- beaver_db-0.18.3.dist-info/entry_points.txt,sha256=bd5E2s45PoBdtdR9-ToKSdLNhmHp8naV1lWP5mOzlrc,42
17
- beaver_db-0.18.3.dist-info/licenses/LICENSE,sha256=1xrIY5JnMk_QDQzsqmVzPIIyCgZAkWCC8kF2Ddo1UT0,1071
18
- beaver_db-0.18.3.dist-info/RECORD,,
13
+ beaver/vectors.py,sha256=EGZf1s364-rMubxkYoTcjBl72lRRxM1cUwypjsoC6ec,18499
14
+ beaver_db-0.18.5.dist-info/METADATA,sha256=ulxwR_QHfaebRgV-ZmTl8TFVAHgcwlx9AxUc4_7snaI,21232
15
+ beaver_db-0.18.5.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
16
+ beaver_db-0.18.5.dist-info/entry_points.txt,sha256=bd5E2s45PoBdtdR9-ToKSdLNhmHp8naV1lWP5mOzlrc,42
17
+ beaver_db-0.18.5.dist-info/licenses/LICENSE,sha256=1xrIY5JnMk_QDQzsqmVzPIIyCgZAkWCC8kF2Ddo1UT0,1071
18
+ beaver_db-0.18.5.dist-info/RECORD,,