askme-rag 0.1.1__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.
Files changed (32) hide show
  1. askme_rag-0.1.1/PKG-INFO +245 -0
  2. askme_rag-0.1.1/README.md +231 -0
  3. askme_rag-0.1.1/pyproject.toml +30 -0
  4. askme_rag-0.1.1/setup.cfg +4 -0
  5. askme_rag-0.1.1/src/askme/__init__.py +0 -0
  6. askme_rag-0.1.1/src/askme/config.py +73 -0
  7. askme_rag-0.1.1/src/askme/file_processors.py +40 -0
  8. askme_rag-0.1.1/src/askme/list_files.py +49 -0
  9. askme_rag-0.1.1/src/askme/llm.py +104 -0
  10. askme_rag-0.1.1/src/askme/main.py +617 -0
  11. askme_rag-0.1.1/src/askme/models.py +16 -0
  12. askme_rag-0.1.1/src/askme/scanner.py +90 -0
  13. askme_rag-0.1.1/src/askme/sparse.py +107 -0
  14. askme_rag-0.1.1/src/askme/ui.py +168 -0
  15. askme_rag-0.1.1/src/askme/utils.py +69 -0
  16. askme_rag-0.1.1/src/askme/vector_db.py +433 -0
  17. askme_rag-0.1.1/src/askme_rag.egg-info/PKG-INFO +245 -0
  18. askme_rag-0.1.1/src/askme_rag.egg-info/SOURCES.txt +30 -0
  19. askme_rag-0.1.1/src/askme_rag.egg-info/dependency_links.txt +1 -0
  20. askme_rag-0.1.1/src/askme_rag.egg-info/entry_points.txt +2 -0
  21. askme_rag-0.1.1/src/askme_rag.egg-info/requires.txt +7 -0
  22. askme_rag-0.1.1/src/askme_rag.egg-info/top_level.txt +1 -0
  23. askme_rag-0.1.1/tests/test_config_defaults.py +81 -0
  24. askme_rag-0.1.1/tests/test_connection_checks.py +71 -0
  25. askme_rag-0.1.1/tests/test_file_processors.py +49 -0
  26. askme_rag-0.1.1/tests/test_history_context.py +122 -0
  27. askme_rag-0.1.1/tests/test_list_files.py +79 -0
  28. askme_rag-0.1.1/tests/test_main_module.py +388 -0
  29. askme_rag-0.1.1/tests/test_scanner_integration.py +129 -0
  30. askme_rag-0.1.1/tests/test_sparse_bm25.py +48 -0
  31. askme_rag-0.1.1/tests/test_utils_extra.py +65 -0
  32. askme_rag-0.1.1/tests/test_vector_db_extra.py +371 -0
@@ -0,0 +1,245 @@
1
+ Metadata-Version: 2.4
2
+ Name: askme-rag
3
+ Version: 0.1.1
4
+ Summary: CLI RAG tool for local codebases
5
+ Requires-Python: >=3.10
6
+ Description-Content-Type: text/markdown
7
+ Requires-Dist: qdrant-client
8
+ Requires-Dist: openai
9
+ Requires-Dist: rich
10
+ Requires-Dist: python-dotenv
11
+ Requires-Dist: prompt-toolkit
12
+ Requires-Dist: python-docx
13
+ Requires-Dist: pypdf
14
+
15
+ # askme - Codebase RAG CLI
16
+
17
+ A Command Line Interface (CLI) tool for local Retrieval-Augmented Generation (RAG) over your codebase. It scans, indexes
18
+ and lets you chat with your project using local LLMs (via an OpenAI-compatible API) and a Qdrant vector database,
19
+ returning context-aware answers with source citations.
20
+
21
+ The Python package is named `askme` and exposes a single `askme` console script.
22
+
23
+ ---
24
+
25
+ ### Key Features
26
+
27
+ - Local-first architecture
28
+ - Runs against any OpenAI-compatible LLM/embedding endpoint (e.g. Ollama) and a Qdrant instance you control.
29
+ - Code never has to leave your machine if you self-host the services.
30
+ - Hybrid retrieval (dense + sparse)
31
+ - Dense vectors are produced by the configured embedding model.
32
+ - Sparse vectors are computed locally with a built-in BM25 encoder (`src/askme/sparse.py`) for keyword/identifier
33
+ recall.
34
+ - Smart incremental indexing
35
+ - MD5 hashing of files detects changes, so only new or modified files are re-embedded.
36
+ - `/reindex` forces a full rebuild on demand.
37
+ - Source citations
38
+ - Answers include inline `[File: ...]` citations and a citation list, backed by chunk metadata stored in Qdrant.
39
+ - Conversation history
40
+ - Sessions are auto-saved as JSON in `.cfg/history/` and can be listed, loaded or deleted via `/history`.
41
+ - Rich interactive prompt
42
+ - Powered by `prompt-toolkit` with multiline and paste modes.
43
+
44
+ ---
45
+
46
+ ### Technology Stack
47
+
48
+ - Language: Python 3.11+
49
+ - Vector Database: Qdrant (1.x; remote, via Docker, or local file-based)
50
+ - LLM / Embeddings: any OpenAI-compatible API (tested with Ollama)
51
+ - Key libraries:
52
+ - `qdrant-client` - Qdrant access (dense + sparse vectors).
53
+ - `openai` - client for the OpenAI-compatible LLM and embedding APIs.
54
+ - `rich` - terminal UI, panels, markdown rendering.
55
+ - `prompt-toolkit` - interactive prompt with multiline support.
56
+ - `python-dotenv` - environment variable loading.
57
+
58
+ ---
59
+
60
+ ### Project Layout
61
+
62
+ ```
63
+ src/askme/
64
+ config.py # ConfigManager - .cfg/settings.json and chat history
65
+ scanner.py # FileScanner - traversal, filtering, MD5 hashing
66
+ vector_db.py # VectorDBConnector - chunking, embeddings, Qdrant I/O
67
+ sparse.py # BM25SparseEncoder - local sparse vector generation
68
+ llm.py # LLMInterface - prompt building and chat completion
69
+ list_files.py # /index command helpers
70
+ ui.py # rich-based UI helpers
71
+ utils.py # shared utilities
72
+ models.py # data models
73
+ main.py # entry point and interactive loop
74
+ tests/ # pytest test suite
75
+ ```
76
+
77
+ #### Data Flow
78
+
79
+ `Codebase` -> `FileScanner (hash + filter)` -> `VectorDBConnector (chunk + dense embed + BM25 sparse)` ->
80
+ `Qdrant (hybrid index)` -> `LLMInterface (query + retrieved context)` -> `User`
81
+
82
+ ---
83
+
84
+ ### Setup
85
+
86
+ #### Prerequisites
87
+
88
+ - Python 3.11+
89
+ - A Qdrant vector database, in one of two modes:
90
+ - `server` - a running Qdrant instance (local Docker or remote).
91
+ - `local` - a file-based store on disk, no server or Docker required (quick start).
92
+ - An OpenAI-compatible LLM and embeddings endpoint (e.g. Ollama).
93
+
94
+ #### 1. Start Qdrant (optional)
95
+
96
+ In `local` mode you can skip this step entirely - the index is stored on disk under
97
+ `qdrant_local_path` (default `./data/vector_store`) and persists between runs.
98
+
99
+ For `server` mode, run Qdrant locally via Docker:
100
+
101
+ ```bash
102
+ docker run -p 6333:6333 -p 6334:6334 \
103
+ -v $(pwd)/qdrant_storage:/qdrant/storage:z \
104
+ qdrant/qdrant
105
+ ```
106
+
107
+ If you start in `server` mode but the server is unreachable, `askme` offers to fall back to `local` file-based mode
108
+ (default answer: yes) and remembers the choice in
109
+ `.cfg/settings.json`.
110
+
111
+ #### 2. Prepare models (example with Ollama)
112
+
113
+ ```bash
114
+ ollama pull llama3
115
+ ollama pull embeddinggemma:300m
116
+ ```
117
+
118
+ #### 3. Install
119
+
120
+ Using `uv` (recommended for development):
121
+
122
+ ```bash
123
+ uv sync
124
+ uv run askme
125
+ ```
126
+
127
+ Or with pip from the project root:
128
+
129
+ ```bash
130
+ pip install .
131
+ askme
132
+ ```
133
+
134
+ Or directly from GitHub:
135
+
136
+ ```bash
137
+ pip install git+https://github.com/varsey/codebase-rag.git
138
+ ```
139
+
140
+ ---
141
+
142
+ ### Configuration
143
+
144
+ On the first run inside a project directory, `askme` prompts for configuration and stores it in `./.cfg/settings.json`.
145
+ Conversation histories are stored next to it under `./.cfg/history/`.
146
+
147
+ | Option | Default | Description |
148
+ |:--------------------|:--------------------------------------|:---------------------------------------------------------------------|
149
+ | `qdrant_mode` | `server` | Connection mode: `server` (remote/Docker) or `local` (file-based). |
150
+ | `qdrant_local_path` | `./data/vector_store` | On-disk path for the local file-based store (used in `local` mode). |
151
+ | `qdrant_host` | `localhost` | Hostname of the Qdrant service (used in `server` mode). |
152
+ | `qdrant_port` | `6333` | Port of the Qdrant service (used in `server` mode). |
153
+ | `llm_api_base` | `http://localhost:11434/v1` | Base URL of the OpenAI-compatible LLM API. |
154
+ | `llm_api_base_cert` | `` | Optional path to custom cert/CA bundle for LLM API TLS verification. |
155
+ | `vdb_api_base` | `http://localhost:11434/v1` | Base URL of the OpenAI-compatible embeddings API. |
156
+ | `api_key` | `sk-...` | API key passed to the OpenAI-compatible client. |
157
+ | `llm_model` | `llama3` | LLM model name. |
158
+ | `embedding_model` | `embeddinggemma:300m` | Embedding model name. |
159
+ | `chunk_size` | `750` | Chunk size in characters. |
160
+ | `chunk_overlap` | `250` | Overlap between chunks in characters. |
161
+ | `buffer_size` | `1048576` | Read buffer size for file scanning. |
162
+ | `top_n` | `10` | Number of chunks retrieved per query. |
163
+ | `collection_name` | auto-generated | Qdrant collection used for this project. |
164
+ | `file_extensions` | `.py, .md, .js, .ts, .go, .java, ...` | File types to index. |
165
+ | `excluded_dirs` | `.git, .venv, node_modules, ...` | Directories skipped during scanning. |
166
+
167
+ A real example lives in `.cfg/settings.json`.
168
+
169
+ ---
170
+
171
+ ### Usage
172
+
173
+ Inside the codebase you want to query:
174
+
175
+ ```bash
176
+ askme
177
+ ```
178
+
179
+ On first launch the tool guides you through configuration, scans the project and builds the Qdrant collection.
180
+ Subsequent runs reuse the existing index and only re-embed changed files.
181
+
182
+ #### Interactive commands
183
+
184
+ - `/new` - start a fresh conversation (resets the context window).
185
+ - `/history` - list, load or delete saved sessions in `.cfg/history/`.
186
+ - `/reindex` - clear the collection and re-embed the codebase from scratch.
187
+ - `/index` - show the files currently indexed.
188
+ - `/multiline` or `/m` - toggle persistent multiline input (submit with Alt+Enter).
189
+ - `/paste` - one-shot multiline input for a single query.
190
+ - `/exit` or `/quit` - end the session.
191
+
192
+ ---
193
+
194
+ ### Storage Schema
195
+
196
+ Each point in the Qdrant collection holds a dense vector, a BM25 sparse vector and the following payload:
197
+
198
+ ```json
199
+ {
200
+ "path": "string (relative path to file)",
201
+ "content": "string (the actual code chunk)",
202
+ "hash": "string (MD5 hash of the original file)",
203
+ "chunk_index": "int",
204
+ "total_chunks": "int"
205
+ }
206
+ ```
207
+
208
+ ---
209
+
210
+ ### Development
211
+
212
+ - Install dev dependencies and run tests with `uv`:
213
+
214
+ ```bash
215
+ uv sync
216
+ uv run pytest
217
+ ```
218
+
219
+ - The test suite covers config defaults, connection checks, scanner behaviour, sparse BM25 encoding, history/context
220
+ handling and the main module wiring.
221
+
222
+ ---
223
+
224
+ ### Known Limitations
225
+
226
+ - Very large files can be memory-heavy during scanning and embedding.
227
+ - Answer quality is bounded by the local LLM's context window.
228
+ - Only text-based source files are supported; binaries are skipped.
229
+ - Hybrid search quality depends on the corpus the BM25 encoder was fit on (the current project).
230
+
231
+ ---
232
+
233
+ ### Contributing
234
+
235
+ 1. Fork the repository.
236
+ 2. Create a feature branch (`git checkout -b feature/your-change`).
237
+ 3. Keep changes focused and follow the existing code style.
238
+ 4. Add or update tests under `tests/` and make sure `uv run pytest` passes.
239
+ 5. Open a Pull Request with a clear description.
240
+
241
+ ---
242
+
243
+ ### License
244
+
245
+ MIT License - see the `LICENSE` file for details (or standard MIT terms if the file is missing).
@@ -0,0 +1,231 @@
1
+ # askme - Codebase RAG CLI
2
+
3
+ A Command Line Interface (CLI) tool for local Retrieval-Augmented Generation (RAG) over your codebase. It scans, indexes
4
+ and lets you chat with your project using local LLMs (via an OpenAI-compatible API) and a Qdrant vector database,
5
+ returning context-aware answers with source citations.
6
+
7
+ The Python package is named `askme` and exposes a single `askme` console script.
8
+
9
+ ---
10
+
11
+ ### Key Features
12
+
13
+ - Local-first architecture
14
+ - Runs against any OpenAI-compatible LLM/embedding endpoint (e.g. Ollama) and a Qdrant instance you control.
15
+ - Code never has to leave your machine if you self-host the services.
16
+ - Hybrid retrieval (dense + sparse)
17
+ - Dense vectors are produced by the configured embedding model.
18
+ - Sparse vectors are computed locally with a built-in BM25 encoder (`src/askme/sparse.py`) for keyword/identifier
19
+ recall.
20
+ - Smart incremental indexing
21
+ - MD5 hashing of files detects changes, so only new or modified files are re-embedded.
22
+ - `/reindex` forces a full rebuild on demand.
23
+ - Source citations
24
+ - Answers include inline `[File: ...]` citations and a citation list, backed by chunk metadata stored in Qdrant.
25
+ - Conversation history
26
+ - Sessions are auto-saved as JSON in `.cfg/history/` and can be listed, loaded or deleted via `/history`.
27
+ - Rich interactive prompt
28
+ - Powered by `prompt-toolkit` with multiline and paste modes.
29
+
30
+ ---
31
+
32
+ ### Technology Stack
33
+
34
+ - Language: Python 3.11+
35
+ - Vector Database: Qdrant (1.x; remote, via Docker, or local file-based)
36
+ - LLM / Embeddings: any OpenAI-compatible API (tested with Ollama)
37
+ - Key libraries:
38
+ - `qdrant-client` - Qdrant access (dense + sparse vectors).
39
+ - `openai` - client for the OpenAI-compatible LLM and embedding APIs.
40
+ - `rich` - terminal UI, panels, markdown rendering.
41
+ - `prompt-toolkit` - interactive prompt with multiline support.
42
+ - `python-dotenv` - environment variable loading.
43
+
44
+ ---
45
+
46
+ ### Project Layout
47
+
48
+ ```
49
+ src/askme/
50
+ config.py # ConfigManager - .cfg/settings.json and chat history
51
+ scanner.py # FileScanner - traversal, filtering, MD5 hashing
52
+ vector_db.py # VectorDBConnector - chunking, embeddings, Qdrant I/O
53
+ sparse.py # BM25SparseEncoder - local sparse vector generation
54
+ llm.py # LLMInterface - prompt building and chat completion
55
+ list_files.py # /index command helpers
56
+ ui.py # rich-based UI helpers
57
+ utils.py # shared utilities
58
+ models.py # data models
59
+ main.py # entry point and interactive loop
60
+ tests/ # pytest test suite
61
+ ```
62
+
63
+ #### Data Flow
64
+
65
+ `Codebase` -> `FileScanner (hash + filter)` -> `VectorDBConnector (chunk + dense embed + BM25 sparse)` ->
66
+ `Qdrant (hybrid index)` -> `LLMInterface (query + retrieved context)` -> `User`
67
+
68
+ ---
69
+
70
+ ### Setup
71
+
72
+ #### Prerequisites
73
+
74
+ - Python 3.11+
75
+ - A Qdrant vector database, in one of two modes:
76
+ - `server` - a running Qdrant instance (local Docker or remote).
77
+ - `local` - a file-based store on disk, no server or Docker required (quick start).
78
+ - An OpenAI-compatible LLM and embeddings endpoint (e.g. Ollama).
79
+
80
+ #### 1. Start Qdrant (optional)
81
+
82
+ In `local` mode you can skip this step entirely - the index is stored on disk under
83
+ `qdrant_local_path` (default `./data/vector_store`) and persists between runs.
84
+
85
+ For `server` mode, run Qdrant locally via Docker:
86
+
87
+ ```bash
88
+ docker run -p 6333:6333 -p 6334:6334 \
89
+ -v $(pwd)/qdrant_storage:/qdrant/storage:z \
90
+ qdrant/qdrant
91
+ ```
92
+
93
+ If you start in `server` mode but the server is unreachable, `askme` offers to fall back to `local` file-based mode
94
+ (default answer: yes) and remembers the choice in
95
+ `.cfg/settings.json`.
96
+
97
+ #### 2. Prepare models (example with Ollama)
98
+
99
+ ```bash
100
+ ollama pull llama3
101
+ ollama pull embeddinggemma:300m
102
+ ```
103
+
104
+ #### 3. Install
105
+
106
+ Using `uv` (recommended for development):
107
+
108
+ ```bash
109
+ uv sync
110
+ uv run askme
111
+ ```
112
+
113
+ Or with pip from the project root:
114
+
115
+ ```bash
116
+ pip install .
117
+ askme
118
+ ```
119
+
120
+ Or directly from GitHub:
121
+
122
+ ```bash
123
+ pip install git+https://github.com/varsey/codebase-rag.git
124
+ ```
125
+
126
+ ---
127
+
128
+ ### Configuration
129
+
130
+ On the first run inside a project directory, `askme` prompts for configuration and stores it in `./.cfg/settings.json`.
131
+ Conversation histories are stored next to it under `./.cfg/history/`.
132
+
133
+ | Option | Default | Description |
134
+ |:--------------------|:--------------------------------------|:---------------------------------------------------------------------|
135
+ | `qdrant_mode` | `server` | Connection mode: `server` (remote/Docker) or `local` (file-based). |
136
+ | `qdrant_local_path` | `./data/vector_store` | On-disk path for the local file-based store (used in `local` mode). |
137
+ | `qdrant_host` | `localhost` | Hostname of the Qdrant service (used in `server` mode). |
138
+ | `qdrant_port` | `6333` | Port of the Qdrant service (used in `server` mode). |
139
+ | `llm_api_base` | `http://localhost:11434/v1` | Base URL of the OpenAI-compatible LLM API. |
140
+ | `llm_api_base_cert` | `` | Optional path to custom cert/CA bundle for LLM API TLS verification. |
141
+ | `vdb_api_base` | `http://localhost:11434/v1` | Base URL of the OpenAI-compatible embeddings API. |
142
+ | `api_key` | `sk-...` | API key passed to the OpenAI-compatible client. |
143
+ | `llm_model` | `llama3` | LLM model name. |
144
+ | `embedding_model` | `embeddinggemma:300m` | Embedding model name. |
145
+ | `chunk_size` | `750` | Chunk size in characters. |
146
+ | `chunk_overlap` | `250` | Overlap between chunks in characters. |
147
+ | `buffer_size` | `1048576` | Read buffer size for file scanning. |
148
+ | `top_n` | `10` | Number of chunks retrieved per query. |
149
+ | `collection_name` | auto-generated | Qdrant collection used for this project. |
150
+ | `file_extensions` | `.py, .md, .js, .ts, .go, .java, ...` | File types to index. |
151
+ | `excluded_dirs` | `.git, .venv, node_modules, ...` | Directories skipped during scanning. |
152
+
153
+ A real example lives in `.cfg/settings.json`.
154
+
155
+ ---
156
+
157
+ ### Usage
158
+
159
+ Inside the codebase you want to query:
160
+
161
+ ```bash
162
+ askme
163
+ ```
164
+
165
+ On first launch the tool guides you through configuration, scans the project and builds the Qdrant collection.
166
+ Subsequent runs reuse the existing index and only re-embed changed files.
167
+
168
+ #### Interactive commands
169
+
170
+ - `/new` - start a fresh conversation (resets the context window).
171
+ - `/history` - list, load or delete saved sessions in `.cfg/history/`.
172
+ - `/reindex` - clear the collection and re-embed the codebase from scratch.
173
+ - `/index` - show the files currently indexed.
174
+ - `/multiline` or `/m` - toggle persistent multiline input (submit with Alt+Enter).
175
+ - `/paste` - one-shot multiline input for a single query.
176
+ - `/exit` or `/quit` - end the session.
177
+
178
+ ---
179
+
180
+ ### Storage Schema
181
+
182
+ Each point in the Qdrant collection holds a dense vector, a BM25 sparse vector and the following payload:
183
+
184
+ ```json
185
+ {
186
+ "path": "string (relative path to file)",
187
+ "content": "string (the actual code chunk)",
188
+ "hash": "string (MD5 hash of the original file)",
189
+ "chunk_index": "int",
190
+ "total_chunks": "int"
191
+ }
192
+ ```
193
+
194
+ ---
195
+
196
+ ### Development
197
+
198
+ - Install dev dependencies and run tests with `uv`:
199
+
200
+ ```bash
201
+ uv sync
202
+ uv run pytest
203
+ ```
204
+
205
+ - The test suite covers config defaults, connection checks, scanner behaviour, sparse BM25 encoding, history/context
206
+ handling and the main module wiring.
207
+
208
+ ---
209
+
210
+ ### Known Limitations
211
+
212
+ - Very large files can be memory-heavy during scanning and embedding.
213
+ - Answer quality is bounded by the local LLM's context window.
214
+ - Only text-based source files are supported; binaries are skipped.
215
+ - Hybrid search quality depends on the corpus the BM25 encoder was fit on (the current project).
216
+
217
+ ---
218
+
219
+ ### Contributing
220
+
221
+ 1. Fork the repository.
222
+ 2. Create a feature branch (`git checkout -b feature/your-change`).
223
+ 3. Keep changes focused and follow the existing code style.
224
+ 4. Add or update tests under `tests/` and make sure `uv run pytest` passes.
225
+ 5. Open a Pull Request with a clear description.
226
+
227
+ ---
228
+
229
+ ### License
230
+
231
+ MIT License - see the `LICENSE` file for details (or standard MIT terms if the file is missing).
@@ -0,0 +1,30 @@
1
+ [project]
2
+ name = "askme-rag"
3
+ version = "0.1.1"
4
+ description = "CLI RAG tool for local codebases"
5
+ readme = "README.md"
6
+ requires-python = ">=3.10"
7
+ dependencies = [
8
+ "qdrant-client",
9
+ "openai",
10
+ "rich",
11
+ "python-dotenv",
12
+ "prompt-toolkit",
13
+ "python-docx",
14
+ "pypdf",
15
+ ]
16
+
17
+ [project.scripts]
18
+ askme = "askme.main:main"
19
+
20
+ [build-system]
21
+ requires = ["setuptools>=61.0"]
22
+ build-backend = "setuptools.build_meta"
23
+
24
+ [dependency-groups]
25
+ dev = [
26
+ "pytest>=9.0.2",
27
+ ]
28
+ publish = [
29
+ "twine>=6.2.0",
30
+ ]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
File without changes
@@ -0,0 +1,73 @@
1
+ import json
2
+ from pathlib import Path
3
+ from typing import Dict, Any, List
4
+
5
+ QDRANT_MODE_SERVER = "server"
6
+ QDRANT_MODE_LOCAL = "local"
7
+ DEFAULT_QDRANT_MODE = QDRANT_MODE_SERVER
8
+ DEFAULT_QDRANT_LOCAL_PATH = "./data/vector_store"
9
+
10
+
11
+ class ConfigManager:
12
+ def __init__(self, root_dir: Path = None):
13
+ self.root_dir = root_dir or Path.cwd()
14
+ self.cfg_path = self.root_dir / ".cfg"
15
+ self.config_file = self.cfg_path / "settings.json"
16
+ self.history_dir = self.cfg_path / "history"
17
+ self.ensure_dirs()
18
+
19
+ def ensure_dirs(self):
20
+ """Create the .cfg folder and history subfolder if they don't exist."""
21
+ self.cfg_path.mkdir(exist_ok=True, parents=True)
22
+ self.history_dir.mkdir(exist_ok=True, parents=True)
23
+
24
+ def is_configured(self) -> bool:
25
+ """Check if the tool is already configured."""
26
+ return self.config_file.exists()
27
+
28
+ @staticmethod
29
+ def get_qdrant_mode(settings: Dict[str, Any]) -> str:
30
+ """Return the validated Qdrant connection mode ('server' or 'local')."""
31
+ mode = str(settings.get("qdrant_mode", DEFAULT_QDRANT_MODE)).lower()
32
+ return mode if mode in (QDRANT_MODE_SERVER, QDRANT_MODE_LOCAL) else DEFAULT_QDRANT_MODE
33
+
34
+ @staticmethod
35
+ def get_qdrant_local_path(settings: Dict[str, Any]) -> str:
36
+ """Return the on-disk path used for the local file-based Qdrant store."""
37
+ return settings.get("qdrant_local_path", DEFAULT_QDRANT_LOCAL_PATH)
38
+
39
+ def save_config(self, settings: Dict[str, Any]):
40
+ """Save user configuration to settings.json."""
41
+ with open(self.config_file, "w") as f:
42
+ json.dump(settings, f, indent=4)
43
+
44
+ def load_config(self) -> Dict[str, Any]:
45
+ """Load configuration from settings.json."""
46
+ if not self.is_configured():
47
+ return {}
48
+ with open(self.config_file, "r") as f:
49
+ return json.load(f)
50
+
51
+ def save_chat_history(self, chat_id: str, history_data: List[Dict[str, str]]):
52
+ """Save a conversation history to a JSON file in .cfg/history."""
53
+ history_file = self.history_dir / f"{chat_id}.json"
54
+ with open(history_file, "w") as f:
55
+ json.dump(history_data, f, indent=4)
56
+
57
+ def load_chat_history(self, chat_id: str) -> List[Dict[str, str]]:
58
+ """Load a conversation history from a JSON file in .cfg/history."""
59
+ history_file = self.history_dir / f"{chat_id}.json"
60
+ if not history_file.exists():
61
+ return []
62
+ with open(history_file, "r") as f:
63
+ return json.load(f)
64
+
65
+ def list_histories(self):
66
+ """List all conversation histories."""
67
+ return list(self.history_dir.glob("*.json"))
68
+
69
+ def delete_history(self, chat_id: str):
70
+ """Delete a conversation history file."""
71
+ history_file = self.history_dir / f"{chat_id}.json"
72
+ if history_file.exists():
73
+ history_file.unlink()
@@ -0,0 +1,40 @@
1
+ from abc import ABC, abstractmethod
2
+ from pathlib import Path
3
+
4
+ import docx
5
+ from pypdf import PdfReader
6
+
7
+
8
+ class FileProcessor(ABC):
9
+ @abstractmethod
10
+ def extract_text(self, file_path: Path) -> str:
11
+ """Extract text content from the file."""
12
+ pass
13
+
14
+ @abstractmethod
15
+ def get_supported_extensions(self) -> set[str]:
16
+ """Return a set of supported file extensions."""
17
+ pass
18
+
19
+
20
+ class DocxProcessor(FileProcessor):
21
+ def extract_text(self, file_path: Path) -> str:
22
+ """Extract text from a .docx file."""
23
+ doc = docx.Document(file_path)
24
+ return "\n".join([paragraph.text for paragraph in doc.paragraphs])
25
+
26
+ def get_supported_extensions(self) -> set[str]:
27
+ return {".docx"}
28
+
29
+
30
+ class PdfProcessor(FileProcessor):
31
+ def extract_text(self, file_path: Path) -> str:
32
+ """Extract text from a .pdf file."""
33
+ reader = PdfReader(file_path)
34
+ text_parts = []
35
+ for page in reader.pages:
36
+ text_parts.append(page.extract_text() or "")
37
+ return "\n".join(text_parts)
38
+
39
+ def get_supported_extensions(self) -> set[str]:
40
+ return {".pdf"}
@@ -0,0 +1,49 @@
1
+ import json
2
+ import os
3
+
4
+ from askme.config import ConfigManager
5
+ from askme.vector_db import VectorDBConnector
6
+
7
+
8
+ def list_indexed_files():
9
+ """
10
+ Retrieves and displays the list of currently indexed files in the vector database.
11
+ """
12
+ # Load the configuration from settings.json
13
+ config_path = 'settings.json'
14
+ if not os.path.exists(config_path):
15
+ print(f"Error: {config_path} not found.")
16
+ return
17
+
18
+ with open(config_path, 'r') as config_file:
19
+ settings = json.load(config_file)
20
+
21
+ # Initialize the VectorDBConnector with the settings
22
+ # We use .get() for optional parameters to handle cases where they might be missing
23
+ vdb = VectorDBConnector(
24
+ host=settings.get("qdrant_host", "localhost"),
25
+ port=settings.get("qdrant_port", 6333),
26
+ collection_name=settings.get("collection_name", "codebase"),
27
+ vdb_api_base=settings.get("vdb_api_base", "http://localhost:11434/v1"),
28
+ model_name=settings.get("embedding_model", "embeddinggemma:300m"),
29
+ chunk_size=settings.get("chunk_size", 1000),
30
+ chunk_overlap=settings.get("chunk_overlap", 100),
31
+ mode=ConfigManager.get_qdrant_mode(settings),
32
+ local_path=ConfigManager.get_qdrant_local_path(settings)
33
+ )
34
+
35
+ # Fetch the list of indexed files
36
+ # get_indexed_files returns a dict {path: hash}
37
+ indexed_files = vdb.get_indexed_files()
38
+
39
+ # Display the list of indexed files
40
+ if not indexed_files:
41
+ print("No files currently indexed.")
42
+ else:
43
+ print("Currently Indexed Files:")
44
+ # Sort paths for consistent output
45
+ for file_path in sorted(indexed_files.keys()):
46
+ print(f"- {file_path}")
47
+
48
+ if __name__ == "__main__":
49
+ list_indexed_files()