vector-inspector 0.2.1__tar.gz → 0.2.2__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.
- {vector_inspector-0.2.1 → vector_inspector-0.2.2}/PKG-INFO +9 -6
- {vector_inspector-0.2.1 → vector_inspector-0.2.2}/README.md +224 -225
- {vector_inspector-0.2.1 → vector_inspector-0.2.2}/pyproject.toml +7 -1
- {vector_inspector-0.2.1 → vector_inspector-0.2.2}/src/vector_inspector/ui/main_window.py +1 -1
- {vector_inspector-0.2.1 → vector_inspector-0.2.2}/tests/test_connections.py +60 -60
- {vector_inspector-0.2.1 → vector_inspector-0.2.2}/tests/test_filter_service.py +101 -101
- {vector_inspector-0.2.1 → vector_inspector-0.2.2}/tests/test_settings_service.py +101 -101
- {vector_inspector-0.2.1 → vector_inspector-0.2.2}/tests/vector_inspector.py +35 -35
- {vector_inspector-0.2.1 → vector_inspector-0.2.2}/src/vector_inspector/__init__.py +0 -0
- {vector_inspector-0.2.1 → vector_inspector-0.2.2}/src/vector_inspector/__main__.py +0 -0
- {vector_inspector-0.2.1 → vector_inspector-0.2.2}/src/vector_inspector/core/__init__.py +0 -0
- {vector_inspector-0.2.1 → vector_inspector-0.2.2}/src/vector_inspector/core/connections/__init__.py +0 -0
- {vector_inspector-0.2.1 → vector_inspector-0.2.2}/src/vector_inspector/core/connections/base_connection.py +0 -0
- {vector_inspector-0.2.1 → vector_inspector-0.2.2}/src/vector_inspector/core/connections/chroma_connection.py +0 -0
- {vector_inspector-0.2.1 → vector_inspector-0.2.2}/src/vector_inspector/core/connections/qdrant_connection.py +0 -0
- {vector_inspector-0.2.1 → vector_inspector-0.2.2}/src/vector_inspector/core/connections/template_connection.py +0 -0
- {vector_inspector-0.2.1 → vector_inspector-0.2.2}/src/vector_inspector/main.py +0 -0
- {vector_inspector-0.2.1 → vector_inspector-0.2.2}/src/vector_inspector/services/__init__.py +0 -0
- {vector_inspector-0.2.1 → vector_inspector-0.2.2}/src/vector_inspector/services/backup_restore_service.py +0 -0
- {vector_inspector-0.2.1 → vector_inspector-0.2.2}/src/vector_inspector/services/filter_service.py +0 -0
- {vector_inspector-0.2.1 → vector_inspector-0.2.2}/src/vector_inspector/services/import_export_service.py +0 -0
- {vector_inspector-0.2.1 → vector_inspector-0.2.2}/src/vector_inspector/services/settings_service.py +0 -0
- {vector_inspector-0.2.1 → vector_inspector-0.2.2}/src/vector_inspector/services/visualization_service.py +0 -0
- {vector_inspector-0.2.1 → vector_inspector-0.2.2}/src/vector_inspector/ui/__init__.py +0 -0
- {vector_inspector-0.2.1 → vector_inspector-0.2.2}/src/vector_inspector/ui/components/__init__.py +0 -0
- {vector_inspector-0.2.1 → vector_inspector-0.2.2}/src/vector_inspector/ui/components/backup_restore_dialog.py +0 -0
- {vector_inspector-0.2.1 → vector_inspector-0.2.2}/src/vector_inspector/ui/components/filter_builder.py +0 -0
- {vector_inspector-0.2.1 → vector_inspector-0.2.2}/src/vector_inspector/ui/components/item_dialog.py +0 -0
- {vector_inspector-0.2.1 → vector_inspector-0.2.2}/src/vector_inspector/ui/components/loading_dialog.py +0 -0
- {vector_inspector-0.2.1 → vector_inspector-0.2.2}/src/vector_inspector/ui/views/__init__.py +0 -0
- {vector_inspector-0.2.1 → vector_inspector-0.2.2}/src/vector_inspector/ui/views/collection_browser.py +0 -0
- {vector_inspector-0.2.1 → vector_inspector-0.2.2}/src/vector_inspector/ui/views/connection_view.py +0 -0
- {vector_inspector-0.2.1 → vector_inspector-0.2.2}/src/vector_inspector/ui/views/metadata_view.py +0 -0
- {vector_inspector-0.2.1 → vector_inspector-0.2.2}/src/vector_inspector/ui/views/search_view.py +0 -0
- {vector_inspector-0.2.1 → vector_inspector-0.2.2}/src/vector_inspector/ui/views/visualization_view.py +0 -0
|
@@ -1,9 +1,13 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: vector-inspector
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.2
|
|
4
4
|
Summary: A comprehensive desktop application for visualizing, querying, and managing vector database data
|
|
5
5
|
Author-Email: Anthony Dawson <anthonypdawson+github@gmail.com>
|
|
6
6
|
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/anthony-dawson/vector-inspector
|
|
8
|
+
Project-URL: Source, https://github.com/anthony-dawson/vector-inspector
|
|
9
|
+
Project-URL: Issues, https://github.com/anthony-dawson/vector-inspector/issues
|
|
10
|
+
Project-URL: Documentation, https://github.com/anthony-dawson/vector-inspector#readme
|
|
7
11
|
Requires-Python: ==3.12.*
|
|
8
12
|
Requires-Dist: chromadb>=0.4.22
|
|
9
13
|
Requires-Dist: qdrant-client>=1.7.0
|
|
@@ -26,12 +30,13 @@ A comprehensive desktop application for visualizing, querying, and managing vect
|
|
|
26
30
|
|
|
27
31
|
## Overview
|
|
28
32
|
|
|
33
|
+
Vector Inspector bridges the gap between vector databases and user-friendly data exploration tools. While vector databases are powerful for semantic search and AI applications, they often lack the intuitive inspection and management tools that traditional SQL databases have. This project aims to provide that missing layer.
|
|
34
|
+
|
|
29
35
|
## Table of Contents
|
|
30
36
|
|
|
31
37
|
- [Overview](#overview)
|
|
32
38
|
- [Key Features](#key-features)
|
|
33
39
|
- [Architecture](#architecture)
|
|
34
|
-
- [Application Structure](#application-structure)
|
|
35
40
|
- [Use Cases](#use-cases)
|
|
36
41
|
- [Feature Access](#feature-access)
|
|
37
42
|
- [Roadmap](#roadmap)
|
|
@@ -42,8 +47,6 @@ A comprehensive desktop application for visualizing, querying, and managing vect
|
|
|
42
47
|
- [License](#license)
|
|
43
48
|
- [Acknowledgments](#acknowledgments)
|
|
44
49
|
|
|
45
|
-
Vector Inspector bridges the gap between vector databases and user-friendly data exploration tools. While vector databases are powerful for semantic search and AI applications, they often lack the intuitive inspection and management tools that traditional SQL databases have. This project aims to provide that missing layer.
|
|
46
|
-
|
|
47
50
|
## Key Features
|
|
48
51
|
|
|
49
52
|
### 1. **Multi-Provider Support**
|
|
@@ -157,8 +160,8 @@ vector-inspector
|
|
|
157
160
|
|
|
158
161
|
```bash
|
|
159
162
|
# Clone the repository
|
|
160
|
-
git clone https://github.com/anthonypdawson/vector-
|
|
161
|
-
cd vector-
|
|
163
|
+
git clone https://github.com/anthonypdawson/vector-inspector.git
|
|
164
|
+
cd vector-inspector
|
|
162
165
|
|
|
163
166
|
# Install dependencies using PDM
|
|
164
167
|
pdm install
|
|
@@ -1,225 +1,224 @@
|
|
|
1
|
-
# Vector Inspector
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
A comprehensive desktop application for visualizing, querying, and managing vector database data. Similar to SQL database viewers, Vector Inspector provides an intuitive GUI for exploring vector embeddings, metadata, and performing similarity searches across multiple vector database providers.
|
|
5
|
-
|
|
6
|
-
## Overview
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
- [
|
|
13
|
-
- [
|
|
14
|
-
- [
|
|
15
|
-
- [
|
|
16
|
-
- [
|
|
17
|
-
- [
|
|
18
|
-
- [
|
|
19
|
-
- [
|
|
20
|
-
- [
|
|
21
|
-
- [
|
|
22
|
-
- [
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
-
|
|
30
|
-
-
|
|
31
|
-
|
|
32
|
-
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
- **
|
|
37
|
-
-
|
|
38
|
-
-
|
|
39
|
-
-
|
|
40
|
-
|
|
41
|
-
- **
|
|
42
|
-
- **
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
-
|
|
47
|
-
-
|
|
48
|
-
-
|
|
49
|
-
-
|
|
50
|
-
|
|
51
|
-
-
|
|
52
|
-
-
|
|
53
|
-
-
|
|
54
|
-
|
|
55
|
-
- **
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
- **
|
|
60
|
-
-
|
|
61
|
-
-
|
|
62
|
-
-
|
|
63
|
-
-
|
|
64
|
-
|
|
65
|
-
-
|
|
66
|
-
-
|
|
67
|
-
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
- **
|
|
73
|
-
-
|
|
74
|
-
-
|
|
75
|
-
-
|
|
76
|
-
|
|
77
|
-
- **
|
|
78
|
-
- **
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
-
|
|
83
|
-
-
|
|
84
|
-
-
|
|
85
|
-
|
|
86
|
-
-
|
|
87
|
-
-
|
|
88
|
-
-
|
|
89
|
-
|
|
90
|
-
-
|
|
91
|
-
-
|
|
92
|
-
-
|
|
93
|
-
|
|
94
|
-
-
|
|
95
|
-
-
|
|
96
|
-
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
#
|
|
146
|
-
./run.
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
{
|
|
159
|
-
|
|
160
|
-
"
|
|
161
|
-
"
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
#
|
|
180
|
-
./run.
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
-
|
|
192
|
-
-
|
|
193
|
-
-
|
|
194
|
-
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
-
|
|
207
|
-
-
|
|
208
|
-
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
-
|
|
217
|
-
-
|
|
218
|
-
-
|
|
219
|
-
-
|
|
220
|
-
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
**Contact**: Anthony Dawson
|
|
1
|
+
# Vector Inspector
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
A comprehensive desktop application for visualizing, querying, and managing vector database data. Similar to SQL database viewers, Vector Inspector provides an intuitive GUI for exploring vector embeddings, metadata, and performing similarity searches across multiple vector database providers.
|
|
5
|
+
|
|
6
|
+
## Overview
|
|
7
|
+
|
|
8
|
+
Vector Inspector bridges the gap between vector databases and user-friendly data exploration tools. While vector databases are powerful for semantic search and AI applications, they often lack the intuitive inspection and management tools that traditional SQL databases have. This project aims to provide that missing layer.
|
|
9
|
+
|
|
10
|
+
## Table of Contents
|
|
11
|
+
|
|
12
|
+
- [Overview](#overview)
|
|
13
|
+
- [Key Features](#key-features)
|
|
14
|
+
- [Architecture](#architecture)
|
|
15
|
+
- [Use Cases](#use-cases)
|
|
16
|
+
- [Feature Access](#feature-access)
|
|
17
|
+
- [Roadmap](#roadmap)
|
|
18
|
+
- [Installation](#installation)
|
|
19
|
+
- [Configuration](#configuration)
|
|
20
|
+
- [Development Setup](#development-setup)
|
|
21
|
+
- [Contributing](#contributing)
|
|
22
|
+
- [License](#license)
|
|
23
|
+
- [Acknowledgments](#acknowledgments)
|
|
24
|
+
|
|
25
|
+
## Key Features
|
|
26
|
+
|
|
27
|
+
### 1. **Multi-Provider Support**
|
|
28
|
+
- Connect to vector databases:
|
|
29
|
+
- ChromaDB (persistent local storage)
|
|
30
|
+
- Qdrant (remote server or embedded local)
|
|
31
|
+
- Unified interface regardless of backend provider
|
|
32
|
+
- Automatically saves last connection configuration
|
|
33
|
+
|
|
34
|
+
### 2. **Data Visualization**
|
|
35
|
+
- **Metadata Explorer**: Browse and filter vector entries by metadata fields
|
|
36
|
+
- **Vector Dimensionality Reduction**: Visualize high-dimensional vectors in 2D/3D using:
|
|
37
|
+
- t-SNE
|
|
38
|
+
- UMAP
|
|
39
|
+
- PCA
|
|
40
|
+
- **Cluster Visualization**: Color-code vectors by metadata categories or clustering results
|
|
41
|
+
- **Interactive Plots**: Zoom, pan, and select vectors for detailed inspection
|
|
42
|
+
- **Data Distribution Charts**: Histograms and statistics for metadata fields
|
|
43
|
+
|
|
44
|
+
### 3. **Search & Query Interface**
|
|
45
|
+
- **Similarity Search**:
|
|
46
|
+
- Text-to-vector search (with embedding model integration)
|
|
47
|
+
- Vector-to-vector search
|
|
48
|
+
- Find similar items to selected entries
|
|
49
|
+
- Adjustable top-k results and similarity thresholds
|
|
50
|
+
- **Metadata Filtering**:
|
|
51
|
+
- SQL-like query builder for metadata
|
|
52
|
+
- Combine vector similarity with metadata filters
|
|
53
|
+
- Advanced filtering: ranges, IN clauses, pattern matching
|
|
54
|
+
- **Hybrid Search**: Combine semantic search with keyword search
|
|
55
|
+
- **Query History**: Save and reuse frequent queries
|
|
56
|
+
|
|
57
|
+
### 4. **Data Management**
|
|
58
|
+
- **Browse Collections/Indexes**: View all available collections with statistics
|
|
59
|
+
- **CRUD Operations**:
|
|
60
|
+
- View individual vectors and their metadata
|
|
61
|
+
- Add new vectors (with auto-embedding options)
|
|
62
|
+
- Update metadata fields
|
|
63
|
+
- Delete vectors (single or batch)
|
|
64
|
+
- **Bulk Import/Export**:
|
|
65
|
+
- Import from CSV, JSON, Parquet
|
|
66
|
+
- Export query results to various formats
|
|
67
|
+
- Backup and restore collections
|
|
68
|
+
- **Schema Inspector**: View collection configuration, vector dimensions, metadata schema
|
|
69
|
+
|
|
70
|
+
### 5. **SQL-Like Experience**
|
|
71
|
+
- **Query Console**: Write queries in a familiar SQL-like syntax (where supported)
|
|
72
|
+
- **Results Grid**:
|
|
73
|
+
- Sortable, filterable table view
|
|
74
|
+
- Pagination for large result sets
|
|
75
|
+
- Column customization
|
|
76
|
+
- **Data Inspector**: Click any row to see full details including raw vector
|
|
77
|
+
- **Query Execution Plans**: Understand how queries are executed
|
|
78
|
+
- **Auto-completion**: Intelligent suggestions for collection names, fields, and operations
|
|
79
|
+
|
|
80
|
+
### 6. **Advanced Features**
|
|
81
|
+
- **Embedding Model Integration**:
|
|
82
|
+
- Use OpenAI, Cohere, HuggingFace models for text-to-vector conversion
|
|
83
|
+
- Local model support (sentence-transformers)
|
|
84
|
+
- Custom model integration
|
|
85
|
+
- **Vector Analysis**:
|
|
86
|
+
- Compute similarity matrices
|
|
87
|
+
- Identify outliers and anomalies
|
|
88
|
+
- Cluster analysis with k-means, DBSCAN
|
|
89
|
+
- **Embedding Inspector**:
|
|
90
|
+
- For similar collections or items, automatically identify which vector dimensions (activations) most contribute to the similarity
|
|
91
|
+
- Map key activations to interpretable concepts (e.g., 'humor', 'sadness', 'anger') using metadata or labels
|
|
92
|
+
- Generate human-readable explanations for why items are similar
|
|
93
|
+
- **Performance Monitoring**:
|
|
94
|
+
- Query latency tracking
|
|
95
|
+
- Index performance metrics
|
|
96
|
+
- Connection health monitoring
|
|
97
|
+
|
|
98
|
+
## Architecture
|
|
99
|
+
|
|
100
|
+
Vector Inspector is built with PySide6 (Qt for Python) for the GUI, providing a native desktop experience. The backend uses Python with support for multiple vector database providers through a unified interface.
|
|
101
|
+
|
|
102
|
+
For detailed architecture information, see [docs/architecture.md](docs/architecture.md).
|
|
103
|
+
|
|
104
|
+
## Use Cases
|
|
105
|
+
|
|
106
|
+
1. **AI/ML Development**: Inspect embeddings generated during model development
|
|
107
|
+
2. **RAG System Debugging**: Verify what documents are being retrieved
|
|
108
|
+
3. **Data Quality Assurance**: Identify poorly embedded or outlier vectors
|
|
109
|
+
4. **Production Monitoring**: Check vector database health and data consistency
|
|
110
|
+
5. **Data Migration**: Transfer data between vector database providers
|
|
111
|
+
6. **Education**: Learn and experiment with vector databases interactively
|
|
112
|
+
|
|
113
|
+
## Feature Access
|
|
114
|
+
|
|
115
|
+
Vector Inspector is available in both free (open source) and Pro versions. The free version includes all core features for ChromaDB and basic Qdrant support, while Pro adds advanced analytics and additional providers.
|
|
116
|
+
|
|
117
|
+
See [FEATURES.md](FEATURES.md) for a complete feature comparison.
|
|
118
|
+
|
|
119
|
+
## Roadmap
|
|
120
|
+
|
|
121
|
+
**Current Status**: ✅ Phase 2 Complete
|
|
122
|
+
|
|
123
|
+
See [ROADMAP.md](ROADMAP.md) for the complete development roadmap and planned features.
|
|
124
|
+
|
|
125
|
+
## Installation
|
|
126
|
+
|
|
127
|
+
### From PyPI (Recommended)
|
|
128
|
+
|
|
129
|
+
```bash
|
|
130
|
+
pip install vector-inspector
|
|
131
|
+
vector-inspector
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
### From Source
|
|
135
|
+
|
|
136
|
+
```bash
|
|
137
|
+
# Clone the repository
|
|
138
|
+
git clone https://github.com/anthonypdawson/vector-inspector.git
|
|
139
|
+
cd vector-inspector
|
|
140
|
+
|
|
141
|
+
# Install dependencies using PDM
|
|
142
|
+
pdm install
|
|
143
|
+
|
|
144
|
+
# Launch application
|
|
145
|
+
./run.sh # Linux/macOS
|
|
146
|
+
./run.bat # Windows
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
## Configuration
|
|
150
|
+
|
|
151
|
+
Paths are resolved relative to the project root (where `pyproject.toml` is). For example, entering `./data/chroma_db` will use the absolute path resolved from the project root.
|
|
152
|
+
|
|
153
|
+
The application automatically saves your last connection configuration to `~/.vector-viewer/settings.json`. The next time you launch the application, it will attempt to reconnect using the last saved settings.
|
|
154
|
+
|
|
155
|
+
Example settings structure:
|
|
156
|
+
```json
|
|
157
|
+
{
|
|
158
|
+
"last_connection": {
|
|
159
|
+
"provider": "chromadb",
|
|
160
|
+
"connection_type": "persistent",
|
|
161
|
+
"path": "./data/chroma_db"
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
## Development Setup
|
|
167
|
+
|
|
168
|
+
```bash
|
|
169
|
+
# Install PDM if you haven't already
|
|
170
|
+
pip install pdm
|
|
171
|
+
|
|
172
|
+
# Install dependencies with development tools (PDM will create venv automatically)
|
|
173
|
+
pdm install -d
|
|
174
|
+
|
|
175
|
+
# Run tests
|
|
176
|
+
pdm run pytest
|
|
177
|
+
|
|
178
|
+
# Run application in development mode
|
|
179
|
+
./run.sh # Linux/macOS
|
|
180
|
+
./run.bat # Windows
|
|
181
|
+
|
|
182
|
+
# Or use Python module directly from src directory:
|
|
183
|
+
cd src
|
|
184
|
+
pdm run python -m vector_viewer
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
## Contributing
|
|
188
|
+
|
|
189
|
+
Contributions are welcome! Areas where help is needed:
|
|
190
|
+
- Additional vector database provider integrations
|
|
191
|
+
- UI/UX improvements
|
|
192
|
+
- Performance optimizations
|
|
193
|
+
- Documentation
|
|
194
|
+
- Test coverage
|
|
195
|
+
|
|
196
|
+
Please see [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines.
|
|
197
|
+
|
|
198
|
+
## License
|
|
199
|
+
|
|
200
|
+
MIT License - See [LICENSE](LICENSE) file for details.
|
|
201
|
+
|
|
202
|
+
## Acknowledgments
|
|
203
|
+
|
|
204
|
+
This project draws inspiration from:
|
|
205
|
+
- DBeaver (SQL database viewer)
|
|
206
|
+
- MongoDB Compass (NoSQL database GUI)
|
|
207
|
+
- Pinecone Console
|
|
208
|
+
- Various vector database management tools
|
|
209
|
+
|
|
210
|
+
---
|
|
211
|
+
|
|
212
|
+
**Status**: ✅ Phase 2 Complete - Advanced Features Implemented!
|
|
213
|
+
|
|
214
|
+
**What's New in Phase 2:**
|
|
215
|
+
- 🔍 Advanced metadata filtering with customizable filter rules (AND/OR logic)
|
|
216
|
+
- ✏️ Double-click to edit items directly in the data browser
|
|
217
|
+
- 📥 Import data from CSV, JSON, and Parquet files
|
|
218
|
+
- 📤 Export filtered data to CSV, JSON, and Parquet formats
|
|
219
|
+
- 💾 Comprehensive backup and restore system for collections
|
|
220
|
+
- 🔄 Metadata filters integrated with search for powerful queries
|
|
221
|
+
|
|
222
|
+
See [GETTING_STARTED.md](GETTING_STARTED.md) for usage instructions and [IMPLEMENTATION_SUMMARY.md](IMPLEMENTATION_SUMMARY.md) for technical details.
|
|
223
|
+
|
|
224
|
+
**Contact**: Anthony Dawson
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "vector-inspector"
|
|
3
|
-
version = "0.2.
|
|
3
|
+
version = "0.2.2"
|
|
4
4
|
description = "A comprehensive desktop application for visualizing, querying, and managing vector database data"
|
|
5
5
|
authors = [
|
|
6
6
|
{ name = "Anthony Dawson", email = "anthonypdawson+github@gmail.com" },
|
|
@@ -25,6 +25,12 @@ readme = "README.md"
|
|
|
25
25
|
[project.license]
|
|
26
26
|
text = "MIT"
|
|
27
27
|
|
|
28
|
+
[project.urls]
|
|
29
|
+
Homepage = "https://github.com/anthony-dawson/vector-inspector"
|
|
30
|
+
Source = "https://github.com/anthony-dawson/vector-inspector"
|
|
31
|
+
Issues = "https://github.com/anthony-dawson/vector-inspector/issues"
|
|
32
|
+
Documentation = "https://github.com/anthony-dawson/vector-inspector#readme"
|
|
33
|
+
|
|
28
34
|
[project.scripts]
|
|
29
35
|
vector-inspector = "vector_inspector.main:main"
|
|
30
36
|
|
|
@@ -282,7 +282,7 @@ class MainWindow(QMainWindow):
|
|
|
282
282
|
"<h2>Vector Inspector 0.1.0</h2>"
|
|
283
283
|
"<p>A comprehensive desktop application for visualizing, "
|
|
284
284
|
"querying, and managing vector database data.</p>"
|
|
285
|
-
'<p><a href="https://github.com/anthonypdawson/vector-
|
|
285
|
+
'<p><a href="https://github.com/anthonypdawson/vector-inspector" style="color:#2980b9;">GitHub Project Page</a></p>'
|
|
286
286
|
"<hr />"
|
|
287
287
|
"<p>Built with PySide6 and ChromaDB</p>"
|
|
288
288
|
)
|
|
@@ -1,60 +1,60 @@
|
|
|
1
|
-
import pytest
|
|
2
|
-
from vector_inspector.core.connections.chroma_connection import ChromaDBConnection
|
|
3
|
-
from vector_inspector.core.connections.qdrant_connection import QdrantConnection
|
|
4
|
-
import uuid
|
|
5
|
-
|
|
6
|
-
@pytest.mark.parametrize("provider", ["chroma", "qdrant"])
|
|
7
|
-
def test_provider_integration(provider, tmp_path):
|
|
8
|
-
"""Test provider connection using standard add_items signature."""
|
|
9
|
-
collection_name = f"test_collection_{uuid.uuid4().hex[:8]}"
|
|
10
|
-
test_ids = ["id1", "id2"]
|
|
11
|
-
test_vectors = [[0.1, 0.2], [0.3, 0.4]]
|
|
12
|
-
test_docs = ["hello", "world"]
|
|
13
|
-
test_metadata = [{"type": "greeting"}, {"type": "noun"}]
|
|
14
|
-
|
|
15
|
-
if provider == "chroma":
|
|
16
|
-
conn = ChromaDBConnection()
|
|
17
|
-
assert conn.connect()
|
|
18
|
-
assert conn.create_collection(collection_name, vector_size=2)
|
|
19
|
-
# Use standard signature: collection_name, documents, metadatas, ids, embeddings
|
|
20
|
-
success = conn.add_items(
|
|
21
|
-
collection_name,
|
|
22
|
-
documents=test_docs,
|
|
23
|
-
metadatas=test_metadata,
|
|
24
|
-
ids=test_ids,
|
|
25
|
-
embeddings=test_vectors
|
|
26
|
-
)
|
|
27
|
-
assert success
|
|
28
|
-
assert collection_name in conn.list_collections()
|
|
29
|
-
# Verify items inserted
|
|
30
|
-
info = conn.get_collection_info(collection_name)
|
|
31
|
-
assert info["count"] == 2
|
|
32
|
-
res = conn.get_all_items(collection_name, limit=10)
|
|
33
|
-
assert len(res["documents"]) == 2
|
|
34
|
-
assert conn.delete_collection(collection_name)
|
|
35
|
-
assert collection_name not in conn.list_collections()
|
|
36
|
-
|
|
37
|
-
elif provider == "qdrant":
|
|
38
|
-
db_path = str(tmp_path / "qdrant_test")
|
|
39
|
-
conn = QdrantConnection(path=db_path)
|
|
40
|
-
assert conn.connect()
|
|
41
|
-
assert conn.create_collection(collection_name, vector_size=2, distance="Cosine")
|
|
42
|
-
# Use standard signature
|
|
43
|
-
success = conn.add_items(
|
|
44
|
-
collection_name,
|
|
45
|
-
documents=test_docs,
|
|
46
|
-
metadatas=test_metadata,
|
|
47
|
-
ids=test_ids,
|
|
48
|
-
embeddings=test_vectors
|
|
49
|
-
)
|
|
50
|
-
assert success
|
|
51
|
-
assert collection_name in conn.list_collections()
|
|
52
|
-
# Verify items inserted
|
|
53
|
-
info = conn.get_collection_info(collection_name)
|
|
54
|
-
if info["count"] == 0:
|
|
55
|
-
pytest.skip("Qdrant local upsert not supported in this environment")
|
|
56
|
-
assert info["count"] == 2
|
|
57
|
-
res = conn.get_all_items(collection_name, limit=10)
|
|
58
|
-
assert len(res["documents"]) == 2
|
|
59
|
-
assert conn.delete_collection(collection_name)
|
|
60
|
-
assert collection_name not in conn.list_collections()
|
|
1
|
+
import pytest
|
|
2
|
+
from vector_inspector.core.connections.chroma_connection import ChromaDBConnection
|
|
3
|
+
from vector_inspector.core.connections.qdrant_connection import QdrantConnection
|
|
4
|
+
import uuid
|
|
5
|
+
|
|
6
|
+
@pytest.mark.parametrize("provider", ["chroma", "qdrant"])
|
|
7
|
+
def test_provider_integration(provider, tmp_path):
|
|
8
|
+
"""Test provider connection using standard add_items signature."""
|
|
9
|
+
collection_name = f"test_collection_{uuid.uuid4().hex[:8]}"
|
|
10
|
+
test_ids = ["id1", "id2"]
|
|
11
|
+
test_vectors = [[0.1, 0.2], [0.3, 0.4]]
|
|
12
|
+
test_docs = ["hello", "world"]
|
|
13
|
+
test_metadata = [{"type": "greeting"}, {"type": "noun"}]
|
|
14
|
+
|
|
15
|
+
if provider == "chroma":
|
|
16
|
+
conn = ChromaDBConnection()
|
|
17
|
+
assert conn.connect()
|
|
18
|
+
assert conn.create_collection(collection_name, vector_size=2)
|
|
19
|
+
# Use standard signature: collection_name, documents, metadatas, ids, embeddings
|
|
20
|
+
success = conn.add_items(
|
|
21
|
+
collection_name,
|
|
22
|
+
documents=test_docs,
|
|
23
|
+
metadatas=test_metadata,
|
|
24
|
+
ids=test_ids,
|
|
25
|
+
embeddings=test_vectors
|
|
26
|
+
)
|
|
27
|
+
assert success
|
|
28
|
+
assert collection_name in conn.list_collections()
|
|
29
|
+
# Verify items inserted
|
|
30
|
+
info = conn.get_collection_info(collection_name)
|
|
31
|
+
assert info["count"] == 2
|
|
32
|
+
res = conn.get_all_items(collection_name, limit=10)
|
|
33
|
+
assert len(res["documents"]) == 2
|
|
34
|
+
assert conn.delete_collection(collection_name)
|
|
35
|
+
assert collection_name not in conn.list_collections()
|
|
36
|
+
|
|
37
|
+
elif provider == "qdrant":
|
|
38
|
+
db_path = str(tmp_path / "qdrant_test")
|
|
39
|
+
conn = QdrantConnection(path=db_path)
|
|
40
|
+
assert conn.connect()
|
|
41
|
+
assert conn.create_collection(collection_name, vector_size=2, distance="Cosine")
|
|
42
|
+
# Use standard signature
|
|
43
|
+
success = conn.add_items(
|
|
44
|
+
collection_name,
|
|
45
|
+
documents=test_docs,
|
|
46
|
+
metadatas=test_metadata,
|
|
47
|
+
ids=test_ids,
|
|
48
|
+
embeddings=test_vectors
|
|
49
|
+
)
|
|
50
|
+
assert success
|
|
51
|
+
assert collection_name in conn.list_collections()
|
|
52
|
+
# Verify items inserted
|
|
53
|
+
info = conn.get_collection_info(collection_name)
|
|
54
|
+
if info["count"] == 0:
|
|
55
|
+
pytest.skip("Qdrant local upsert not supported in this environment")
|
|
56
|
+
assert info["count"] == 2
|
|
57
|
+
res = conn.get_all_items(collection_name, limit=10)
|
|
58
|
+
assert len(res["documents"]) == 2
|
|
59
|
+
assert conn.delete_collection(collection_name)
|
|
60
|
+
assert collection_name not in conn.list_collections()
|
|
@@ -1,101 +1,101 @@
|
|
|
1
|
-
import pytest
|
|
2
|
-
from vector_inspector.services.filter_service import apply_client_side_filters
|
|
3
|
-
|
|
4
|
-
def sample_data():
|
|
5
|
-
return {
|
|
6
|
-
"ids": [1, 2, 3],
|
|
7
|
-
"documents": ["The quick brown fox", "Jumps over the lazy dog", "Hello world!"],
|
|
8
|
-
"metadatas": [
|
|
9
|
-
{"category": "animal", "author": "A"},
|
|
10
|
-
{"category": "animal", "author": "B"},
|
|
11
|
-
{"category": "greeting", "author": "C"},
|
|
12
|
-
],
|
|
13
|
-
"embeddings": [[0.1], [0.2], [0.3]],
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
def test_no_filters_returns_all():
|
|
17
|
-
data = sample_data()
|
|
18
|
-
result = apply_client_side_filters(data, [])
|
|
19
|
-
assert result == {
|
|
20
|
-
"ids": [1, 2, 3],
|
|
21
|
-
"documents": ["The quick brown fox", "Jumps over the lazy dog", "Hello world!"],
|
|
22
|
-
"metadatas": [
|
|
23
|
-
{"category": "animal", "author": "A"},
|
|
24
|
-
{"category": "animal", "author": "B"},
|
|
25
|
-
{"category": "greeting", "author": "C"},
|
|
26
|
-
],
|
|
27
|
-
"embeddings": [[0.1], [0.2], [0.3]],
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
def test_contains_document():
|
|
31
|
-
data = sample_data()
|
|
32
|
-
filters = [{"field": "document", "op": "contains", "value": "fox"}]
|
|
33
|
-
result = apply_client_side_filters(data, filters)
|
|
34
|
-
assert result["ids"] == [1]
|
|
35
|
-
assert result["documents"] == ["The quick brown fox"]
|
|
36
|
-
assert result["metadatas"] == [{"category": "animal", "author": "A"}]
|
|
37
|
-
assert result["embeddings"] == [[0.1]]
|
|
38
|
-
|
|
39
|
-
def test_not_contains_metadata():
|
|
40
|
-
data = sample_data()
|
|
41
|
-
filters = [{"field": "category", "op": "not_contains", "value": "animal"}]
|
|
42
|
-
result = apply_client_side_filters(data, filters)
|
|
43
|
-
assert result["ids"] == [3]
|
|
44
|
-
assert result["documents"] == ["Hello world!"]
|
|
45
|
-
assert result["metadatas"] == [{"category": "greeting", "author": "C"}]
|
|
46
|
-
assert result["embeddings"] == [[0.3]]
|
|
47
|
-
|
|
48
|
-
def test_multiple_filters():
|
|
49
|
-
data = sample_data()
|
|
50
|
-
filters = [
|
|
51
|
-
{"field": "category", "op": "contains", "value": "animal"},
|
|
52
|
-
{"field": "author", "op": "contains", "value": "B"},
|
|
53
|
-
]
|
|
54
|
-
result = apply_client_side_filters(data, filters)
|
|
55
|
-
assert result["ids"] == [2]
|
|
56
|
-
assert result["documents"] == ["Jumps over the lazy dog"]
|
|
57
|
-
assert result["metadatas"] == [{"category": "animal", "author": "B"}]
|
|
58
|
-
assert result["embeddings"] == [[0.2]]
|
|
59
|
-
|
|
60
|
-
def test_empty_data():
|
|
61
|
-
result = apply_client_side_filters({}, [])
|
|
62
|
-
assert result == {}
|
|
63
|
-
|
|
64
|
-
def test_missing_fields():
|
|
65
|
-
data = {"ids": [1], "documents": ["foo"]}
|
|
66
|
-
filters = [{"field": "category", "op": "contains", "value": "animal"}]
|
|
67
|
-
result = apply_client_side_filters(data, filters)
|
|
68
|
-
assert result["ids"] == []
|
|
69
|
-
assert result["documents"] == []
|
|
70
|
-
assert result["metadatas"] == []
|
|
71
|
-
|
|
72
|
-
def test_case_sensitivity():
|
|
73
|
-
data = {"ids": [1], "documents": ["Hello World"], "metadatas": [{"author": "Alice"}]}
|
|
74
|
-
filters = [{"field": "document", "op": "contains", "value": "hello world"}]
|
|
75
|
-
result = apply_client_side_filters(data, filters)
|
|
76
|
-
assert result["ids"] == [1]
|
|
77
|
-
|
|
78
|
-
def test_non_string_metadata_value():
|
|
79
|
-
data = {"ids": [1], "documents": ["foo"], "metadatas": [{"num": 123}]}
|
|
80
|
-
filters = [{"field": "num", "op": "contains", "value": "123"}]
|
|
81
|
-
result = apply_client_side_filters(data, filters)
|
|
82
|
-
assert result["ids"] == [1]
|
|
83
|
-
|
|
84
|
-
def test_unknown_operator():
|
|
85
|
-
data = {"ids": [1], "documents": ["foo"], "metadatas": [{"author": "Bob"}]}
|
|
86
|
-
filters = [{"field": "author", "op": "unknown", "value": "Bob"}]
|
|
87
|
-
result = apply_client_side_filters(data, filters)
|
|
88
|
-
# Unknown op: should not filter out
|
|
89
|
-
assert result["ids"] == [1]
|
|
90
|
-
|
|
91
|
-
def test_large_input():
|
|
92
|
-
data = {
|
|
93
|
-
"ids": list(range(1000)),
|
|
94
|
-
"documents": ["doc" + str(i) for i in range(1000)],
|
|
95
|
-
"metadatas": [{"author": "A" if i % 2 == 0 else "B"} for i in range(1000)],
|
|
96
|
-
"embeddings": [[i] for i in range(1000)],
|
|
97
|
-
}
|
|
98
|
-
filters = [{"field": "author", "op": "contains", "value": "A"}]
|
|
99
|
-
result = apply_client_side_filters(data, filters)
|
|
100
|
-
assert all(m["author"] == "A" for m in result["metadatas"])
|
|
101
|
-
assert len(result["ids"]) == 500
|
|
1
|
+
import pytest
|
|
2
|
+
from vector_inspector.services.filter_service import apply_client_side_filters
|
|
3
|
+
|
|
4
|
+
def sample_data():
|
|
5
|
+
return {
|
|
6
|
+
"ids": [1, 2, 3],
|
|
7
|
+
"documents": ["The quick brown fox", "Jumps over the lazy dog", "Hello world!"],
|
|
8
|
+
"metadatas": [
|
|
9
|
+
{"category": "animal", "author": "A"},
|
|
10
|
+
{"category": "animal", "author": "B"},
|
|
11
|
+
{"category": "greeting", "author": "C"},
|
|
12
|
+
],
|
|
13
|
+
"embeddings": [[0.1], [0.2], [0.3]],
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
def test_no_filters_returns_all():
|
|
17
|
+
data = sample_data()
|
|
18
|
+
result = apply_client_side_filters(data, [])
|
|
19
|
+
assert result == {
|
|
20
|
+
"ids": [1, 2, 3],
|
|
21
|
+
"documents": ["The quick brown fox", "Jumps over the lazy dog", "Hello world!"],
|
|
22
|
+
"metadatas": [
|
|
23
|
+
{"category": "animal", "author": "A"},
|
|
24
|
+
{"category": "animal", "author": "B"},
|
|
25
|
+
{"category": "greeting", "author": "C"},
|
|
26
|
+
],
|
|
27
|
+
"embeddings": [[0.1], [0.2], [0.3]],
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
def test_contains_document():
|
|
31
|
+
data = sample_data()
|
|
32
|
+
filters = [{"field": "document", "op": "contains", "value": "fox"}]
|
|
33
|
+
result = apply_client_side_filters(data, filters)
|
|
34
|
+
assert result["ids"] == [1]
|
|
35
|
+
assert result["documents"] == ["The quick brown fox"]
|
|
36
|
+
assert result["metadatas"] == [{"category": "animal", "author": "A"}]
|
|
37
|
+
assert result["embeddings"] == [[0.1]]
|
|
38
|
+
|
|
39
|
+
def test_not_contains_metadata():
|
|
40
|
+
data = sample_data()
|
|
41
|
+
filters = [{"field": "category", "op": "not_contains", "value": "animal"}]
|
|
42
|
+
result = apply_client_side_filters(data, filters)
|
|
43
|
+
assert result["ids"] == [3]
|
|
44
|
+
assert result["documents"] == ["Hello world!"]
|
|
45
|
+
assert result["metadatas"] == [{"category": "greeting", "author": "C"}]
|
|
46
|
+
assert result["embeddings"] == [[0.3]]
|
|
47
|
+
|
|
48
|
+
def test_multiple_filters():
|
|
49
|
+
data = sample_data()
|
|
50
|
+
filters = [
|
|
51
|
+
{"field": "category", "op": "contains", "value": "animal"},
|
|
52
|
+
{"field": "author", "op": "contains", "value": "B"},
|
|
53
|
+
]
|
|
54
|
+
result = apply_client_side_filters(data, filters)
|
|
55
|
+
assert result["ids"] == [2]
|
|
56
|
+
assert result["documents"] == ["Jumps over the lazy dog"]
|
|
57
|
+
assert result["metadatas"] == [{"category": "animal", "author": "B"}]
|
|
58
|
+
assert result["embeddings"] == [[0.2]]
|
|
59
|
+
|
|
60
|
+
def test_empty_data():
|
|
61
|
+
result = apply_client_side_filters({}, [])
|
|
62
|
+
assert result == {}
|
|
63
|
+
|
|
64
|
+
def test_missing_fields():
|
|
65
|
+
data = {"ids": [1], "documents": ["foo"]}
|
|
66
|
+
filters = [{"field": "category", "op": "contains", "value": "animal"}]
|
|
67
|
+
result = apply_client_side_filters(data, filters)
|
|
68
|
+
assert result["ids"] == []
|
|
69
|
+
assert result["documents"] == []
|
|
70
|
+
assert result["metadatas"] == []
|
|
71
|
+
|
|
72
|
+
def test_case_sensitivity():
|
|
73
|
+
data = {"ids": [1], "documents": ["Hello World"], "metadatas": [{"author": "Alice"}]}
|
|
74
|
+
filters = [{"field": "document", "op": "contains", "value": "hello world"}]
|
|
75
|
+
result = apply_client_side_filters(data, filters)
|
|
76
|
+
assert result["ids"] == [1]
|
|
77
|
+
|
|
78
|
+
def test_non_string_metadata_value():
|
|
79
|
+
data = {"ids": [1], "documents": ["foo"], "metadatas": [{"num": 123}]}
|
|
80
|
+
filters = [{"field": "num", "op": "contains", "value": "123"}]
|
|
81
|
+
result = apply_client_side_filters(data, filters)
|
|
82
|
+
assert result["ids"] == [1]
|
|
83
|
+
|
|
84
|
+
def test_unknown_operator():
|
|
85
|
+
data = {"ids": [1], "documents": ["foo"], "metadatas": [{"author": "Bob"}]}
|
|
86
|
+
filters = [{"field": "author", "op": "unknown", "value": "Bob"}]
|
|
87
|
+
result = apply_client_side_filters(data, filters)
|
|
88
|
+
# Unknown op: should not filter out
|
|
89
|
+
assert result["ids"] == [1]
|
|
90
|
+
|
|
91
|
+
def test_large_input():
|
|
92
|
+
data = {
|
|
93
|
+
"ids": list(range(1000)),
|
|
94
|
+
"documents": ["doc" + str(i) for i in range(1000)],
|
|
95
|
+
"metadatas": [{"author": "A" if i % 2 == 0 else "B"} for i in range(1000)],
|
|
96
|
+
"embeddings": [[i] for i in range(1000)],
|
|
97
|
+
}
|
|
98
|
+
filters = [{"field": "author", "op": "contains", "value": "A"}]
|
|
99
|
+
result = apply_client_side_filters(data, filters)
|
|
100
|
+
assert all(m["author"] == "A" for m in result["metadatas"])
|
|
101
|
+
assert len(result["ids"]) == 500
|
|
@@ -1,101 +1,101 @@
|
|
|
1
|
-
import json
|
|
2
|
-
from pathlib import Path
|
|
3
|
-
|
|
4
|
-
import pytest
|
|
5
|
-
|
|
6
|
-
from vector_inspector.services.settings_service import SettingsService
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
@pytest.fixture()
|
|
10
|
-
def temp_home(tmp_path):
|
|
11
|
-
# Monkeypatch Path.home() to point to a temporary directory for isolation
|
|
12
|
-
original_home = Path.home
|
|
13
|
-
Path.home = lambda: tmp_path # type: ignore
|
|
14
|
-
try:
|
|
15
|
-
yield tmp_path
|
|
16
|
-
finally:
|
|
17
|
-
Path.home = original_home # restore
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
def test_last_connection_roundtrip(temp_home):
|
|
21
|
-
svc = SettingsService()
|
|
22
|
-
assert svc.get_last_connection() is None
|
|
23
|
-
|
|
24
|
-
config = {
|
|
25
|
-
"provider": "chromadb",
|
|
26
|
-
"connection_type": "persistent",
|
|
27
|
-
"path": "./data/chroma_db",
|
|
28
|
-
}
|
|
29
|
-
svc.save_last_connection(config)
|
|
30
|
-
|
|
31
|
-
# Create a new service to ensure it reads from disk
|
|
32
|
-
svc2 = SettingsService()
|
|
33
|
-
assert svc2.get_last_connection() == config
|
|
34
|
-
|
|
35
|
-
# Validate file exists with expected content
|
|
36
|
-
settings_file = temp_home / ".vector-viewer" / "settings.json"
|
|
37
|
-
assert settings_file.exists()
|
|
38
|
-
data = json.loads(settings_file.read_text(encoding="utf-8"))
|
|
39
|
-
assert data["last_connection"] == config
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
def test_set_get_and_clear(temp_home):
|
|
43
|
-
svc = SettingsService()
|
|
44
|
-
|
|
45
|
-
assert svc.get("theme", "light") == "light"
|
|
46
|
-
svc.set("theme", "dark")
|
|
47
|
-
assert svc.get("theme") == "dark"
|
|
48
|
-
|
|
49
|
-
# Ensure persisted
|
|
50
|
-
settings_file = temp_home / ".vector-viewer" / "settings.json"
|
|
51
|
-
data = json.loads(settings_file.read_text(encoding="utf-8"))
|
|
52
|
-
assert data["theme"] == "dark"
|
|
53
|
-
|
|
54
|
-
# Clear and verify
|
|
55
|
-
svc.clear()
|
|
56
|
-
assert svc.get("theme") is None
|
|
57
|
-
|
|
58
|
-
# File should reflect cleared settings
|
|
59
|
-
data2 = json.loads(settings_file.read_text(encoding="utf-8"))
|
|
60
|
-
assert data2 == {}
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
def test_missing_settings_file(temp_home):
|
|
64
|
-
svc = SettingsService()
|
|
65
|
-
# Remove file if exists
|
|
66
|
-
settings_file = temp_home / ".vector-viewer" / "settings.json"
|
|
67
|
-
if settings_file.exists():
|
|
68
|
-
settings_file.unlink()
|
|
69
|
-
svc._load_settings()
|
|
70
|
-
assert svc.settings == {}
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
def test_invalid_json_file(temp_home):
|
|
74
|
-
settings_file = temp_home / ".vector-viewer" / "settings.json"
|
|
75
|
-
settings_file.parent.mkdir(parents=True, exist_ok=True)
|
|
76
|
-
settings_file.write_text("{ invalid json }", encoding="utf-8")
|
|
77
|
-
svc = SettingsService()
|
|
78
|
-
# Should fallback to empty settings
|
|
79
|
-
assert svc.settings == {}
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
def test_overwrite_key(temp_home):
|
|
83
|
-
svc = SettingsService()
|
|
84
|
-
svc.set("theme", "light")
|
|
85
|
-
svc.set("theme", "dark")
|
|
86
|
-
assert svc.get("theme") == "dark"
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
def test_unicode_and_large_value(temp_home):
|
|
90
|
-
svc = SettingsService()
|
|
91
|
-
unicode_val = "你好, мир, hello!"
|
|
92
|
-
large_val = "x" * 10000
|
|
93
|
-
svc.set("greeting", unicode_val)
|
|
94
|
-
svc.set("blob", large_val)
|
|
95
|
-
assert svc.get("greeting") == unicode_val
|
|
96
|
-
assert svc.get("blob") == large_val
|
|
97
|
-
# Validate persistence
|
|
98
|
-
settings_file = temp_home / ".vector-viewer" / "settings.json"
|
|
99
|
-
data = json.loads(settings_file.read_text(encoding="utf-8"))
|
|
100
|
-
assert data["greeting"] == unicode_val
|
|
101
|
-
assert data["blob"] == large_val
|
|
1
|
+
import json
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
|
|
4
|
+
import pytest
|
|
5
|
+
|
|
6
|
+
from vector_inspector.services.settings_service import SettingsService
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
@pytest.fixture()
|
|
10
|
+
def temp_home(tmp_path):
|
|
11
|
+
# Monkeypatch Path.home() to point to a temporary directory for isolation
|
|
12
|
+
original_home = Path.home
|
|
13
|
+
Path.home = lambda: tmp_path # type: ignore
|
|
14
|
+
try:
|
|
15
|
+
yield tmp_path
|
|
16
|
+
finally:
|
|
17
|
+
Path.home = original_home # restore
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def test_last_connection_roundtrip(temp_home):
|
|
21
|
+
svc = SettingsService()
|
|
22
|
+
assert svc.get_last_connection() is None
|
|
23
|
+
|
|
24
|
+
config = {
|
|
25
|
+
"provider": "chromadb",
|
|
26
|
+
"connection_type": "persistent",
|
|
27
|
+
"path": "./data/chroma_db",
|
|
28
|
+
}
|
|
29
|
+
svc.save_last_connection(config)
|
|
30
|
+
|
|
31
|
+
# Create a new service to ensure it reads from disk
|
|
32
|
+
svc2 = SettingsService()
|
|
33
|
+
assert svc2.get_last_connection() == config
|
|
34
|
+
|
|
35
|
+
# Validate file exists with expected content
|
|
36
|
+
settings_file = temp_home / ".vector-viewer" / "settings.json"
|
|
37
|
+
assert settings_file.exists()
|
|
38
|
+
data = json.loads(settings_file.read_text(encoding="utf-8"))
|
|
39
|
+
assert data["last_connection"] == config
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def test_set_get_and_clear(temp_home):
|
|
43
|
+
svc = SettingsService()
|
|
44
|
+
|
|
45
|
+
assert svc.get("theme", "light") == "light"
|
|
46
|
+
svc.set("theme", "dark")
|
|
47
|
+
assert svc.get("theme") == "dark"
|
|
48
|
+
|
|
49
|
+
# Ensure persisted
|
|
50
|
+
settings_file = temp_home / ".vector-viewer" / "settings.json"
|
|
51
|
+
data = json.loads(settings_file.read_text(encoding="utf-8"))
|
|
52
|
+
assert data["theme"] == "dark"
|
|
53
|
+
|
|
54
|
+
# Clear and verify
|
|
55
|
+
svc.clear()
|
|
56
|
+
assert svc.get("theme") is None
|
|
57
|
+
|
|
58
|
+
# File should reflect cleared settings
|
|
59
|
+
data2 = json.loads(settings_file.read_text(encoding="utf-8"))
|
|
60
|
+
assert data2 == {}
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def test_missing_settings_file(temp_home):
|
|
64
|
+
svc = SettingsService()
|
|
65
|
+
# Remove file if exists
|
|
66
|
+
settings_file = temp_home / ".vector-viewer" / "settings.json"
|
|
67
|
+
if settings_file.exists():
|
|
68
|
+
settings_file.unlink()
|
|
69
|
+
svc._load_settings()
|
|
70
|
+
assert svc.settings == {}
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def test_invalid_json_file(temp_home):
|
|
74
|
+
settings_file = temp_home / ".vector-viewer" / "settings.json"
|
|
75
|
+
settings_file.parent.mkdir(parents=True, exist_ok=True)
|
|
76
|
+
settings_file.write_text("{ invalid json }", encoding="utf-8")
|
|
77
|
+
svc = SettingsService()
|
|
78
|
+
# Should fallback to empty settings
|
|
79
|
+
assert svc.settings == {}
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def test_overwrite_key(temp_home):
|
|
83
|
+
svc = SettingsService()
|
|
84
|
+
svc.set("theme", "light")
|
|
85
|
+
svc.set("theme", "dark")
|
|
86
|
+
assert svc.get("theme") == "dark"
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def test_unicode_and_large_value(temp_home):
|
|
90
|
+
svc = SettingsService()
|
|
91
|
+
unicode_val = "你好, мир, hello!"
|
|
92
|
+
large_val = "x" * 10000
|
|
93
|
+
svc.set("greeting", unicode_val)
|
|
94
|
+
svc.set("blob", large_val)
|
|
95
|
+
assert svc.get("greeting") == unicode_val
|
|
96
|
+
assert svc.get("blob") == large_val
|
|
97
|
+
# Validate persistence
|
|
98
|
+
settings_file = temp_home / ".vector-viewer" / "settings.json"
|
|
99
|
+
data = json.loads(settings_file.read_text(encoding="utf-8"))
|
|
100
|
+
assert data["greeting"] == unicode_val
|
|
101
|
+
assert data["blob"] == large_val
|
|
@@ -1,35 +1,35 @@
|
|
|
1
|
-
import os
|
|
2
|
-
import sys
|
|
3
|
-
import tempfile
|
|
4
|
-
from pathlib import Path
|
|
5
|
-
|
|
6
|
-
import pytest
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
def run_tests():
|
|
10
|
-
project_path = Path(__file__).parent.parent
|
|
11
|
-
os.chdir(project_path)
|
|
12
|
-
|
|
13
|
-
# Determine any args to pass to pytest. If there aren't any,
|
|
14
|
-
# default to running the whole test suite.
|
|
15
|
-
args = sys.argv[1:]
|
|
16
|
-
if len(args) == 0:
|
|
17
|
-
args = ["tests"]
|
|
18
|
-
|
|
19
|
-
returncode = pytest.main(
|
|
20
|
-
[
|
|
21
|
-
# Turn up verbosity
|
|
22
|
-
"-vv",
|
|
23
|
-
# Disable color
|
|
24
|
-
"--color=no",
|
|
25
|
-
# Overwrite the cache directory to somewhere writable
|
|
26
|
-
"-o",
|
|
27
|
-
f"cache_dir={tempfile.gettempdir()}/.pytest_cache",
|
|
28
|
-
] + args
|
|
29
|
-
)
|
|
30
|
-
|
|
31
|
-
print(f">>>>>>>>>> EXIT {returncode} <<<<<<<<<<")
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
if __name__ == "__main__":
|
|
35
|
-
run_tests()
|
|
1
|
+
import os
|
|
2
|
+
import sys
|
|
3
|
+
import tempfile
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
|
|
6
|
+
import pytest
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def run_tests():
|
|
10
|
+
project_path = Path(__file__).parent.parent
|
|
11
|
+
os.chdir(project_path)
|
|
12
|
+
|
|
13
|
+
# Determine any args to pass to pytest. If there aren't any,
|
|
14
|
+
# default to running the whole test suite.
|
|
15
|
+
args = sys.argv[1:]
|
|
16
|
+
if len(args) == 0:
|
|
17
|
+
args = ["tests"]
|
|
18
|
+
|
|
19
|
+
returncode = pytest.main(
|
|
20
|
+
[
|
|
21
|
+
# Turn up verbosity
|
|
22
|
+
"-vv",
|
|
23
|
+
# Disable color
|
|
24
|
+
"--color=no",
|
|
25
|
+
# Overwrite the cache directory to somewhere writable
|
|
26
|
+
"-o",
|
|
27
|
+
f"cache_dir={tempfile.gettempdir()}/.pytest_cache",
|
|
28
|
+
] + args
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
print(f">>>>>>>>>> EXIT {returncode} <<<<<<<<<<")
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
if __name__ == "__main__":
|
|
35
|
+
run_tests()
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{vector_inspector-0.2.1 → vector_inspector-0.2.2}/src/vector_inspector/core/connections/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{vector_inspector-0.2.1 → vector_inspector-0.2.2}/src/vector_inspector/services/filter_service.py
RENAMED
|
File without changes
|
|
File without changes
|
{vector_inspector-0.2.1 → vector_inspector-0.2.2}/src/vector_inspector/services/settings_service.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{vector_inspector-0.2.1 → vector_inspector-0.2.2}/src/vector_inspector/ui/components/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{vector_inspector-0.2.1 → vector_inspector-0.2.2}/src/vector_inspector/ui/components/item_dialog.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{vector_inspector-0.2.1 → vector_inspector-0.2.2}/src/vector_inspector/ui/views/connection_view.py
RENAMED
|
File without changes
|
{vector_inspector-0.2.1 → vector_inspector-0.2.2}/src/vector_inspector/ui/views/metadata_view.py
RENAMED
|
File without changes
|
{vector_inspector-0.2.1 → vector_inspector-0.2.2}/src/vector_inspector/ui/views/search_view.py
RENAMED
|
File without changes
|
|
File without changes
|