beaver-db 0.18.3__py3-none-any.whl → 0.18.4__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 +1 -1
- beaver/collections.py +118 -56
- beaver/server.py +5 -5
- {beaver_db-0.18.3.dist-info → beaver_db-0.18.4.dist-info}/METADATA +1 -1
- {beaver_db-0.18.3.dist-info → beaver_db-0.18.4.dist-info}/RECORD +8 -8
- {beaver_db-0.18.3.dist-info → beaver_db-0.18.4.dist-info}/WHEEL +0 -0
- {beaver_db-0.18.3.dist-info → beaver_db-0.18.4.dist-info}/entry_points.txt +0 -0
- {beaver_db-0.18.3.dist-info → beaver_db-0.18.4.dist-info}/licenses/LICENSE +0 -0
beaver/__init__.py
CHANGED
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(
|
|
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
|
-
|
|
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
|
-
|
|
110
|
-
|
|
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
|
-
|
|
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(
|
|
229
|
-
|
|
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 = {
|
|
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 = {
|
|
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 = [
|
|
240
|
-
|
|
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(
|
|
268
|
+
trigram_data.append(
|
|
269
|
+
(self._name, document.id, path, trigram)
|
|
270
|
+
)
|
|
246
271
|
if trigram_data:
|
|
247
|
-
cursor.executemany(
|
|
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(
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
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=(
|
|
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(
|
|
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"]
|
|
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(
|
|
419
|
-
|
|
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[
|
|
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[
|
|
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(
|
|
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
|
-
|
|
463
|
-
|
|
464
|
-
|
|
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 = {
|
|
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(
|
|
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
|
|
|
@@ -1,18 +1,18 @@
|
|
|
1
|
-
beaver/__init__.py,sha256=
|
|
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=
|
|
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=
|
|
11
|
+
beaver/server.py,sha256=WoNcPXU9oh6hcHtb60IbEk5DfZT5J4Fb-yubJE3YLIc,13642
|
|
12
12
|
beaver/types.py,sha256=WZLINf7hy6zdKdAFQK0EVMSl5vnY_KnrHXNdXgAKuPg,1582
|
|
13
13
|
beaver/vectors.py,sha256=qvI6RwUOGrhVH5d6PUmI3jKDaoDotMy0iy-bHyvmXks,18496
|
|
14
|
-
beaver_db-0.18.
|
|
15
|
-
beaver_db-0.18.
|
|
16
|
-
beaver_db-0.18.
|
|
17
|
-
beaver_db-0.18.
|
|
18
|
-
beaver_db-0.18.
|
|
14
|
+
beaver_db-0.18.4.dist-info/METADATA,sha256=rJzAJiR7iaz8jCqlwUKIInamZEvLK_QKVNrzwvqkWGY,21232
|
|
15
|
+
beaver_db-0.18.4.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
16
|
+
beaver_db-0.18.4.dist-info/entry_points.txt,sha256=bd5E2s45PoBdtdR9-ToKSdLNhmHp8naV1lWP5mOzlrc,42
|
|
17
|
+
beaver_db-0.18.4.dist-info/licenses/LICENSE,sha256=1xrIY5JnMk_QDQzsqmVzPIIyCgZAkWCC8kF2Ddo1UT0,1071
|
|
18
|
+
beaver_db-0.18.4.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|