vector-inspector 0.2.0__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.
- vector_inspector/__init__.py +3 -0
- vector_inspector/__main__.py +4 -0
- vector_inspector/core/__init__.py +1 -0
- vector_inspector/core/connections/__init__.py +7 -0
- vector_inspector/core/connections/base_connection.py +233 -0
- vector_inspector/core/connections/chroma_connection.py +384 -0
- vector_inspector/core/connections/qdrant_connection.py +723 -0
- vector_inspector/core/connections/template_connection.py +346 -0
- vector_inspector/main.py +21 -0
- vector_inspector/services/__init__.py +1 -0
- vector_inspector/services/backup_restore_service.py +286 -0
- vector_inspector/services/filter_service.py +72 -0
- vector_inspector/services/import_export_service.py +287 -0
- vector_inspector/services/settings_service.py +60 -0
- vector_inspector/services/visualization_service.py +116 -0
- vector_inspector/ui/__init__.py +1 -0
- vector_inspector/ui/components/__init__.py +1 -0
- vector_inspector/ui/components/backup_restore_dialog.py +350 -0
- vector_inspector/ui/components/filter_builder.py +370 -0
- vector_inspector/ui/components/item_dialog.py +118 -0
- vector_inspector/ui/components/loading_dialog.py +30 -0
- vector_inspector/ui/main_window.py +288 -0
- vector_inspector/ui/views/__init__.py +1 -0
- vector_inspector/ui/views/collection_browser.py +112 -0
- vector_inspector/ui/views/connection_view.py +423 -0
- vector_inspector/ui/views/metadata_view.py +555 -0
- vector_inspector/ui/views/search_view.py +268 -0
- vector_inspector/ui/views/visualization_view.py +245 -0
- vector_inspector-0.2.0.dist-info/METADATA +382 -0
- vector_inspector-0.2.0.dist-info/RECORD +32 -0
- vector_inspector-0.2.0.dist-info/WHEEL +4 -0
- vector_inspector-0.2.0.dist-info/entry_points.txt +5 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""Core functionality for vector database operations."""
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
"""Connection managers for vector databases."""
|
|
2
|
+
|
|
3
|
+
from .base_connection import VectorDBConnection
|
|
4
|
+
from .chroma_connection import ChromaDBConnection
|
|
5
|
+
from .qdrant_connection import QdrantConnection
|
|
6
|
+
|
|
7
|
+
__all__ = ["VectorDBConnection", "ChromaDBConnection", "QdrantConnection"]
|
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
"""Abstract base class for vector database connections."""
|
|
2
|
+
|
|
3
|
+
from abc import ABC, abstractmethod
|
|
4
|
+
from typing import Optional, List, Dict, Any
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class VectorDBConnection(ABC):
|
|
8
|
+
"""Abstract base class for vector database connections.
|
|
9
|
+
|
|
10
|
+
This class defines the interface that all vector database providers
|
|
11
|
+
must implement to be compatible with Vector Inspector.
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
@abstractmethod
|
|
15
|
+
def connect(self) -> bool:
|
|
16
|
+
"""
|
|
17
|
+
Establish connection to the vector database.
|
|
18
|
+
|
|
19
|
+
Returns:
|
|
20
|
+
True if connection successful, False otherwise
|
|
21
|
+
"""
|
|
22
|
+
pass
|
|
23
|
+
|
|
24
|
+
@abstractmethod
|
|
25
|
+
def disconnect(self):
|
|
26
|
+
"""Close connection to the vector database."""
|
|
27
|
+
pass
|
|
28
|
+
|
|
29
|
+
@property
|
|
30
|
+
@abstractmethod
|
|
31
|
+
def is_connected(self) -> bool:
|
|
32
|
+
"""
|
|
33
|
+
Check if connected to the vector database.
|
|
34
|
+
|
|
35
|
+
Returns:
|
|
36
|
+
True if connected, False otherwise
|
|
37
|
+
"""
|
|
38
|
+
pass
|
|
39
|
+
|
|
40
|
+
@abstractmethod
|
|
41
|
+
def list_collections(self) -> List[str]:
|
|
42
|
+
"""
|
|
43
|
+
Get list of all collections/indexes.
|
|
44
|
+
|
|
45
|
+
Returns:
|
|
46
|
+
List of collection/index names
|
|
47
|
+
"""
|
|
48
|
+
pass
|
|
49
|
+
|
|
50
|
+
@abstractmethod
|
|
51
|
+
def get_collection_info(self, name: str) -> Optional[Dict[str, Any]]:
|
|
52
|
+
"""
|
|
53
|
+
Get collection metadata and statistics.
|
|
54
|
+
|
|
55
|
+
Args:
|
|
56
|
+
name: Collection/index name
|
|
57
|
+
|
|
58
|
+
Returns:
|
|
59
|
+
Dictionary with collection info including:
|
|
60
|
+
- name: Collection name
|
|
61
|
+
- count: Number of items
|
|
62
|
+
- metadata_fields: List of available metadata field names
|
|
63
|
+
"""
|
|
64
|
+
pass
|
|
65
|
+
|
|
66
|
+
@abstractmethod
|
|
67
|
+
def create_collection(self, name: str, vector_size: int, distance: str = "Cosine") -> bool:
|
|
68
|
+
"""Create a collection/index with a given vector size and distance metric."""
|
|
69
|
+
pass
|
|
70
|
+
|
|
71
|
+
@abstractmethod
|
|
72
|
+
def add_items(
|
|
73
|
+
self,
|
|
74
|
+
collection_name: str,
|
|
75
|
+
documents: List[str],
|
|
76
|
+
metadatas: Optional[List[Dict[str, Any]]] = None,
|
|
77
|
+
ids: Optional[List[str]] = None,
|
|
78
|
+
embeddings: Optional[List[List[float]]] = None,
|
|
79
|
+
) -> bool:
|
|
80
|
+
"""Add items to a collection."""
|
|
81
|
+
pass
|
|
82
|
+
|
|
83
|
+
@abstractmethod
|
|
84
|
+
def get_items(self, name: str, ids: List[str]) -> Dict[str, Any]:
|
|
85
|
+
"""Retrieve items by original ids. Should return a dict with 'documents' and 'metadatas'."""
|
|
86
|
+
pass
|
|
87
|
+
|
|
88
|
+
@abstractmethod
|
|
89
|
+
def delete_collection(self, name: str) -> bool:
|
|
90
|
+
"""Delete a collection/index."""
|
|
91
|
+
pass
|
|
92
|
+
|
|
93
|
+
@abstractmethod
|
|
94
|
+
def count_collection(self, name: str) -> int:
|
|
95
|
+
"""Return the number of items in the collection."""
|
|
96
|
+
pass
|
|
97
|
+
|
|
98
|
+
@abstractmethod
|
|
99
|
+
def query_collection(
|
|
100
|
+
self,
|
|
101
|
+
collection_name: str,
|
|
102
|
+
query_texts: Optional[List[str]] = None,
|
|
103
|
+
query_embeddings: Optional[List[List[float]]] = None,
|
|
104
|
+
n_results: int = 10,
|
|
105
|
+
where: Optional[Dict[str, Any]] = None,
|
|
106
|
+
where_document: Optional[Dict[str, Any]] = None,
|
|
107
|
+
) -> Optional[Dict[str, Any]]:
|
|
108
|
+
"""
|
|
109
|
+
Query a collection for similar vectors.
|
|
110
|
+
|
|
111
|
+
Args:
|
|
112
|
+
collection_name: Name of collection to query
|
|
113
|
+
query_texts: Text queries to embed and search
|
|
114
|
+
query_embeddings: Direct embedding vectors to search
|
|
115
|
+
n_results: Number of results to return
|
|
116
|
+
where: Metadata filter
|
|
117
|
+
where_document: Document content filter
|
|
118
|
+
|
|
119
|
+
Returns:
|
|
120
|
+
Query results dictionary with keys:
|
|
121
|
+
- ids: List of result IDs
|
|
122
|
+
- distances: List of distances/scores
|
|
123
|
+
- documents: List of document texts
|
|
124
|
+
- metadatas: List of metadata dicts
|
|
125
|
+
- embeddings: List of embedding vectors (optional)
|
|
126
|
+
"""
|
|
127
|
+
pass
|
|
128
|
+
|
|
129
|
+
@abstractmethod
|
|
130
|
+
def get_all_items(
|
|
131
|
+
self,
|
|
132
|
+
collection_name: str,
|
|
133
|
+
limit: Optional[int] = None,
|
|
134
|
+
offset: Optional[int] = None,
|
|
135
|
+
where: Optional[Dict[str, Any]] = None,
|
|
136
|
+
) -> Optional[Dict[str, Any]]:
|
|
137
|
+
"""
|
|
138
|
+
Get all items from a collection.
|
|
139
|
+
|
|
140
|
+
Args:
|
|
141
|
+
collection_name: Name of collection
|
|
142
|
+
limit: Maximum number of items to return
|
|
143
|
+
offset: Number of items to skip
|
|
144
|
+
where: Metadata filter
|
|
145
|
+
|
|
146
|
+
Returns:
|
|
147
|
+
Dictionary with collection items:
|
|
148
|
+
- ids: List of item IDs
|
|
149
|
+
- documents: List of document texts
|
|
150
|
+
- metadatas: List of metadata dicts
|
|
151
|
+
- embeddings: List of embedding vectors
|
|
152
|
+
"""
|
|
153
|
+
pass
|
|
154
|
+
|
|
155
|
+
@abstractmethod
|
|
156
|
+
def update_items(
|
|
157
|
+
self,
|
|
158
|
+
collection_name: str,
|
|
159
|
+
ids: List[str],
|
|
160
|
+
documents: Optional[List[str]] = None,
|
|
161
|
+
metadatas: Optional[List[Dict[str, Any]]] = None,
|
|
162
|
+
embeddings: Optional[List[List[float]]] = None,
|
|
163
|
+
) -> bool:
|
|
164
|
+
"""
|
|
165
|
+
Update items in a collection.
|
|
166
|
+
|
|
167
|
+
Args:
|
|
168
|
+
collection_name: Name of collection
|
|
169
|
+
ids: IDs of items to update
|
|
170
|
+
documents: New document texts
|
|
171
|
+
metadatas: New metadata
|
|
172
|
+
embeddings: New embeddings
|
|
173
|
+
|
|
174
|
+
Returns:
|
|
175
|
+
True if successful, False otherwise
|
|
176
|
+
"""
|
|
177
|
+
pass
|
|
178
|
+
|
|
179
|
+
@abstractmethod
|
|
180
|
+
def delete_items(
|
|
181
|
+
self,
|
|
182
|
+
collection_name: str,
|
|
183
|
+
ids: Optional[List[str]] = None,
|
|
184
|
+
where: Optional[Dict[str, Any]] = None,
|
|
185
|
+
) -> bool:
|
|
186
|
+
"""
|
|
187
|
+
Delete items from a collection.
|
|
188
|
+
|
|
189
|
+
Args:
|
|
190
|
+
collection_name: Name of collection
|
|
191
|
+
ids: IDs of items to delete
|
|
192
|
+
where: Metadata filter for items to delete
|
|
193
|
+
|
|
194
|
+
Returns:
|
|
195
|
+
True if successful, False otherwise
|
|
196
|
+
"""
|
|
197
|
+
pass
|
|
198
|
+
|
|
199
|
+
|
|
200
|
+
# Optional: Methods that may be provider-specific but useful to define
|
|
201
|
+
|
|
202
|
+
def get_connection_info(self) -> Dict[str, Any]:
|
|
203
|
+
"""
|
|
204
|
+
Get information about the current connection.
|
|
205
|
+
|
|
206
|
+
Returns:
|
|
207
|
+
Dictionary with connection details (provider-specific)
|
|
208
|
+
"""
|
|
209
|
+
return {
|
|
210
|
+
"provider": self.__class__.__name__,
|
|
211
|
+
"connected": self.is_connected
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
def get_supported_filter_operators(self) -> List[Dict[str, Any]]:
|
|
215
|
+
"""
|
|
216
|
+
Get list of filter operators supported by this provider.
|
|
217
|
+
|
|
218
|
+
Returns:
|
|
219
|
+
List of operator dictionaries with 'name' and 'server_side' keys
|
|
220
|
+
"""
|
|
221
|
+
# Default operators supported by most providers
|
|
222
|
+
return [
|
|
223
|
+
{"name": "=", "server_side": True},
|
|
224
|
+
{"name": "!=", "server_side": True},
|
|
225
|
+
{"name": ">", "server_side": True},
|
|
226
|
+
{"name": ">=", "server_side": True},
|
|
227
|
+
{"name": "<", "server_side": True},
|
|
228
|
+
{"name": "<=", "server_side": True},
|
|
229
|
+
{"name": "in", "server_side": True},
|
|
230
|
+
{"name": "not in", "server_side": True},
|
|
231
|
+
{"name": "contains", "server_side": False},
|
|
232
|
+
{"name": "not contains", "server_side": False},
|
|
233
|
+
]
|
|
@@ -0,0 +1,384 @@
|
|
|
1
|
+
"""ChromaDB connection manager."""
|
|
2
|
+
|
|
3
|
+
from typing import Optional, List, Dict, Any, cast
|
|
4
|
+
import os
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
import chromadb
|
|
7
|
+
from chromadb.api import ClientAPI
|
|
8
|
+
from chromadb.api.models.Collection import Collection
|
|
9
|
+
|
|
10
|
+
from .base_connection import VectorDBConnection
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class ChromaDBConnection(VectorDBConnection):
|
|
14
|
+
"""Manages connection to ChromaDB and provides query interface."""
|
|
15
|
+
|
|
16
|
+
def __init__(self, path: Optional[str] = None, host: Optional[str] = None, port: Optional[int] = None):
|
|
17
|
+
"""
|
|
18
|
+
Initialize ChromaDB connection.
|
|
19
|
+
|
|
20
|
+
Args:
|
|
21
|
+
path: Path for persistent client (local storage)
|
|
22
|
+
host: Host for HTTP client
|
|
23
|
+
port: Port for HTTP client
|
|
24
|
+
"""
|
|
25
|
+
self.path = path
|
|
26
|
+
self.host = host
|
|
27
|
+
self.port = port
|
|
28
|
+
self._client: Optional[ClientAPI] = None
|
|
29
|
+
self._current_collection: Optional[Collection] = None
|
|
30
|
+
|
|
31
|
+
def connect(self) -> bool:
|
|
32
|
+
"""
|
|
33
|
+
Establish connection to ChromaDB.
|
|
34
|
+
|
|
35
|
+
Returns:
|
|
36
|
+
True if connection successful, False otherwise
|
|
37
|
+
"""
|
|
38
|
+
try:
|
|
39
|
+
if self.path:
|
|
40
|
+
# Resolve relative paths to project root
|
|
41
|
+
path_to_use = self._resolve_path(self.path)
|
|
42
|
+
# Ensure directory exists
|
|
43
|
+
os.makedirs(path_to_use, exist_ok=True)
|
|
44
|
+
self._client = chromadb.PersistentClient(path=path_to_use)
|
|
45
|
+
elif self.host and self.port:
|
|
46
|
+
self._client = chromadb.HttpClient(host=self.host, port=self.port)
|
|
47
|
+
else:
|
|
48
|
+
# Default to ephemeral client for testing
|
|
49
|
+
self._client = chromadb.Client()
|
|
50
|
+
return True
|
|
51
|
+
except Exception as e:
|
|
52
|
+
print(f"Connection failed: {e}")
|
|
53
|
+
return False
|
|
54
|
+
|
|
55
|
+
def _resolve_path(self, input_path: str) -> str:
|
|
56
|
+
"""Resolve a path relative to the project root if not absolute."""
|
|
57
|
+
if os.path.isabs(input_path):
|
|
58
|
+
return input_path
|
|
59
|
+
# Find project root by searching for pyproject.toml
|
|
60
|
+
current = Path(__file__).resolve()
|
|
61
|
+
for parent in current.parents:
|
|
62
|
+
if (parent / "pyproject.toml").exists():
|
|
63
|
+
return str((parent / input_path).resolve())
|
|
64
|
+
# Fallback to CWD if project root not found
|
|
65
|
+
return str(Path(input_path).resolve())
|
|
66
|
+
|
|
67
|
+
def disconnect(self):
|
|
68
|
+
"""Close connection to ChromaDB."""
|
|
69
|
+
self._client = None
|
|
70
|
+
self._current_collection = None
|
|
71
|
+
|
|
72
|
+
@property
|
|
73
|
+
def is_connected(self) -> bool:
|
|
74
|
+
"""Check if connected to ChromaDB."""
|
|
75
|
+
return self._client is not None
|
|
76
|
+
|
|
77
|
+
def list_collections(self) -> List[str]:
|
|
78
|
+
"""
|
|
79
|
+
Get list of all collections.
|
|
80
|
+
|
|
81
|
+
Returns:
|
|
82
|
+
List of collection names
|
|
83
|
+
"""
|
|
84
|
+
if not self._client:
|
|
85
|
+
return []
|
|
86
|
+
try:
|
|
87
|
+
collections = self._client.list_collections()
|
|
88
|
+
return [col.name for col in collections]
|
|
89
|
+
except Exception as e:
|
|
90
|
+
print(f"Failed to list collections: {e}")
|
|
91
|
+
return []
|
|
92
|
+
|
|
93
|
+
def get_collection(self, name: str) -> Optional[Collection]:
|
|
94
|
+
"""
|
|
95
|
+
Get or create a collection.
|
|
96
|
+
|
|
97
|
+
Args:
|
|
98
|
+
name: Collection name
|
|
99
|
+
|
|
100
|
+
Returns:
|
|
101
|
+
Collection object or None if failed
|
|
102
|
+
"""
|
|
103
|
+
if not self._client:
|
|
104
|
+
return None
|
|
105
|
+
try:
|
|
106
|
+
self._current_collection = self._client.get_or_create_collection(name=name)
|
|
107
|
+
return self._current_collection
|
|
108
|
+
except Exception as e:
|
|
109
|
+
print(f"Failed to get collection: {e}")
|
|
110
|
+
return None
|
|
111
|
+
|
|
112
|
+
def get_collection_info(self, name: str) -> Optional[Dict[str, Any]]:
|
|
113
|
+
"""
|
|
114
|
+
Get collection metadata and statistics.
|
|
115
|
+
|
|
116
|
+
Args:
|
|
117
|
+
name: Collection name
|
|
118
|
+
|
|
119
|
+
Returns:
|
|
120
|
+
Dictionary with collection info
|
|
121
|
+
"""
|
|
122
|
+
collection = self.get_collection(name)
|
|
123
|
+
if not collection:
|
|
124
|
+
return None
|
|
125
|
+
|
|
126
|
+
try:
|
|
127
|
+
count = collection.count()
|
|
128
|
+
# Get a sample to determine metadata fields
|
|
129
|
+
sample = collection.get(limit=1, include=["metadatas"])
|
|
130
|
+
metadata_fields = []
|
|
131
|
+
if sample and sample["metadatas"]:
|
|
132
|
+
metadata_fields = list(sample["metadatas"][0].keys()) if sample["metadatas"][0] else []
|
|
133
|
+
|
|
134
|
+
return {
|
|
135
|
+
"name": name,
|
|
136
|
+
"count": count,
|
|
137
|
+
"metadata_fields": metadata_fields,
|
|
138
|
+
}
|
|
139
|
+
except Exception as e:
|
|
140
|
+
print(f"Failed to get collection info: {e}")
|
|
141
|
+
return None
|
|
142
|
+
|
|
143
|
+
def query_collection(
|
|
144
|
+
self,
|
|
145
|
+
collection_name: str,
|
|
146
|
+
query_texts: Optional[List[str]] = None,
|
|
147
|
+
query_embeddings: Optional[List[List[float]]] = None,
|
|
148
|
+
n_results: int = 10,
|
|
149
|
+
where: Optional[Dict[str, Any]] = None,
|
|
150
|
+
where_document: Optional[Dict[str, Any]] = None,
|
|
151
|
+
) -> Optional[Dict[str, Any]]:
|
|
152
|
+
"""
|
|
153
|
+
Query a collection for similar vectors.
|
|
154
|
+
|
|
155
|
+
Args:
|
|
156
|
+
collection_name: Name of collection to query
|
|
157
|
+
query_texts: Text queries to embed and search
|
|
158
|
+
query_embeddings: Direct embedding vectors to search
|
|
159
|
+
n_results: Number of results to return
|
|
160
|
+
where: Metadata filter
|
|
161
|
+
where_document: Document content filter
|
|
162
|
+
|
|
163
|
+
Returns:
|
|
164
|
+
Query results or None if failed
|
|
165
|
+
"""
|
|
166
|
+
collection = self.get_collection(collection_name)
|
|
167
|
+
if not collection:
|
|
168
|
+
return None
|
|
169
|
+
|
|
170
|
+
try:
|
|
171
|
+
results = collection.query(
|
|
172
|
+
query_texts=query_texts,
|
|
173
|
+
query_embeddings=query_embeddings, # type: ignore
|
|
174
|
+
n_results=n_results,
|
|
175
|
+
where=where,
|
|
176
|
+
where_document=where_document, # type: ignore
|
|
177
|
+
include=["metadatas", "documents", "distances", "embeddings"]
|
|
178
|
+
)
|
|
179
|
+
return cast(Dict[str, Any], results)
|
|
180
|
+
except Exception as e:
|
|
181
|
+
print(f"Query failed: {e}")
|
|
182
|
+
return None
|
|
183
|
+
|
|
184
|
+
def get_all_items(
|
|
185
|
+
self,
|
|
186
|
+
collection_name: str,
|
|
187
|
+
limit: Optional[int] = None,
|
|
188
|
+
offset: Optional[int] = None,
|
|
189
|
+
where: Optional[Dict[str, Any]] = None,
|
|
190
|
+
) -> Optional[Dict[str, Any]]:
|
|
191
|
+
"""
|
|
192
|
+
Get all items from a collection.
|
|
193
|
+
|
|
194
|
+
Args:
|
|
195
|
+
collection_name: Name of collection
|
|
196
|
+
limit: Maximum number of items to return
|
|
197
|
+
offset: Number of items to skip
|
|
198
|
+
where: Metadata filter
|
|
199
|
+
|
|
200
|
+
Returns:
|
|
201
|
+
Collection items or None if failed
|
|
202
|
+
"""
|
|
203
|
+
collection = self.get_collection(collection_name)
|
|
204
|
+
if not collection:
|
|
205
|
+
return None
|
|
206
|
+
|
|
207
|
+
try:
|
|
208
|
+
results = collection.get(
|
|
209
|
+
limit=limit,
|
|
210
|
+
offset=offset,
|
|
211
|
+
where=where,
|
|
212
|
+
include=["metadatas", "documents", "embeddings"]
|
|
213
|
+
)
|
|
214
|
+
return cast(Dict[str, Any], results)
|
|
215
|
+
except Exception as e:
|
|
216
|
+
print(f"Failed to get items: {e}")
|
|
217
|
+
return None
|
|
218
|
+
|
|
219
|
+
def add_items(
|
|
220
|
+
self,
|
|
221
|
+
collection_name: str,
|
|
222
|
+
documents: List[str],
|
|
223
|
+
metadatas: Optional[List[Dict[str, Any]]] = None,
|
|
224
|
+
ids: Optional[List[str]] = None,
|
|
225
|
+
embeddings: Optional[List[List[float]]] = None,
|
|
226
|
+
) -> bool:
|
|
227
|
+
"""
|
|
228
|
+
Add items to a collection.
|
|
229
|
+
|
|
230
|
+
Args:
|
|
231
|
+
collection_name: Name of collection
|
|
232
|
+
documents: Document texts
|
|
233
|
+
metadatas: Metadata for each document
|
|
234
|
+
ids: IDs for each document
|
|
235
|
+
embeddings: Pre-computed embeddings
|
|
236
|
+
|
|
237
|
+
Returns:
|
|
238
|
+
True if successful, False otherwise
|
|
239
|
+
"""
|
|
240
|
+
collection = self.get_collection(collection_name)
|
|
241
|
+
if not collection:
|
|
242
|
+
return False
|
|
243
|
+
|
|
244
|
+
try:
|
|
245
|
+
collection.add(
|
|
246
|
+
documents=documents,
|
|
247
|
+
metadatas=metadatas, # type: ignore
|
|
248
|
+
ids=ids, # type: ignore
|
|
249
|
+
embeddings=embeddings # type: ignore
|
|
250
|
+
)
|
|
251
|
+
return True
|
|
252
|
+
except Exception as e:
|
|
253
|
+
print(f"Failed to add items: {e}")
|
|
254
|
+
return False
|
|
255
|
+
|
|
256
|
+
def update_items(
|
|
257
|
+
self,
|
|
258
|
+
collection_name: str,
|
|
259
|
+
ids: List[str],
|
|
260
|
+
documents: Optional[List[str]] = None,
|
|
261
|
+
metadatas: Optional[List[Dict[str, Any]]] = None,
|
|
262
|
+
embeddings: Optional[List[List[float]]] = None,
|
|
263
|
+
) -> bool:
|
|
264
|
+
"""
|
|
265
|
+
Update items in a collection.
|
|
266
|
+
|
|
267
|
+
Args:
|
|
268
|
+
collection_name: Name of collection
|
|
269
|
+
ids: IDs of items to update
|
|
270
|
+
documents: New document texts
|
|
271
|
+
metadatas: New metadata
|
|
272
|
+
embeddings: New embeddings
|
|
273
|
+
|
|
274
|
+
Returns:
|
|
275
|
+
True if successful, False otherwise
|
|
276
|
+
"""
|
|
277
|
+
collection = self.get_collection(collection_name)
|
|
278
|
+
if not collection:
|
|
279
|
+
return False
|
|
280
|
+
|
|
281
|
+
try:
|
|
282
|
+
collection.update(
|
|
283
|
+
ids=ids,
|
|
284
|
+
documents=documents,
|
|
285
|
+
metadatas=metadatas, # type: ignore
|
|
286
|
+
embeddings=embeddings # type: ignore
|
|
287
|
+
)
|
|
288
|
+
return True
|
|
289
|
+
except Exception as e:
|
|
290
|
+
print(f"Failed to update items: {e}")
|
|
291
|
+
return False
|
|
292
|
+
|
|
293
|
+
def delete_items(
|
|
294
|
+
self,
|
|
295
|
+
collection_name: str,
|
|
296
|
+
ids: Optional[List[str]] = None,
|
|
297
|
+
where: Optional[Dict[str, Any]] = None,
|
|
298
|
+
) -> bool:
|
|
299
|
+
"""
|
|
300
|
+
Delete items from a collection.
|
|
301
|
+
|
|
302
|
+
Args:
|
|
303
|
+
collection_name: Name of collection
|
|
304
|
+
ids: IDs of items to delete
|
|
305
|
+
where: Metadata filter for items to delete
|
|
306
|
+
|
|
307
|
+
Returns:
|
|
308
|
+
True if successful, False otherwise
|
|
309
|
+
"""
|
|
310
|
+
collection = self.get_collection(collection_name)
|
|
311
|
+
if not collection:
|
|
312
|
+
return False
|
|
313
|
+
|
|
314
|
+
try:
|
|
315
|
+
collection.delete(ids=ids, where=where)
|
|
316
|
+
return True
|
|
317
|
+
except Exception as e:
|
|
318
|
+
print(f"Failed to delete items: {e}")
|
|
319
|
+
return False
|
|
320
|
+
|
|
321
|
+
def delete_collection(self, name: str) -> bool:
|
|
322
|
+
"""
|
|
323
|
+
Delete an entire collection.
|
|
324
|
+
|
|
325
|
+
Args:
|
|
326
|
+
name: Collection name
|
|
327
|
+
|
|
328
|
+
Returns:
|
|
329
|
+
True if successful, False otherwise
|
|
330
|
+
"""
|
|
331
|
+
if not self._client:
|
|
332
|
+
return False
|
|
333
|
+
|
|
334
|
+
try:
|
|
335
|
+
self._client.delete_collection(name=name)
|
|
336
|
+
if self._current_collection and self._current_collection.name == name:
|
|
337
|
+
self._current_collection = None
|
|
338
|
+
return True
|
|
339
|
+
except Exception as e:
|
|
340
|
+
print(f"Failed to delete collection: {e}")
|
|
341
|
+
return False
|
|
342
|
+
|
|
343
|
+
# Implement base connection uniform APIs
|
|
344
|
+
def create_collection(self, name: str, vector_size: int, distance: str = "Cosine") -> bool:
|
|
345
|
+
"""Create a collection. Chroma doesn't require vector size at creation."""
|
|
346
|
+
return self.get_collection(name) is not None
|
|
347
|
+
|
|
348
|
+
def get_items(self, name: str, ids: List[str]) -> Dict[str, Any]:
|
|
349
|
+
"""Retrieve items by IDs."""
|
|
350
|
+
col = self.get_collection(name)
|
|
351
|
+
if not col:
|
|
352
|
+
raise RuntimeError("Collection not available")
|
|
353
|
+
return cast(Dict[str, Any], col.get(ids=ids, include=["metadatas", "documents", "embeddings"]))
|
|
354
|
+
|
|
355
|
+
def count_collection(self, name: str) -> int:
|
|
356
|
+
"""Count items in a collection."""
|
|
357
|
+
col = self.get_collection(name)
|
|
358
|
+
if not col:
|
|
359
|
+
return 0
|
|
360
|
+
try:
|
|
361
|
+
return col.count()
|
|
362
|
+
except Exception:
|
|
363
|
+
return 0
|
|
364
|
+
|
|
365
|
+
def get_supported_filter_operators(self) -> List[Dict[str, Any]]:
|
|
366
|
+
"""
|
|
367
|
+
Get filter operators supported by ChromaDB.
|
|
368
|
+
|
|
369
|
+
Returns:
|
|
370
|
+
List of operator dictionaries
|
|
371
|
+
"""
|
|
372
|
+
return [
|
|
373
|
+
{"name": "=", "server_side": True},
|
|
374
|
+
{"name": "!=", "server_side": True},
|
|
375
|
+
{"name": ">", "server_side": True},
|
|
376
|
+
{"name": ">=", "server_side": True},
|
|
377
|
+
{"name": "<", "server_side": True},
|
|
378
|
+
{"name": "<=", "server_side": True},
|
|
379
|
+
{"name": "in", "server_side": True},
|
|
380
|
+
{"name": "not in", "server_side": True},
|
|
381
|
+
# Client-side only operators
|
|
382
|
+
{"name": "contains", "server_side": False},
|
|
383
|
+
{"name": "not contains", "server_side": False},
|
|
384
|
+
]
|