citadeldb-haystack 2.0.0__tar.gz → 2.2.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {citadeldb_haystack-2.0.0 → citadeldb_haystack-2.2.0}/.gitignore +1 -0
- citadeldb_haystack-2.2.0/PKG-INFO +132 -0
- citadeldb_haystack-2.2.0/README.md +110 -0
- {citadeldb_haystack-2.0.0 → citadeldb_haystack-2.2.0}/pyproject.toml +2 -2
- {citadeldb_haystack-2.0.0 → citadeldb_haystack-2.2.0}/src/citadeldb_haystack/document_store.py +217 -48
- citadeldb_haystack-2.2.0/tests/_embedder.py +22 -0
- {citadeldb_haystack-2.0.0 → citadeldb_haystack-2.2.0}/tests/test_conformance.py +9 -3
- citadeldb_haystack-2.2.0/tests/test_document_store.py +858 -0
- citadeldb_haystack-2.0.0/PKG-INFO +0 -109
- citadeldb_haystack-2.0.0/README.md +0 -87
- citadeldb_haystack-2.0.0/tests/test_document_store.py +0 -528
- {citadeldb_haystack-2.0.0 → citadeldb_haystack-2.2.0}/src/citadeldb_haystack/__init__.py +0 -0
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: citadeldb-haystack
|
|
3
|
+
Version: 2.2.0
|
|
4
|
+
Summary: Haystack document store backed by Citadel: encrypted at rest, with deletes that destroy the key
|
|
5
|
+
Project-URL: Homepage, https://citadeldb.dev
|
|
6
|
+
Project-URL: Repository, https://github.com/yp3y5akh0v/citadel
|
|
7
|
+
Author: Yuriy Peysakhov
|
|
8
|
+
License-Expression: Apache-2.0
|
|
9
|
+
Keywords: document-store,encryption,haystack,rag,vector-store
|
|
10
|
+
Classifier: Development Status :: 4 - Beta
|
|
11
|
+
Classifier: Intended Audience :: Developers
|
|
12
|
+
Classifier: Programming Language :: Python :: 3
|
|
13
|
+
Classifier: Topic :: Database
|
|
14
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
15
|
+
Requires-Python: >=3.10
|
|
16
|
+
Requires-Dist: citadeldb<3,>=2.2
|
|
17
|
+
Requires-Dist: haystack-ai<4,>=2.9
|
|
18
|
+
Provides-Extra: test
|
|
19
|
+
Requires-Dist: pytest-asyncio>=0.23; extra == 'test'
|
|
20
|
+
Requires-Dist: pytest>=8; extra == 'test'
|
|
21
|
+
Description-Content-Type: text/markdown
|
|
22
|
+
|
|
23
|
+
# citadeldb-haystack
|
|
24
|
+
|
|
25
|
+
A [Haystack](https://github.com/deepset-ai/haystack) `DocumentStore` backed by
|
|
26
|
+
[Citadel](https://citadeldb.dev). Encrypted at rest, embedded in your process, and deletes
|
|
27
|
+
that destroy the key, not just the row.
|
|
28
|
+
|
|
29
|
+
```
|
|
30
|
+
pip install citadeldb-haystack sentence-transformers
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
Requires `citadeldb>=2.2,<3` and `haystack-ai>=2.9,<4`. Set `CITADEL_KEY` before
|
|
34
|
+
running the example. The embedding model downloads on first use and runs locally.
|
|
35
|
+
|
|
36
|
+
```python
|
|
37
|
+
from haystack import Document
|
|
38
|
+
from haystack.components.embedders import SentenceTransformersTextEmbedder
|
|
39
|
+
from haystack.utils import Secret
|
|
40
|
+
from citadeldb_haystack import CitadelDocumentStore
|
|
41
|
+
|
|
42
|
+
embedder = SentenceTransformersTextEmbedder(model="sentence-transformers/all-mpnet-base-v2")
|
|
43
|
+
store = CitadelDocumentStore(
|
|
44
|
+
"corpus.cdl",
|
|
45
|
+
Secret.from_env_var("CITADEL_KEY"),
|
|
46
|
+
embedder=embedder,
|
|
47
|
+
dim=768,
|
|
48
|
+
embedding_similarity_function="cosine",
|
|
49
|
+
)
|
|
50
|
+
store.write_documents([
|
|
51
|
+
Document(id="d1", content="The deployment failed because the disk was full.",
|
|
52
|
+
meta={"chapter": "intro"}),
|
|
53
|
+
])
|
|
54
|
+
store.filter_documents({"field": "meta.chapter", "operator": "==", "value": "intro"})
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
`dim` defaults to 768 and must match your embedding model.
|
|
58
|
+
`embedding_similarity_function` is `"cosine"` or `"dot_product"` and is persisted
|
|
59
|
+
with the store. It defaults to Haystack's `"dot_product"`; choose `"cosine"`
|
|
60
|
+
explicitly when that is what produced the supplied vectors.
|
|
61
|
+
Embedders exposing `model_id`, `model`, or `model_name` record that identity automatically,
|
|
62
|
+
in that order. For a custom component without any of those attributes, pass a stable
|
|
63
|
+
`model_id=` explicitly; Citadel refuses to guess from the Python class name.
|
|
64
|
+
|
|
65
|
+
## Pipeline serialization
|
|
66
|
+
|
|
67
|
+
Use a Haystack environment-variable `Secret` for pipeline serialization. Literal
|
|
68
|
+
passphrases cannot be serialized:
|
|
69
|
+
|
|
70
|
+
```python
|
|
71
|
+
CitadelDocumentStore(
|
|
72
|
+
"literal.cdl", "literal-passphrase", embedder=embedder, dim=768
|
|
73
|
+
).to_dict()
|
|
74
|
+
# ValueError: Cannot serialize token-based secret.
|
|
75
|
+
|
|
76
|
+
CitadelDocumentStore(
|
|
77
|
+
"corpus.cdl", Secret.from_env_var("CITADEL_KEY"), embedder=embedder, dim=768,
|
|
78
|
+
embedding_similarity_function="cosine",
|
|
79
|
+
).to_dict()
|
|
80
|
+
# {... "key": {"type": "env_var", "env_vars": ["CITADEL_KEY"], ...}}
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
Use `Secret.from_env_var` for any store that goes into a saved pipeline.
|
|
84
|
+
|
|
85
|
+
## Deletes destroy the key
|
|
86
|
+
|
|
87
|
+
Every document is sealed under its own key. Deleting destroys that key and removes the
|
|
88
|
+
row. Pre-erasure backups or snapshots containing keys, and exported plaintext, are outside
|
|
89
|
+
that erasure.
|
|
90
|
+
|
|
91
|
+
```python
|
|
92
|
+
store.delete_documents(["d1"])
|
|
93
|
+
store.delete_all() # returns the number erased
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
`DuplicatePolicy.NONE` is treated as `FAIL`: duplicate ids are rejected.
|
|
97
|
+
|
|
98
|
+
## Retrieval
|
|
99
|
+
|
|
100
|
+
```python
|
|
101
|
+
embedder.warm_up()
|
|
102
|
+
query_embedding = embedder.run(text="Why did the release break?")["embedding"]
|
|
103
|
+
|
|
104
|
+
store.embedding_retrieval(
|
|
105
|
+
query_embedding,
|
|
106
|
+
top_k=5,
|
|
107
|
+
filters={"field": "meta.chapter", "operator": "==", "value": "intro"},
|
|
108
|
+
scale_score=False,
|
|
109
|
+
return_embedding=False,
|
|
110
|
+
)
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
Filters use Haystack's evaluator.
|
|
114
|
+
Top-level `AND` string equalities, including nested paths such as `meta.person.name`,
|
|
115
|
+
are passed to Citadel as payload filters. Other predicates filter ranked candidates; the
|
|
116
|
+
search window expands until `top_k` matches survive or the region is exhausted.
|
|
117
|
+
|
|
118
|
+
## Notes
|
|
119
|
+
|
|
120
|
+
The store requires a Haystack text embedder. Documents that arrive without a vector are
|
|
121
|
+
embedded with it, while vectors already supplied by the pipeline are stored as-is. Pass the
|
|
122
|
+
same model to the pipeline and store so both paths remain in one vector space. The store warms
|
|
123
|
+
the embedder lazily before its first model call and includes its configuration in pipeline
|
|
124
|
+
serialization.
|
|
125
|
+
|
|
126
|
+
Citadel is embedded and one process owns the file. A path already open on this thread,
|
|
127
|
+
under the same passphrase, is shared, so this can sit on the same database as another
|
|
128
|
+
Citadel adapter; construct them on the same thread.
|
|
129
|
+
|
|
130
|
+
## License
|
|
131
|
+
|
|
132
|
+
Apache-2.0
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
# citadeldb-haystack
|
|
2
|
+
|
|
3
|
+
A [Haystack](https://github.com/deepset-ai/haystack) `DocumentStore` backed by
|
|
4
|
+
[Citadel](https://citadeldb.dev). Encrypted at rest, embedded in your process, and deletes
|
|
5
|
+
that destroy the key, not just the row.
|
|
6
|
+
|
|
7
|
+
```
|
|
8
|
+
pip install citadeldb-haystack sentence-transformers
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
Requires `citadeldb>=2.2,<3` and `haystack-ai>=2.9,<4`. Set `CITADEL_KEY` before
|
|
12
|
+
running the example. The embedding model downloads on first use and runs locally.
|
|
13
|
+
|
|
14
|
+
```python
|
|
15
|
+
from haystack import Document
|
|
16
|
+
from haystack.components.embedders import SentenceTransformersTextEmbedder
|
|
17
|
+
from haystack.utils import Secret
|
|
18
|
+
from citadeldb_haystack import CitadelDocumentStore
|
|
19
|
+
|
|
20
|
+
embedder = SentenceTransformersTextEmbedder(model="sentence-transformers/all-mpnet-base-v2")
|
|
21
|
+
store = CitadelDocumentStore(
|
|
22
|
+
"corpus.cdl",
|
|
23
|
+
Secret.from_env_var("CITADEL_KEY"),
|
|
24
|
+
embedder=embedder,
|
|
25
|
+
dim=768,
|
|
26
|
+
embedding_similarity_function="cosine",
|
|
27
|
+
)
|
|
28
|
+
store.write_documents([
|
|
29
|
+
Document(id="d1", content="The deployment failed because the disk was full.",
|
|
30
|
+
meta={"chapter": "intro"}),
|
|
31
|
+
])
|
|
32
|
+
store.filter_documents({"field": "meta.chapter", "operator": "==", "value": "intro"})
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
`dim` defaults to 768 and must match your embedding model.
|
|
36
|
+
`embedding_similarity_function` is `"cosine"` or `"dot_product"` and is persisted
|
|
37
|
+
with the store. It defaults to Haystack's `"dot_product"`; choose `"cosine"`
|
|
38
|
+
explicitly when that is what produced the supplied vectors.
|
|
39
|
+
Embedders exposing `model_id`, `model`, or `model_name` record that identity automatically,
|
|
40
|
+
in that order. For a custom component without any of those attributes, pass a stable
|
|
41
|
+
`model_id=` explicitly; Citadel refuses to guess from the Python class name.
|
|
42
|
+
|
|
43
|
+
## Pipeline serialization
|
|
44
|
+
|
|
45
|
+
Use a Haystack environment-variable `Secret` for pipeline serialization. Literal
|
|
46
|
+
passphrases cannot be serialized:
|
|
47
|
+
|
|
48
|
+
```python
|
|
49
|
+
CitadelDocumentStore(
|
|
50
|
+
"literal.cdl", "literal-passphrase", embedder=embedder, dim=768
|
|
51
|
+
).to_dict()
|
|
52
|
+
# ValueError: Cannot serialize token-based secret.
|
|
53
|
+
|
|
54
|
+
CitadelDocumentStore(
|
|
55
|
+
"corpus.cdl", Secret.from_env_var("CITADEL_KEY"), embedder=embedder, dim=768,
|
|
56
|
+
embedding_similarity_function="cosine",
|
|
57
|
+
).to_dict()
|
|
58
|
+
# {... "key": {"type": "env_var", "env_vars": ["CITADEL_KEY"], ...}}
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
Use `Secret.from_env_var` for any store that goes into a saved pipeline.
|
|
62
|
+
|
|
63
|
+
## Deletes destroy the key
|
|
64
|
+
|
|
65
|
+
Every document is sealed under its own key. Deleting destroys that key and removes the
|
|
66
|
+
row. Pre-erasure backups or snapshots containing keys, and exported plaintext, are outside
|
|
67
|
+
that erasure.
|
|
68
|
+
|
|
69
|
+
```python
|
|
70
|
+
store.delete_documents(["d1"])
|
|
71
|
+
store.delete_all() # returns the number erased
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
`DuplicatePolicy.NONE` is treated as `FAIL`: duplicate ids are rejected.
|
|
75
|
+
|
|
76
|
+
## Retrieval
|
|
77
|
+
|
|
78
|
+
```python
|
|
79
|
+
embedder.warm_up()
|
|
80
|
+
query_embedding = embedder.run(text="Why did the release break?")["embedding"]
|
|
81
|
+
|
|
82
|
+
store.embedding_retrieval(
|
|
83
|
+
query_embedding,
|
|
84
|
+
top_k=5,
|
|
85
|
+
filters={"field": "meta.chapter", "operator": "==", "value": "intro"},
|
|
86
|
+
scale_score=False,
|
|
87
|
+
return_embedding=False,
|
|
88
|
+
)
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
Filters use Haystack's evaluator.
|
|
92
|
+
Top-level `AND` string equalities, including nested paths such as `meta.person.name`,
|
|
93
|
+
are passed to Citadel as payload filters. Other predicates filter ranked candidates; the
|
|
94
|
+
search window expands until `top_k` matches survive or the region is exhausted.
|
|
95
|
+
|
|
96
|
+
## Notes
|
|
97
|
+
|
|
98
|
+
The store requires a Haystack text embedder. Documents that arrive without a vector are
|
|
99
|
+
embedded with it, while vectors already supplied by the pipeline are stored as-is. Pass the
|
|
100
|
+
same model to the pipeline and store so both paths remain in one vector space. The store warms
|
|
101
|
+
the embedder lazily before its first model call and includes its configuration in pipeline
|
|
102
|
+
serialization.
|
|
103
|
+
|
|
104
|
+
Citadel is embedded and one process owns the file. A path already open on this thread,
|
|
105
|
+
under the same passphrase, is shared, so this can sit on the same database as another
|
|
106
|
+
Citadel adapter; construct them on the same thread.
|
|
107
|
+
|
|
108
|
+
## License
|
|
109
|
+
|
|
110
|
+
Apache-2.0
|
|
@@ -18,8 +18,8 @@ classifiers = [
|
|
|
18
18
|
"Topic :: Database",
|
|
19
19
|
"Topic :: Scientific/Engineering :: Artificial Intelligence",
|
|
20
20
|
]
|
|
21
|
-
#
|
|
22
|
-
dependencies = ["citadeldb>=2.
|
|
21
|
+
# Reads the expanded AtomHit scoring fields added in citadeldb 2.2.
|
|
22
|
+
dependencies = ["citadeldb>=2.2,<3", "haystack-ai>=2.9,<4"]
|
|
23
23
|
|
|
24
24
|
[project.optional-dependencies]
|
|
25
25
|
test = ["pytest>=8", "pytest-asyncio>=0.23"]
|