ragjson 0.1.4__tar.gz → 0.2.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {ragjson-0.1.4 → ragjson-0.2.0}/.gitignore +2 -1
- {ragjson-0.1.4 → ragjson-0.2.0}/CLAUDE.md +0 -22
- ragjson-0.1.4/README.md → ragjson-0.2.0/PKG-INFO +58 -22
- ragjson-0.1.4/PKG-INFO → ragjson-0.2.0/README.md +46 -35
- {ragjson-0.1.4 → ragjson-0.2.0}/pyproject.toml +3 -3
- {ragjson-0.1.4 → ragjson-0.2.0}/src/ragjson/__init__.py +1 -1
- {ragjson-0.1.4 → ragjson-0.2.0}/src/ragjson/cli.py +20 -20
- ragjson-0.2.0/src/ragjson/commands/__init__.py +15 -0
- ragjson-0.2.0/src/ragjson/commands/_helpers.py +90 -0
- ragjson-0.2.0/src/ragjson/commands/destroy.py +38 -0
- ragjson-0.2.0/src/ragjson/commands/export.py +37 -0
- ragjson-0.2.0/src/ragjson/commands/index.py +183 -0
- ragjson-0.2.0/src/ragjson/commands/status.py +76 -0
- ragjson-0.2.0/src/ragjson/commands/update.py +250 -0
- {ragjson-0.1.4 → ragjson-0.2.0}/src/ragjson/config.py +27 -0
- ragjson-0.2.0/src/ragjson/embed/__init__.py +5 -0
- {ragjson-0.1.4/src/ragjson → ragjson-0.2.0/src/ragjson/embed}/embedding.py +13 -0
- ragjson-0.2.0/src/ragjson/index/__init__.py +0 -0
- ragjson-0.2.0/src/ragjson/index/backend.py +133 -0
- ragjson-0.2.0/src/ragjson/index/zvec_backend.py +121 -0
- ragjson-0.2.0/src/ragjson/parser/__init__.py +20 -0
- ragjson-0.2.0/src/ragjson/parser/backend.py +64 -0
- ragjson-0.2.0/src/ragjson/parser/markitdown_parser.py +44 -0
- ragjson-0.2.0/src/ragjson/splitter/__init__.py +18 -0
- ragjson-0.2.0/src/ragjson/splitter/backend.py +69 -0
- ragjson-0.2.0/src/ragjson/splitter/markdown.py +24 -0
- ragjson-0.2.0/src/ragjson/splitter/overlap.py +34 -0
- ragjson-0.2.0/src/ragjson/splitter/recursive.py +19 -0
- ragjson-0.2.0/src/ragjson/store/__init__.py +66 -0
- {ragjson-0.1.4/src/ragjson → ragjson-0.2.0/src/ragjson/store}/atomic_ops.py +15 -42
- ragjson-0.2.0/src/ragjson/store/mushroom.py +38 -0
- {ragjson-0.1.4/src/ragjson → ragjson-0.2.0/src/ragjson/store}/ragjson_file.py +1 -1
- ragjson-0.1.4/docs/design/mushroom-sync.md +0 -378
- ragjson-0.1.4/src/ragjson/commands.py +0 -549
- ragjson-0.1.4/src/ragjson/doc_parser.py +0 -45
- ragjson-0.1.4/src/ragjson/splitter.py +0 -82
- {ragjson-0.1.4 → ragjson-0.2.0}/.python-version +0 -0
- {ragjson-0.1.4 → ragjson-0.2.0}/docs/RAG-Storage-Spec.md +0 -0
- {ragjson-0.1.4 → ragjson-0.2.0}/src/ragjson/__main__.py +0 -0
- {ragjson-0.1.4 → ragjson-0.2.0}/src/ragjson/utils.py +0 -0
|
@@ -23,30 +23,8 @@ ragjson — 遵循 RAG-Storage-Spec 规范的文件系统 RAG 知识库构建工
|
|
|
23
23
|
- 蘑菇文件(`.mushroom`):`rag update` 生成的变更通知(JSONL),供外部索引工具消费
|
|
24
24
|
- 删除机制:源文件删除时 rename `.ragjson` → `.ragjson.deleted`(非状态转换)
|
|
25
25
|
|
|
26
|
-
## 命令
|
|
27
|
-
|
|
28
|
-
| 命令 | 说明 |
|
|
29
|
-
|------|------|
|
|
30
|
-
| `rag status [path] [-r]` | 查看知识库状态 |
|
|
31
|
-
| `rag update [path] [-r] [-j N]` | 增量更新(含删除/恢复检测 + 蘑菇生成) |
|
|
32
|
-
| `rag cleanup [path] [-r]` | 清理 .tmp 和 .ragjson.deleted |
|
|
33
|
-
| `rag destroy [path] [-r] [-y]` | 销毁 .rag/ 目录 |
|
|
34
|
-
| `rag export <output> [path] [-r] [-y]` | 导出 .ragjson 为 tar.gz |
|
|
35
26
|
|
|
36
27
|
## 规范文档
|
|
37
28
|
|
|
38
29
|
- `docs/RAG-Storage-Spec.md` — 协议规范(v2.2)
|
|
39
|
-
- `docs/design/mushroom-sync.md` — 蘑菇 + zvec 索引集成设计
|
|
40
|
-
|
|
41
|
-
## 开发
|
|
42
|
-
|
|
43
|
-
```bash
|
|
44
|
-
# 安装
|
|
45
|
-
uv pip install -e ".[all]"
|
|
46
|
-
|
|
47
|
-
# 运行
|
|
48
|
-
rag --help
|
|
49
30
|
|
|
50
|
-
# 构建
|
|
51
|
-
uv build
|
|
52
|
-
```
|
|
@@ -1,3 +1,15 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: ragjson
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Summary: 遵循RAG-Storage-Spec的知识库构建工具
|
|
5
|
+
Requires-Python: >=3.12
|
|
6
|
+
Requires-Dist: langchain-text-splitters>=0.3
|
|
7
|
+
Requires-Dist: markitdown[all]
|
|
8
|
+
Requires-Dist: openai>=1.0
|
|
9
|
+
Provides-Extra: index
|
|
10
|
+
Requires-Dist: zvec>=0.5; extra == 'index'
|
|
11
|
+
Description-Content-Type: text/markdown
|
|
12
|
+
|
|
1
13
|
# ragjson
|
|
2
14
|
|
|
3
15
|
遵循 [RAG-Storage-Spec](docs/RAG-Storage-Spec.md) 规范的基于文件系统的 RAG 知识库构建工具。
|
|
@@ -18,7 +30,7 @@
|
|
|
18
30
|
|
|
19
31
|
```bash
|
|
20
32
|
# 全局安装为独立命令(推荐,无需 venv)
|
|
21
|
-
uv tool install ragjson
|
|
33
|
+
uv tool install ragjson
|
|
22
34
|
```
|
|
23
35
|
|
|
24
36
|
或在项目虚拟环境中使用:
|
|
@@ -26,25 +38,17 @@ uv tool install ragjson[all]
|
|
|
26
38
|
```bash
|
|
27
39
|
uv venv
|
|
28
40
|
source .venv/bin/activate
|
|
29
|
-
uv pip install ragjson
|
|
41
|
+
uv pip install ragjson
|
|
30
42
|
```
|
|
31
43
|
|
|
32
44
|
本地开发模式(从源码安装):
|
|
33
45
|
|
|
34
46
|
```bash
|
|
35
47
|
uv venv
|
|
36
|
-
uv pip install -e
|
|
48
|
+
uv pip install -e .
|
|
37
49
|
source .venv/bin/activate
|
|
38
50
|
```
|
|
39
51
|
|
|
40
|
-
可选的 extras 组合:
|
|
41
|
-
|
|
42
|
-
| extras | 说明 |
|
|
43
|
-
|--------|------|
|
|
44
|
-
| `ragjson` | 基础安装(切片 + Embedding) |
|
|
45
|
-
| `ragjson[markitdown]` | 增加 markitdown 文档解析 |
|
|
46
|
-
| `ragjson[all]` | 全部依赖 |
|
|
47
|
-
|
|
48
52
|
## 配置
|
|
49
53
|
|
|
50
54
|
在用户 home 目录下创建 `~/.rag.config` 文件(JSON 格式):
|
|
@@ -60,6 +64,10 @@ source .venv/bin/activate
|
|
|
60
64
|
"splitter": {
|
|
61
65
|
"chunk_size": 1000,
|
|
62
66
|
"chunk_overlap": 200
|
|
67
|
+
},
|
|
68
|
+
"vector_database": {
|
|
69
|
+
"db_url": "zvec:/data/vectordb",
|
|
70
|
+
"index_type": "flat"
|
|
63
71
|
}
|
|
64
72
|
}
|
|
65
73
|
```
|
|
@@ -72,6 +80,8 @@ source .venv/bin/activate
|
|
|
72
80
|
| `embedding_model.dimension` | 向量维度 |
|
|
73
81
|
| `splitter.chunk_size` | 切片最大字符数 |
|
|
74
82
|
| `splitter.chunk_overlap` | 切片重叠字符数 |
|
|
83
|
+
| `vector_database.db_url` | 向量数据库目录(collection 在其下的 `zvec-db1/`) |
|
|
84
|
+
| `vector_database.index_type` | 索引类型:`flat` / `hnsw` |
|
|
75
85
|
|
|
76
86
|
## 使用方法
|
|
77
87
|
|
|
@@ -119,15 +129,6 @@ rag update ./docs -r -j 4
|
|
|
119
129
|
| `-j N` | 并行处理的目录数 | CPU 核心数 |
|
|
120
130
|
| `--splitter` | 切片器类型:`recursive` / `markdown` | `recursive` |
|
|
121
131
|
|
|
122
|
-
### 清理
|
|
123
|
-
|
|
124
|
-
清除残留的 `.tmp` 临时文件和 `.ragjson.deleted` 文件:
|
|
125
|
-
|
|
126
|
-
```bash
|
|
127
|
-
rag cleanup ./docs
|
|
128
|
-
rag cleanup ./docs -r # 递归清理
|
|
129
|
-
```
|
|
130
|
-
|
|
131
132
|
### 销毁
|
|
132
133
|
|
|
133
134
|
删除 `.rag/` 目录及所有元数据(高危操作,需确认):
|
|
@@ -147,6 +148,26 @@ rag export backup.tar.gz # 仅当前目录
|
|
|
147
148
|
rag export backup.tar.gz -r # 递归包含子目录
|
|
148
149
|
```
|
|
149
150
|
|
|
151
|
+
### 索引同步
|
|
152
|
+
|
|
153
|
+
消费 `.mushroom` 变更通知文件,将向量同步到向量数据库(需安装 `ragjson[index]`):
|
|
154
|
+
|
|
155
|
+
```bash
|
|
156
|
+
# 同步当前目录
|
|
157
|
+
rag index
|
|
158
|
+
|
|
159
|
+
# 递归同步子目录
|
|
160
|
+
rag index -r
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
向量数据库路径和索引类型在 `~/.rag.config` 的 `vector_database` 节配置。
|
|
164
|
+
|
|
165
|
+
安装索引依赖:
|
|
166
|
+
|
|
167
|
+
```bash
|
|
168
|
+
uv pip install ragjson[index]
|
|
169
|
+
```
|
|
170
|
+
|
|
150
171
|
## 目录结构
|
|
151
172
|
|
|
152
173
|
执行 `rag update` 后的目录结构示例:
|
|
@@ -204,10 +225,25 @@ INITIAL → CHUNKING → CHUNKED → EMBEDDING → EMBEDDED
|
|
|
204
225
|
↓
|
|
205
226
|
(源文件变更) ←─────────────────── INITIAL (回退)
|
|
206
227
|
|
|
207
|
-
源文件删除:任意状态 → rename .ragjson
|
|
228
|
+
源文件删除:任意状态 → rename .ragjson → .ragjson.deleted
|
|
208
229
|
源文件恢复:.ragjson.deleted → rename .ragjson → (update 检测 MD5)
|
|
209
230
|
```
|
|
210
231
|
|
|
232
|
+
## 项目架构
|
|
233
|
+
|
|
234
|
+
```
|
|
235
|
+
src/ragjson/
|
|
236
|
+
├── commands/ # CLI 命令(status, update, destroy, export, index)
|
|
237
|
+
├── embed/ # Embedding 生成(OpenAI 兼容 API)
|
|
238
|
+
├── index/ # 向量索引(zvec backend,可扩展)
|
|
239
|
+
├── parser/ # 文档解析(markitdown backend,可扩展)
|
|
240
|
+
├── splitter/ # 文本切片(recursive / markdown)
|
|
241
|
+
├── store/ # 存储层(ragjson 文件、原子操作、mushroom)
|
|
242
|
+
├── config.py # ~/.rag.config 配置加载
|
|
243
|
+
├── utils.py # 工具函数(MD5、向量编码、颜色输出)
|
|
244
|
+
└── cli.py # CLI 入口
|
|
245
|
+
```
|
|
246
|
+
|
|
211
247
|
## 开发
|
|
212
248
|
|
|
213
249
|
```bash
|
|
@@ -217,7 +253,7 @@ cd ragjson
|
|
|
217
253
|
|
|
218
254
|
# 创建虚拟环境并安装开发版本
|
|
219
255
|
uv venv
|
|
220
|
-
uv pip install -e ".[
|
|
256
|
+
uv pip install -e ".[index]"
|
|
221
257
|
source .venv/bin/activate
|
|
222
258
|
|
|
223
259
|
# 运行 CLI
|
|
@@ -1,16 +1,3 @@
|
|
|
1
|
-
Metadata-Version: 2.4
|
|
2
|
-
Name: ragjson
|
|
3
|
-
Version: 0.1.4
|
|
4
|
-
Summary: 遵循RAG-Storage-Spec的知识库构建工具
|
|
5
|
-
Requires-Python: >=3.12
|
|
6
|
-
Requires-Dist: langchain-text-splitters>=0.3
|
|
7
|
-
Requires-Dist: openai>=1.0
|
|
8
|
-
Provides-Extra: all
|
|
9
|
-
Requires-Dist: markitdown[all]; extra == 'all'
|
|
10
|
-
Provides-Extra: markitdown
|
|
11
|
-
Requires-Dist: markitdown[all]; extra == 'markitdown'
|
|
12
|
-
Description-Content-Type: text/markdown
|
|
13
|
-
|
|
14
1
|
# ragjson
|
|
15
2
|
|
|
16
3
|
遵循 [RAG-Storage-Spec](docs/RAG-Storage-Spec.md) 规范的基于文件系统的 RAG 知识库构建工具。
|
|
@@ -31,7 +18,7 @@ Description-Content-Type: text/markdown
|
|
|
31
18
|
|
|
32
19
|
```bash
|
|
33
20
|
# 全局安装为独立命令(推荐,无需 venv)
|
|
34
|
-
uv tool install ragjson
|
|
21
|
+
uv tool install ragjson
|
|
35
22
|
```
|
|
36
23
|
|
|
37
24
|
或在项目虚拟环境中使用:
|
|
@@ -39,25 +26,17 @@ uv tool install ragjson[all]
|
|
|
39
26
|
```bash
|
|
40
27
|
uv venv
|
|
41
28
|
source .venv/bin/activate
|
|
42
|
-
uv pip install ragjson
|
|
29
|
+
uv pip install ragjson
|
|
43
30
|
```
|
|
44
31
|
|
|
45
32
|
本地开发模式(从源码安装):
|
|
46
33
|
|
|
47
34
|
```bash
|
|
48
35
|
uv venv
|
|
49
|
-
uv pip install -e
|
|
36
|
+
uv pip install -e .
|
|
50
37
|
source .venv/bin/activate
|
|
51
38
|
```
|
|
52
39
|
|
|
53
|
-
可选的 extras 组合:
|
|
54
|
-
|
|
55
|
-
| extras | 说明 |
|
|
56
|
-
|--------|------|
|
|
57
|
-
| `ragjson` | 基础安装(切片 + Embedding) |
|
|
58
|
-
| `ragjson[markitdown]` | 增加 markitdown 文档解析 |
|
|
59
|
-
| `ragjson[all]` | 全部依赖 |
|
|
60
|
-
|
|
61
40
|
## 配置
|
|
62
41
|
|
|
63
42
|
在用户 home 目录下创建 `~/.rag.config` 文件(JSON 格式):
|
|
@@ -73,6 +52,10 @@ source .venv/bin/activate
|
|
|
73
52
|
"splitter": {
|
|
74
53
|
"chunk_size": 1000,
|
|
75
54
|
"chunk_overlap": 200
|
|
55
|
+
},
|
|
56
|
+
"vector_database": {
|
|
57
|
+
"db_url": "zvec:/data/vectordb",
|
|
58
|
+
"index_type": "flat"
|
|
76
59
|
}
|
|
77
60
|
}
|
|
78
61
|
```
|
|
@@ -85,6 +68,8 @@ source .venv/bin/activate
|
|
|
85
68
|
| `embedding_model.dimension` | 向量维度 |
|
|
86
69
|
| `splitter.chunk_size` | 切片最大字符数 |
|
|
87
70
|
| `splitter.chunk_overlap` | 切片重叠字符数 |
|
|
71
|
+
| `vector_database.db_url` | 向量数据库目录(collection 在其下的 `zvec-db1/`) |
|
|
72
|
+
| `vector_database.index_type` | 索引类型:`flat` / `hnsw` |
|
|
88
73
|
|
|
89
74
|
## 使用方法
|
|
90
75
|
|
|
@@ -132,15 +117,6 @@ rag update ./docs -r -j 4
|
|
|
132
117
|
| `-j N` | 并行处理的目录数 | CPU 核心数 |
|
|
133
118
|
| `--splitter` | 切片器类型:`recursive` / `markdown` | `recursive` |
|
|
134
119
|
|
|
135
|
-
### 清理
|
|
136
|
-
|
|
137
|
-
清除残留的 `.tmp` 临时文件和 `.ragjson.deleted` 文件:
|
|
138
|
-
|
|
139
|
-
```bash
|
|
140
|
-
rag cleanup ./docs
|
|
141
|
-
rag cleanup ./docs -r # 递归清理
|
|
142
|
-
```
|
|
143
|
-
|
|
144
120
|
### 销毁
|
|
145
121
|
|
|
146
122
|
删除 `.rag/` 目录及所有元数据(高危操作,需确认):
|
|
@@ -160,6 +136,26 @@ rag export backup.tar.gz # 仅当前目录
|
|
|
160
136
|
rag export backup.tar.gz -r # 递归包含子目录
|
|
161
137
|
```
|
|
162
138
|
|
|
139
|
+
### 索引同步
|
|
140
|
+
|
|
141
|
+
消费 `.mushroom` 变更通知文件,将向量同步到向量数据库(需安装 `ragjson[index]`):
|
|
142
|
+
|
|
143
|
+
```bash
|
|
144
|
+
# 同步当前目录
|
|
145
|
+
rag index
|
|
146
|
+
|
|
147
|
+
# 递归同步子目录
|
|
148
|
+
rag index -r
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
向量数据库路径和索引类型在 `~/.rag.config` 的 `vector_database` 节配置。
|
|
152
|
+
|
|
153
|
+
安装索引依赖:
|
|
154
|
+
|
|
155
|
+
```bash
|
|
156
|
+
uv pip install ragjson[index]
|
|
157
|
+
```
|
|
158
|
+
|
|
163
159
|
## 目录结构
|
|
164
160
|
|
|
165
161
|
执行 `rag update` 后的目录结构示例:
|
|
@@ -217,10 +213,25 @@ INITIAL → CHUNKING → CHUNKED → EMBEDDING → EMBEDDED
|
|
|
217
213
|
↓
|
|
218
214
|
(源文件变更) ←─────────────────── INITIAL (回退)
|
|
219
215
|
|
|
220
|
-
源文件删除:任意状态 → rename .ragjson
|
|
216
|
+
源文件删除:任意状态 → rename .ragjson → .ragjson.deleted
|
|
221
217
|
源文件恢复:.ragjson.deleted → rename .ragjson → (update 检测 MD5)
|
|
222
218
|
```
|
|
223
219
|
|
|
220
|
+
## 项目架构
|
|
221
|
+
|
|
222
|
+
```
|
|
223
|
+
src/ragjson/
|
|
224
|
+
├── commands/ # CLI 命令(status, update, destroy, export, index)
|
|
225
|
+
├── embed/ # Embedding 生成(OpenAI 兼容 API)
|
|
226
|
+
├── index/ # 向量索引(zvec backend,可扩展)
|
|
227
|
+
├── parser/ # 文档解析(markitdown backend,可扩展)
|
|
228
|
+
├── splitter/ # 文本切片(recursive / markdown)
|
|
229
|
+
├── store/ # 存储层(ragjson 文件、原子操作、mushroom)
|
|
230
|
+
├── config.py # ~/.rag.config 配置加载
|
|
231
|
+
├── utils.py # 工具函数(MD5、向量编码、颜色输出)
|
|
232
|
+
└── cli.py # CLI 入口
|
|
233
|
+
```
|
|
234
|
+
|
|
224
235
|
## 开发
|
|
225
236
|
|
|
226
237
|
```bash
|
|
@@ -230,7 +241,7 @@ cd ragjson
|
|
|
230
241
|
|
|
231
242
|
# 创建虚拟环境并安装开发版本
|
|
232
243
|
uv venv
|
|
233
|
-
uv pip install -e ".[
|
|
244
|
+
uv pip install -e ".[index]"
|
|
234
245
|
source .venv/bin/activate
|
|
235
246
|
|
|
236
247
|
# 运行 CLI
|
|
@@ -7,18 +7,18 @@ packages = ["src/ragjson"]
|
|
|
7
7
|
|
|
8
8
|
[project]
|
|
9
9
|
name = "ragjson"
|
|
10
|
-
version = "0.
|
|
10
|
+
version = "0.2.0"
|
|
11
11
|
description = "遵循RAG-Storage-Spec的知识库构建工具"
|
|
12
12
|
readme = "README.md"
|
|
13
13
|
requires-python = ">=3.12"
|
|
14
14
|
dependencies = [
|
|
15
15
|
"langchain-text-splitters>=0.3",
|
|
16
16
|
"openai>=1.0",
|
|
17
|
+
"markitdown[all]",
|
|
17
18
|
]
|
|
18
19
|
|
|
19
20
|
[project.optional-dependencies]
|
|
20
|
-
|
|
21
|
-
all = ["markitdown[all]"]
|
|
21
|
+
index = ["zvec>=0.5"]
|
|
22
22
|
|
|
23
23
|
[project.scripts]
|
|
24
24
|
rag = "ragjson.cli:main"
|
|
@@ -70,23 +70,6 @@ def main() -> None:
|
|
|
70
70
|
help="Text splitter type (default: recursive)",
|
|
71
71
|
)
|
|
72
72
|
|
|
73
|
-
# --- rag cleanup [path] [-r] ---
|
|
74
|
-
cleanup_parser = subparsers.add_parser(
|
|
75
|
-
"cleanup",
|
|
76
|
-
help="Clean .tmp files and remove .ragjson.deleted files",
|
|
77
|
-
)
|
|
78
|
-
cleanup_parser.add_argument(
|
|
79
|
-
"path",
|
|
80
|
-
nargs="?",
|
|
81
|
-
default=".",
|
|
82
|
-
help="Directory path to clean up (default: current directory)",
|
|
83
|
-
)
|
|
84
|
-
cleanup_parser.add_argument(
|
|
85
|
-
"-r", "--recursive",
|
|
86
|
-
action="store_true",
|
|
87
|
-
help="Recursively process subdirectories",
|
|
88
|
-
)
|
|
89
|
-
|
|
90
73
|
# --- rag destroy [path] [-r] ---
|
|
91
74
|
destroy_parser = subparsers.add_parser(
|
|
92
75
|
"destroy",
|
|
@@ -135,6 +118,23 @@ def main() -> None:
|
|
|
135
118
|
help="Skip overwrite confirmation prompt",
|
|
136
119
|
)
|
|
137
120
|
|
|
121
|
+
# --- rag index [path] [-r] ---
|
|
122
|
+
index_parser = subparsers.add_parser(
|
|
123
|
+
"index",
|
|
124
|
+
help="Consume .mushroom files and sync vectors to database",
|
|
125
|
+
)
|
|
126
|
+
index_parser.add_argument(
|
|
127
|
+
"path",
|
|
128
|
+
nargs="?",
|
|
129
|
+
default=".",
|
|
130
|
+
help="Directory path to scan for .mushroom files (default: current directory)",
|
|
131
|
+
)
|
|
132
|
+
index_parser.add_argument(
|
|
133
|
+
"-r", "--recursive",
|
|
134
|
+
action="store_true",
|
|
135
|
+
help="Recursively scan subdirectories for .mushroom files",
|
|
136
|
+
)
|
|
137
|
+
|
|
138
138
|
# Parse and dispatch
|
|
139
139
|
args = parser.parse_args()
|
|
140
140
|
|
|
@@ -144,9 +144,9 @@ def main() -> None:
|
|
|
144
144
|
|
|
145
145
|
# Dispatch to command implementations
|
|
146
146
|
from ragjson.commands import (
|
|
147
|
-
cmd_cleanup,
|
|
148
147
|
cmd_destroy,
|
|
149
148
|
cmd_export,
|
|
149
|
+
cmd_index,
|
|
150
150
|
cmd_status,
|
|
151
151
|
cmd_update,
|
|
152
152
|
)
|
|
@@ -160,12 +160,12 @@ def main() -> None:
|
|
|
160
160
|
jobs=args.jobs,
|
|
161
161
|
splitter_type=args.splitter,
|
|
162
162
|
)
|
|
163
|
-
elif args.command == "cleanup":
|
|
164
|
-
cmd_cleanup(args.path, args.recursive)
|
|
165
163
|
elif args.command == "destroy":
|
|
166
164
|
cmd_destroy(args.path, args.recursive, args.yes)
|
|
167
165
|
elif args.command == "export":
|
|
168
166
|
cmd_export(args.path, args.output, args.recursive, args.yes)
|
|
167
|
+
elif args.command == "index":
|
|
168
|
+
cmd_index(args.path, args.recursive)
|
|
169
169
|
else:
|
|
170
170
|
parser.print_help()
|
|
171
171
|
sys.exit(1)
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"""CLI command implementations — re-exported from submodules."""
|
|
2
|
+
|
|
3
|
+
from ragjson.commands.destroy import cmd_destroy
|
|
4
|
+
from ragjson.commands.export import cmd_export
|
|
5
|
+
from ragjson.commands.index import cmd_index
|
|
6
|
+
from ragjson.commands.status import cmd_status
|
|
7
|
+
from ragjson.commands.update import cmd_update
|
|
8
|
+
|
|
9
|
+
__all__ = [
|
|
10
|
+
"cmd_destroy",
|
|
11
|
+
"cmd_export",
|
|
12
|
+
"cmd_index",
|
|
13
|
+
"cmd_status",
|
|
14
|
+
"cmd_update",
|
|
15
|
+
]
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
"""Shared helper functions for CLI commands."""
|
|
2
|
+
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
|
|
5
|
+
from ragjson.parser import is_supported_file
|
|
6
|
+
from ragjson.store import (
|
|
7
|
+
STATUS_CHUNKED,
|
|
8
|
+
STATUS_CHUNKING,
|
|
9
|
+
STATUS_EMBEDDED,
|
|
10
|
+
STATUS_EMBEDDING,
|
|
11
|
+
STATUS_INITIAL,
|
|
12
|
+
is_source_changed,
|
|
13
|
+
ragjson_path_for,
|
|
14
|
+
read_ragjson,
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def collect_dirs(base: Path, recursive: bool) -> list[Path]:
|
|
19
|
+
"""Collect directories to scan, optionally recursive."""
|
|
20
|
+
dirs = [base]
|
|
21
|
+
if recursive:
|
|
22
|
+
for child in sorted(base.rglob("*")):
|
|
23
|
+
if child.is_dir() and child.name != ".rag":
|
|
24
|
+
dirs.append(child)
|
|
25
|
+
return dirs
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def list_supported_files(scan_dir: Path) -> list[Path]:
|
|
29
|
+
"""List supported files directly in a directory (not recursive)."""
|
|
30
|
+
files = []
|
|
31
|
+
for child in sorted(scan_dir.iterdir()):
|
|
32
|
+
if child.is_file() and is_supported_file(child):
|
|
33
|
+
files.append(child)
|
|
34
|
+
return files
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def files_needing_update(scan_dir: Path) -> list[Path]:
|
|
38
|
+
"""Find files in a directory that need processing.
|
|
39
|
+
|
|
40
|
+
A file needs update if:
|
|
41
|
+
- No corresponding .ragjson exists
|
|
42
|
+
- The .ragjson status is INITIAL (or stuck intermediate state)
|
|
43
|
+
- The source MD5 has changed
|
|
44
|
+
"""
|
|
45
|
+
result = []
|
|
46
|
+
|
|
47
|
+
for f in list_supported_files(scan_dir):
|
|
48
|
+
ragjson_path = ragjson_path_for(scan_dir, f.name)
|
|
49
|
+
data = read_ragjson(ragjson_path)
|
|
50
|
+
|
|
51
|
+
if data is None:
|
|
52
|
+
result.append(f)
|
|
53
|
+
elif data.processing.status == STATUS_EMBEDDED:
|
|
54
|
+
if is_source_changed(data, f):
|
|
55
|
+
result.append(f)
|
|
56
|
+
elif data.processing.status in (STATUS_CHUNKING, STATUS_EMBEDDING):
|
|
57
|
+
# Stuck intermediate state — needs recovery
|
|
58
|
+
result.append(f)
|
|
59
|
+
elif data.processing.status in (STATUS_INITIAL, STATUS_CHUNKED):
|
|
60
|
+
result.append(f)
|
|
61
|
+
|
|
62
|
+
return result
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def has_deletions(scan_dir: Path) -> bool:
|
|
66
|
+
"""Check if any .ragjson files have missing source files (need deletion)."""
|
|
67
|
+
rag_dir = scan_dir / ".rag"
|
|
68
|
+
if not rag_dir.exists():
|
|
69
|
+
return False
|
|
70
|
+
|
|
71
|
+
for rj_path in rag_dir.glob("*.ragjson"):
|
|
72
|
+
source_name = rj_path.name.removesuffix(".ragjson")
|
|
73
|
+
source_path = scan_dir / source_name
|
|
74
|
+
if not source_path.exists():
|
|
75
|
+
return True
|
|
76
|
+
return False
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def has_recoveries(scan_dir: Path) -> bool:
|
|
80
|
+
"""Check if any .ragjson.deleted files have their source files back."""
|
|
81
|
+
rag_dir = scan_dir / ".rag"
|
|
82
|
+
if not rag_dir.exists():
|
|
83
|
+
return False
|
|
84
|
+
|
|
85
|
+
for deleted_path in rag_dir.glob("*.ragjson.deleted"):
|
|
86
|
+
source_name = deleted_path.name.removesuffix(".ragjson.deleted")
|
|
87
|
+
source_path = scan_dir / source_name
|
|
88
|
+
if source_path.exists():
|
|
89
|
+
return True
|
|
90
|
+
return False
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
"""rag destroy [path] [-r] [-y] — remove .rag/ directories."""
|
|
2
|
+
|
|
3
|
+
import shutil
|
|
4
|
+
import sys
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
|
|
7
|
+
from ragjson.commands._helpers import collect_dirs
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def cmd_destroy(path: str, recursive: bool = False, yes: bool = False) -> None:
|
|
11
|
+
"""Remove .rag/ directory and all its contents (DESTRUCTIVE)."""
|
|
12
|
+
target = Path(path).resolve()
|
|
13
|
+
if not target.is_dir():
|
|
14
|
+
print(f"Error: Path is not a directory: {target}", file=sys.stderr)
|
|
15
|
+
sys.exit(1)
|
|
16
|
+
|
|
17
|
+
dirs_to_scan = collect_dirs(target, recursive)
|
|
18
|
+
rag_dirs = [d / ".rag" for d in dirs_to_scan if (d / ".rag").exists()]
|
|
19
|
+
|
|
20
|
+
if not rag_dirs:
|
|
21
|
+
print("No .rag/ directories found.")
|
|
22
|
+
return
|
|
23
|
+
|
|
24
|
+
print(f"WARNING: This will permanently delete {len(rag_dirs)} .rag/ directory(ies):")
|
|
25
|
+
for rag_dir in rag_dirs:
|
|
26
|
+
print(f" {rag_dir}")
|
|
27
|
+
|
|
28
|
+
if not yes:
|
|
29
|
+
answer = input("\nAre you sure? [y/N] ")
|
|
30
|
+
if answer.lower() not in ("y", "yes"):
|
|
31
|
+
print("Aborted.")
|
|
32
|
+
return
|
|
33
|
+
|
|
34
|
+
for rag_dir in rag_dirs:
|
|
35
|
+
shutil.rmtree(rag_dir)
|
|
36
|
+
print(f" Destroyed: {rag_dir}")
|
|
37
|
+
|
|
38
|
+
print(f"Total: destroyed {len(rag_dirs)} .rag/ directory(ies).")
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
"""rag export <output> [path] [-r] [-y] — export .ragjson to tar.gz."""
|
|
2
|
+
|
|
3
|
+
import sys
|
|
4
|
+
import tarfile
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def cmd_export(path: str, output: str, recursive: bool = False, yes: bool = False) -> None:
|
|
9
|
+
"""Export .ragjson files into a tar.gz archive."""
|
|
10
|
+
target = Path(path).resolve()
|
|
11
|
+
if not target.is_dir():
|
|
12
|
+
print(f"Error: Path is not a directory: {target}", file=sys.stderr)
|
|
13
|
+
sys.exit(1)
|
|
14
|
+
|
|
15
|
+
output_path = Path(output).resolve()
|
|
16
|
+
|
|
17
|
+
if output_path.exists() and not yes:
|
|
18
|
+
answer = input(f"File already exists: {output_path}\nOverwrite? [y/N] ")
|
|
19
|
+
if answer.lower() not in ("y", "yes"):
|
|
20
|
+
print("Aborted.")
|
|
21
|
+
return
|
|
22
|
+
|
|
23
|
+
if recursive:
|
|
24
|
+
ragjson_files = list(target.rglob(".rag/*.ragjson"))
|
|
25
|
+
else:
|
|
26
|
+
ragjson_files = list((target / ".rag").glob("*.ragjson"))
|
|
27
|
+
|
|
28
|
+
if not ragjson_files:
|
|
29
|
+
print("No .ragjson files found to export.")
|
|
30
|
+
return
|
|
31
|
+
|
|
32
|
+
with tarfile.open(output_path, "w:gz") as tar:
|
|
33
|
+
for rj_file in ragjson_files:
|
|
34
|
+
arcname = rj_file.relative_to(target)
|
|
35
|
+
tar.add(rj_file, arcname=arcname)
|
|
36
|
+
|
|
37
|
+
print(f"Exported {len(ragjson_files)} ragjson file(s) to: {output_path}")
|