embedsync 0.4.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.
- embedsync-0.4.0/.env.example +2 -0
- embedsync-0.4.0/.github/workflows/ci.yml +11 -0
- embedsync-0.4.0/.gitignore +5 -0
- embedsync-0.4.0/CHANGELOG.md +29 -0
- embedsync-0.4.0/CONTRIBUTING.md +30 -0
- embedsync-0.4.0/LICENSE +21 -0
- embedsync-0.4.0/PKG-INFO +128 -0
- embedsync-0.4.0/README.md +105 -0
- embedsync-0.4.0/SECURITY.md +14 -0
- embedsync-0.4.0/compose.yaml +11 -0
- embedsync-0.4.0/docker/Dockerfile +8 -0
- embedsync-0.4.0/examples/docs/api.md +3 -0
- embedsync-0.4.0/examples/docs/getting-started.md +5 -0
- embedsync-0.4.0/plans/README.md +6 -0
- embedsync-0.4.0/plans/v0.1-completed-foundation.md +220 -0
- embedsync-0.4.0/plans/v0.2-next-phase-roadmap.md +366 -0
- embedsync-0.4.0/pyproject.toml +46 -0
- embedsync-0.4.0/src/embedsync/__init__.py +3 -0
- embedsync-0.4.0/src/embedsync/chunking.py +39 -0
- embedsync-0.4.0/src/embedsync/cli.py +111 -0
- embedsync-0.4.0/src/embedsync/config.py +11 -0
- embedsync-0.4.0/src/embedsync/destinations/__init__.py +0 -0
- embedsync-0.4.0/src/embedsync/destinations/jsonl.py +55 -0
- embedsync-0.4.0/src/embedsync/destinations/memory.py +52 -0
- embedsync-0.4.0/src/embedsync/embedders.py +88 -0
- embedsync-0.4.0/src/embedsync/sources/__init__.py +0 -0
- embedsync-0.4.0/src/embedsync/sources/local.py +34 -0
- embedsync-0.4.0/src/embedsync/state/__init__.py +0 -0
- embedsync-0.4.0/src/embedsync/state/store.py +93 -0
- embedsync-0.4.0/src/embedsync/sync/__init__.py +0 -0
- embedsync-0.4.0/src/embedsync/sync/engine.py +107 -0
- embedsync-0.4.0/tests/test_embedders.py +51 -0
- embedsync-0.4.0/tests/test_engine.py +48 -0
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## [0.4.0] - 2026-09-14
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
- Ollama embedder via `--embedder ollama` or `--embedder ollama:<model>` (default model `nomic-embed-text`)
|
|
7
|
+
|
|
8
|
+
## [0.3.0] - 2026-08-19
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
- Chunk-level re-embed: unchanged chunks are skipped on document edit
|
|
12
|
+
- SQLite `chunks` table in the state store
|
|
13
|
+
|
|
14
|
+
## [0.2.0] - 2026-08-19
|
|
15
|
+
|
|
16
|
+
### Added
|
|
17
|
+
- Hash embedder for offline/CI runs (`--embedder hash`)
|
|
18
|
+
- Paragraph chunking with stable-ish chunk IDs
|
|
19
|
+
- JSONL destination (`--destination jsonl:/path`)
|
|
20
|
+
- Unchanged documents write 0 embeddings on the second run
|
|
21
|
+
|
|
22
|
+
### Notes
|
|
23
|
+
- Real pgvector/Qdrant destinations are still open
|
|
24
|
+
|
|
25
|
+
## [0.1.0] - 2026-08-18
|
|
26
|
+
|
|
27
|
+
### Added
|
|
28
|
+
- Local markdown source, SQLite state store, sync plan engine
|
|
29
|
+
- Memory destination stub and dry-run mode
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# Contributing
|
|
2
|
+
|
|
3
|
+
## Running tests
|
|
4
|
+
|
|
5
|
+
Prefer Docker Compose:
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
docker compose run --rm test
|
|
9
|
+
docker compose run --rm plan
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
Locally:
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
pip install -e ".[dev]"
|
|
16
|
+
pytest tests/ -v
|
|
17
|
+
embedsync plan examples/docs --state-db /tmp/embedsync-demo.db
|
|
18
|
+
embedsync run examples/docs --dry-run --state-db /tmp/embedsync-demo.db
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## Pull requests
|
|
22
|
+
|
|
23
|
+
- Keep embedder changes covered by unit tests (mock Ollama where needed)
|
|
24
|
+
- Update README/CHANGELOG for new destinations or CLI flags
|
|
25
|
+
- Prefer small PRs with a clear test plan
|
|
26
|
+
|
|
27
|
+
## Commit style
|
|
28
|
+
|
|
29
|
+
- Imperative subject line; mention the user-facing why when relevant
|
|
30
|
+
- Do not add AI co-author trailers (e.g. Co-authored-by: Cursor) to commits.
|
embedsync-0.4.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 embedsync contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
embedsync-0.4.0/PKG-INFO
ADDED
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: embedsync
|
|
3
|
+
Version: 0.4.0
|
|
4
|
+
Summary: Incremental synchronization between source documents and vector indexes
|
|
5
|
+
Project-URL: Homepage, https://github.com/yashshah9/embedsync
|
|
6
|
+
Project-URL: Repository, https://github.com/yashshah9/embedsync
|
|
7
|
+
Project-URL: Issues, https://github.com/yashshah9/embedsync/issues
|
|
8
|
+
Author-email: Yash Shah <yash376351@gmail.com>
|
|
9
|
+
License-Expression: MIT
|
|
10
|
+
License-File: LICENSE
|
|
11
|
+
Requires-Python: >=3.11
|
|
12
|
+
Requires-Dist: click>=8.1
|
|
13
|
+
Requires-Dist: pydantic-settings>=2.2
|
|
14
|
+
Requires-Dist: pydantic>=2.6
|
|
15
|
+
Requires-Dist: pyyaml>=6.0
|
|
16
|
+
Requires-Dist: rich>=13.7
|
|
17
|
+
Requires-Dist: structlog>=24.1
|
|
18
|
+
Provides-Extra: dev
|
|
19
|
+
Requires-Dist: mypy>=1.9; extra == 'dev'
|
|
20
|
+
Requires-Dist: pytest>=8.0; extra == 'dev'
|
|
21
|
+
Requires-Dist: ruff>=0.4; extra == 'dev'
|
|
22
|
+
Description-Content-Type: text/markdown
|
|
23
|
+
|
|
24
|
+
# embedsync
|
|
25
|
+
|
|
26
|
+
Incremental synchronization between **source documents** and **vector indexes** — detect changes, re-embed only deltas, and delete stale chunks.
|
|
27
|
+
|
|
28
|
+
[](LICENSE)
|
|
29
|
+
[](https://www.python.org/downloads/)
|
|
30
|
+
[](https://github.com/yashshah9/embedsync/actions/workflows/ci.yml)
|
|
31
|
+
|
|
32
|
+
> **Status:** v0.4 — hash + Ollama embedders, paragraph chunks, JSONL destination, chunk-level re-embed.
|
|
33
|
+
|
|
34
|
+
## 60-second try
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
docker compose run --rm plan # plan sync for examples/docs
|
|
38
|
+
docker compose run --rm test # pytest
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## Why this vs alternatives
|
|
42
|
+
|
|
43
|
+
| Approach | Strength | Gap |
|
|
44
|
+
|----------|----------|-----|
|
|
45
|
+
| **embedsync** | Content-hash deltas + pluggable embedders | Destinations still local (JSONL/memory) |
|
|
46
|
+
| Full re-embed pipelines | Simple mentally | Expensive; misses deletes |
|
|
47
|
+
| Framework ingestion (e.g. LlamaIndex) | Rich connectors | Change detection is DIY |
|
|
48
|
+
| One-off sync scripts | Fits one repo | No shared plan/state model |
|
|
49
|
+
|
|
50
|
+
## Problem
|
|
51
|
+
|
|
52
|
+
RAG indexes rot when documents change. Full re-embeds are expensive and miss deletes. Every team rebuilds change detection from scratch.
|
|
53
|
+
|
|
54
|
+
## Key features (v0.4)
|
|
55
|
+
|
|
56
|
+
- Content-hash change detection per document
|
|
57
|
+
- Sync plan: add / update / delete actions
|
|
58
|
+
- Hash embedder for offline/CI (`--embedder hash`)
|
|
59
|
+
- Ollama embedder (`--embedder ollama` or `ollama:nomic-embed-text`)
|
|
60
|
+
- JSONL or in-memory destination
|
|
61
|
+
- Unchanged docs/chunks skip re-embedding on the next run
|
|
62
|
+
|
|
63
|
+
## Architecture
|
|
64
|
+
|
|
65
|
+
```
|
|
66
|
+
embedsync run ./docs
|
|
67
|
+
├── LocalFileSource
|
|
68
|
+
├── StateStore (SQLite)
|
|
69
|
+
├── plan_sync() → diff
|
|
70
|
+
└── Destination (Memory → pgvector next)
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
## Installation
|
|
74
|
+
|
|
75
|
+
```bash
|
|
76
|
+
pip install embedsync
|
|
77
|
+
pip install -e ".[dev]"
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
## Usage
|
|
81
|
+
|
|
82
|
+
```bash
|
|
83
|
+
embedsync health
|
|
84
|
+
embedsync plan examples/docs --state-db /tmp/embedsync-demo.db
|
|
85
|
+
embedsync run examples/docs --dry-run --state-db /tmp/embedsync-demo.db
|
|
86
|
+
embedsync run examples/docs --embedder hash --destination memory --state-db /tmp/embedsync-demo.db
|
|
87
|
+
embedsync run examples/docs --embedder hash --destination jsonl:/tmp/index.jsonl
|
|
88
|
+
# Requires a running Ollama with an embedding model:
|
|
89
|
+
embedsync run examples/docs --embedder ollama --destination jsonl:/tmp/index.jsonl
|
|
90
|
+
embedsync run examples/docs --embedder ollama:nomic-embed-text --destination memory
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
## Docker
|
|
94
|
+
|
|
95
|
+
```bash
|
|
96
|
+
docker compose run --rm test
|
|
97
|
+
docker compose run --rm plan
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
## Configuration
|
|
101
|
+
|
|
102
|
+
| Variable | Default | Description |
|
|
103
|
+
|----------|---------|-------------|
|
|
104
|
+
| `EMBEDSYNC_STATE_DB` | `.embedsync/state.db` | State database path |
|
|
105
|
+
| `EMBEDSYNC_LOG_LEVEL` | `INFO` | Log level |
|
|
106
|
+
|
|
107
|
+
Ollama uses `OLLAMA_HOST` when set (otherwise the embedder default host).
|
|
108
|
+
|
|
109
|
+
## Roadmap
|
|
110
|
+
|
|
111
|
+
- [x] Pluggable embedder protocol + hash backend
|
|
112
|
+
- [x] JSONL destination (local stand-in)
|
|
113
|
+
- [x] Chunk-level stable IDs across edits
|
|
114
|
+
- [x] Ollama embedder (`--embedder ollama`)
|
|
115
|
+
- [ ] pgvector and Qdrant destinations
|
|
116
|
+
- [ ] Notion and sitemap sources
|
|
117
|
+
|
|
118
|
+
## License
|
|
119
|
+
|
|
120
|
+
MIT
|
|
121
|
+
|
|
122
|
+
## Known limitations (v0.4)
|
|
123
|
+
|
|
124
|
+
- Hash embeddings are not semantic — use `--embedder ollama` for local semantic vectors
|
|
125
|
+
- JSONL is not a vector DB
|
|
126
|
+
- Local markdown files only
|
|
127
|
+
- Re-runs reuse `.embedsync/state.db`; pass `--state-db` for an isolated plan
|
|
128
|
+
- Ollama must already be running and have the embedding model pulled
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
# embedsync
|
|
2
|
+
|
|
3
|
+
Incremental synchronization between **source documents** and **vector indexes** — detect changes, re-embed only deltas, and delete stale chunks.
|
|
4
|
+
|
|
5
|
+
[](LICENSE)
|
|
6
|
+
[](https://www.python.org/downloads/)
|
|
7
|
+
[](https://github.com/yashshah9/embedsync/actions/workflows/ci.yml)
|
|
8
|
+
|
|
9
|
+
> **Status:** v0.4 — hash + Ollama embedders, paragraph chunks, JSONL destination, chunk-level re-embed.
|
|
10
|
+
|
|
11
|
+
## 60-second try
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
docker compose run --rm plan # plan sync for examples/docs
|
|
15
|
+
docker compose run --rm test # pytest
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## Why this vs alternatives
|
|
19
|
+
|
|
20
|
+
| Approach | Strength | Gap |
|
|
21
|
+
|----------|----------|-----|
|
|
22
|
+
| **embedsync** | Content-hash deltas + pluggable embedders | Destinations still local (JSONL/memory) |
|
|
23
|
+
| Full re-embed pipelines | Simple mentally | Expensive; misses deletes |
|
|
24
|
+
| Framework ingestion (e.g. LlamaIndex) | Rich connectors | Change detection is DIY |
|
|
25
|
+
| One-off sync scripts | Fits one repo | No shared plan/state model |
|
|
26
|
+
|
|
27
|
+
## Problem
|
|
28
|
+
|
|
29
|
+
RAG indexes rot when documents change. Full re-embeds are expensive and miss deletes. Every team rebuilds change detection from scratch.
|
|
30
|
+
|
|
31
|
+
## Key features (v0.4)
|
|
32
|
+
|
|
33
|
+
- Content-hash change detection per document
|
|
34
|
+
- Sync plan: add / update / delete actions
|
|
35
|
+
- Hash embedder for offline/CI (`--embedder hash`)
|
|
36
|
+
- Ollama embedder (`--embedder ollama` or `ollama:nomic-embed-text`)
|
|
37
|
+
- JSONL or in-memory destination
|
|
38
|
+
- Unchanged docs/chunks skip re-embedding on the next run
|
|
39
|
+
|
|
40
|
+
## Architecture
|
|
41
|
+
|
|
42
|
+
```
|
|
43
|
+
embedsync run ./docs
|
|
44
|
+
├── LocalFileSource
|
|
45
|
+
├── StateStore (SQLite)
|
|
46
|
+
├── plan_sync() → diff
|
|
47
|
+
└── Destination (Memory → pgvector next)
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
## Installation
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
pip install embedsync
|
|
54
|
+
pip install -e ".[dev]"
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
## Usage
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
embedsync health
|
|
61
|
+
embedsync plan examples/docs --state-db /tmp/embedsync-demo.db
|
|
62
|
+
embedsync run examples/docs --dry-run --state-db /tmp/embedsync-demo.db
|
|
63
|
+
embedsync run examples/docs --embedder hash --destination memory --state-db /tmp/embedsync-demo.db
|
|
64
|
+
embedsync run examples/docs --embedder hash --destination jsonl:/tmp/index.jsonl
|
|
65
|
+
# Requires a running Ollama with an embedding model:
|
|
66
|
+
embedsync run examples/docs --embedder ollama --destination jsonl:/tmp/index.jsonl
|
|
67
|
+
embedsync run examples/docs --embedder ollama:nomic-embed-text --destination memory
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
## Docker
|
|
71
|
+
|
|
72
|
+
```bash
|
|
73
|
+
docker compose run --rm test
|
|
74
|
+
docker compose run --rm plan
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
## Configuration
|
|
78
|
+
|
|
79
|
+
| Variable | Default | Description |
|
|
80
|
+
|----------|---------|-------------|
|
|
81
|
+
| `EMBEDSYNC_STATE_DB` | `.embedsync/state.db` | State database path |
|
|
82
|
+
| `EMBEDSYNC_LOG_LEVEL` | `INFO` | Log level |
|
|
83
|
+
|
|
84
|
+
Ollama uses `OLLAMA_HOST` when set (otherwise the embedder default host).
|
|
85
|
+
|
|
86
|
+
## Roadmap
|
|
87
|
+
|
|
88
|
+
- [x] Pluggable embedder protocol + hash backend
|
|
89
|
+
- [x] JSONL destination (local stand-in)
|
|
90
|
+
- [x] Chunk-level stable IDs across edits
|
|
91
|
+
- [x] Ollama embedder (`--embedder ollama`)
|
|
92
|
+
- [ ] pgvector and Qdrant destinations
|
|
93
|
+
- [ ] Notion and sitemap sources
|
|
94
|
+
|
|
95
|
+
## License
|
|
96
|
+
|
|
97
|
+
MIT
|
|
98
|
+
|
|
99
|
+
## Known limitations (v0.4)
|
|
100
|
+
|
|
101
|
+
- Hash embeddings are not semantic — use `--embedder ollama` for local semantic vectors
|
|
102
|
+
- JSONL is not a vector DB
|
|
103
|
+
- Local markdown files only
|
|
104
|
+
- Re-runs reuse `.embedsync/state.db`; pass `--state-db` for an isolated plan
|
|
105
|
+
- Ollama must already be running and have the embedding model pulled
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# Security Policy
|
|
2
|
+
|
|
3
|
+
## Reporting a vulnerability
|
|
4
|
+
|
|
5
|
+
Email **yash376351@gmail.com** with the repo name, a short description, and steps to reproduce. Please do not open a public issue for exploitable findings until we have had a reasonable chance to respond.
|
|
6
|
+
|
|
7
|
+
## Threat model (honest)
|
|
8
|
+
|
|
9
|
+
embedsync reads local documents, stores sync state in SQLite, and may call a local Ollama (or other configured) embedder.
|
|
10
|
+
|
|
11
|
+
- It is **not** a vector database security boundary.
|
|
12
|
+
- Document content is sent to the embedder you select (`hash` stays local; `ollama` hits your Ollama host).
|
|
13
|
+
- State DBs and JSONL destinations may contain document text and embeddings — treat them as sensitive if the source corpus is.
|
|
14
|
+
- There is no multi-tenant isolation; run one sync workspace per trust domain.
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
services:
|
|
2
|
+
test:
|
|
3
|
+
build: {context: ., dockerfile: docker/Dockerfile}
|
|
4
|
+
volumes: [".:/app"]
|
|
5
|
+
working_dir: /app
|
|
6
|
+
command: ["pytest", "tests/", "-v"]
|
|
7
|
+
plan:
|
|
8
|
+
build: {context: ., dockerfile: docker/Dockerfile}
|
|
9
|
+
volumes: [".:/app"]
|
|
10
|
+
working_dir: /app
|
|
11
|
+
command: ["embedsync", "plan", "examples/docs", "--state-db", "/tmp/embedsync-demo.db"]
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
# embedsync — Planning Documents
|
|
2
|
+
|
|
3
|
+
| Document | Status | Description |
|
|
4
|
+
|----------|--------|-------------|
|
|
5
|
+
| [v0.1-completed-foundation.md](./v0.1-completed-foundation.md) | **Completed** | Initial foundation release |
|
|
6
|
+
| [v0.2-next-phase-roadmap.md](./v0.2-next-phase-roadmap.md) | **In Progress** | v0.2.0 shipped hash embedder + JSONL; pgvector still open |
|
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
# v0.1 Completed Foundation Plan — embedsync
|
|
2
|
+
|
|
3
|
+
**Status:** Completed
|
|
4
|
+
**Version:** 0.1.0
|
|
5
|
+
**Completed:** 2026-08-18
|
|
6
|
+
**Owner:** Yash Shah
|
|
7
|
+
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
## 1. Executive Summary
|
|
11
|
+
|
|
12
|
+
Deliver a **minimal viable open-source foundation** for incremental document-to-vector-index synchronization. v0.1 focuses on content-hash change detection, add/update/delete sync planning, SQLite state persistence, local markdown sources, and an in-memory destination with dry-run CLI — zero embedding API calls, zero vector DB infrastructure.
|
|
13
|
+
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
## 2. Problem Statement
|
|
17
|
+
|
|
18
|
+
- RAG indexes rot when source documents change; full re-embeds are expensive and miss deletes
|
|
19
|
+
- Every team rebuilds change detection, state tracking, and delta sync from scratch
|
|
20
|
+
- Existing tools either full-reindex or require heavyweight orchestration
|
|
21
|
+
- No standard CLI for "diff documents against last sync state and plan vector index mutations"
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
## 3. Goals (v0.1)
|
|
26
|
+
|
|
27
|
+
| # | Goal | Status |
|
|
28
|
+
|---|------|--------|
|
|
29
|
+
| G1 | Provide CLI installable Python package | Done |
|
|
30
|
+
| G2 | Content-hash change detection per document | Done |
|
|
31
|
+
| G3 | Sync plan: add / update / delete actions | Done |
|
|
32
|
+
| G4 | SQLite state store (no extra infrastructure) | Done |
|
|
33
|
+
| G5 | Local markdown file source | Done |
|
|
34
|
+
| G6 | In-memory destination for testing and dry-run | Done |
|
|
35
|
+
| G7 | `--dry-run` mode on `run` command | Done |
|
|
36
|
+
| G8 | Docker-based dev and test environment | Done |
|
|
37
|
+
| G9 | Contributor-ready repo (README, CI, LICENSE) | Done |
|
|
38
|
+
|
|
39
|
+
## 4. Non-Goals (v0.1)
|
|
40
|
+
|
|
41
|
+
- Real vector database destinations (pgvector, Qdrant)
|
|
42
|
+
- Embedding API calls or pluggable embedders
|
|
43
|
+
- Chunk-level stable IDs across document edits
|
|
44
|
+
- Notion, sitemap, or remote document sources
|
|
45
|
+
- Chunk-level diff (document-level hash only)
|
|
46
|
+
- PyPI publish (deferred to v0.2)
|
|
47
|
+
- Web UI or hosted sync dashboard
|
|
48
|
+
|
|
49
|
+
---
|
|
50
|
+
|
|
51
|
+
## 5. Architecture Delivered
|
|
52
|
+
|
|
53
|
+
```
|
|
54
|
+
CLI (Click + Rich)
|
|
55
|
+
├── health → version + sanity check
|
|
56
|
+
├── plan <source_dir> → plan_sync() → Rich table (dry)
|
|
57
|
+
└── run <source_dir> → execute_sync() with --dry-run flag
|
|
58
|
+
|
|
59
|
+
Sync Engine
|
|
60
|
+
├── plan_sync() → diff source vs state → SyncPlan
|
|
61
|
+
└── execute_sync() → apply actions to destination + update state
|
|
62
|
+
|
|
63
|
+
Sources
|
|
64
|
+
└── LocalFileSource → glob **/*.md, relative path as doc_id
|
|
65
|
+
|
|
66
|
+
State
|
|
67
|
+
└── StateStore (SQLite) → documents(doc_id, content_hash, chunk_count)
|
|
68
|
+
|
|
69
|
+
Destinations
|
|
70
|
+
└── MemoryDestination → in-memory dict for testing/dry-run
|
|
71
|
+
|
|
72
|
+
Config
|
|
73
|
+
└── Settings (pydantic-settings) → EMBEDSYNC_* env vars
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
### Sync Flow
|
|
77
|
+
|
|
78
|
+
```
|
|
79
|
+
LocalFileSource.list_documents()
|
|
80
|
+
└── for each doc:
|
|
81
|
+
├── content_hash(SHA-256) vs StateStore.get()
|
|
82
|
+
├── new doc → ADD
|
|
83
|
+
├── hash changed → UPDATE
|
|
84
|
+
└── missing in source → DELETE (from state)
|
|
85
|
+
└── SyncPlan → Destination.apply() → StateStore upsert/delete
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
### Technology Choices
|
|
89
|
+
|
|
90
|
+
| Layer | Choice | Rationale |
|
|
91
|
+
|-------|--------|-----------|
|
|
92
|
+
| Language | Python 3.11+ | ML/RAG ecosystem familiarity |
|
|
93
|
+
| CLI | Click | Mature, composable subcommands |
|
|
94
|
+
| Terminal UX | Rich | Colored ADD/UPDATE/DELETE plan table |
|
|
95
|
+
| State | SQLite (stdlib) | Zero infra; portable single file |
|
|
96
|
+
| Hashing | SHA-256 (stdlib) | Fast, deterministic content fingerprint |
|
|
97
|
+
| Config | pydantic-settings | Env-based settings with `.env` support |
|
|
98
|
+
| Logging | structlog | Structured logs per sync action |
|
|
99
|
+
| Packaging | hatchling | Modern pyproject.toml builds |
|
|
100
|
+
| Quality | ruff, mypy, pytest | Standard Python OSS toolchain |
|
|
101
|
+
|
|
102
|
+
---
|
|
103
|
+
|
|
104
|
+
## 6. Deliverables Completed
|
|
105
|
+
|
|
106
|
+
### 6.1 Source Code
|
|
107
|
+
|
|
108
|
+
- [x] `src/embedsync/cli.py` — `health`, `plan`, `run` commands
|
|
109
|
+
- [x] `src/embedsync/sync/engine.py` — `plan_sync()`, `execute_sync()`, `SyncPlan`
|
|
110
|
+
- [x] `src/embedsync/state/store.py` — `StateStore`, `DocumentState`, `content_hash()`
|
|
111
|
+
- [x] `src/embedsync/sources/local.py` — `LocalFileSource`, `SourceDocument`
|
|
112
|
+
- [x] `src/embedsync/destinations/memory.py` — `MemoryDestination`, `SyncAction`
|
|
113
|
+
- [x] `src/embedsync/config.py` — `Settings` with env prefix
|
|
114
|
+
- [x] `src/embedsync/__init__.py` — package version
|
|
115
|
+
|
|
116
|
+
### 6.2 Sync Actions
|
|
117
|
+
|
|
118
|
+
| Action | Trigger | Destination Effect | State Effect |
|
|
119
|
+
|--------|---------|-------------------|--------------|
|
|
120
|
+
| ADD | doc_id not in state | Index doc (chunk count recorded) | upsert hash + chunk_count |
|
|
121
|
+
| UPDATE | content_hash changed | Re-index doc | upsert new hash |
|
|
122
|
+
| DELETE | doc_id in state but not in source | Remove from index | delete row |
|
|
123
|
+
|
|
124
|
+
### 6.3 State Schema (SQLite)
|
|
125
|
+
|
|
126
|
+
| Column | Type | Description |
|
|
127
|
+
|--------|------|-------------|
|
|
128
|
+
| `doc_id` | TEXT PRIMARY KEY | Relative path from source root |
|
|
129
|
+
| `content_hash` | TEXT NOT NULL | SHA-256 hex digest of document content |
|
|
130
|
+
| `chunk_count` | INTEGER | Heuristic chunk count (not real embedding yet) |
|
|
131
|
+
|
|
132
|
+
### 6.4 Chunk Heuristic (v0.1)
|
|
133
|
+
|
|
134
|
+
| Parameter | Value | Notes |
|
|
135
|
+
|-----------|-------|-------|
|
|
136
|
+
| Chunk size | 500 chars | Fixed heuristic; `ponytail:` comment in engine |
|
|
137
|
+
| Formula | `max(1, len(content) // 500)` | Placeholder until real chunker in v0.2 |
|
|
138
|
+
|
|
139
|
+
### 6.5 Examples & Fixtures
|
|
140
|
+
|
|
141
|
+
- [x] `examples/docs/getting-started.md` — sample document
|
|
142
|
+
- [x] `examples/docs/api.md` — sample document
|
|
143
|
+
|
|
144
|
+
### 6.6 Tests
|
|
145
|
+
|
|
146
|
+
- [x] `tests/test_engine.py` — plan detects 2 new docs; run indexes 2 docs
|
|
147
|
+
- [x] All tests passing in Docker (Python 3.12)
|
|
148
|
+
|
|
149
|
+
### 6.7 DevOps & Documentation
|
|
150
|
+
|
|
151
|
+
- [x] `docker/Dockerfile` — multi-stage dev/runtime
|
|
152
|
+
- [x] `compose.yaml` — `test` and `plan` services
|
|
153
|
+
- [x] `.github/workflows/ci.yml` — ruff, mypy, pytest
|
|
154
|
+
- [x] `README.md`, `CHANGELOG.md`, `LICENSE`
|
|
155
|
+
- [x] `pyproject.toml` — hatchling build, `[project.scripts]` entry point
|
|
156
|
+
|
|
157
|
+
---
|
|
158
|
+
|
|
159
|
+
## 7. CLI Reference (v0.1)
|
|
160
|
+
|
|
161
|
+
| Command | Description | Key Options |
|
|
162
|
+
|---------|-------------|-------------|
|
|
163
|
+
| `embedsync health` | Print version and OK status | — |
|
|
164
|
+
| `embedsync plan <dir>` | Show ADD/UPDATE/DELETE plan without applying | `--state-db` |
|
|
165
|
+
| `embedsync run <dir>` | Execute sync plan against destination | `--dry-run`, `--state-db` |
|
|
166
|
+
|
|
167
|
+
### Configuration
|
|
168
|
+
|
|
169
|
+
| Variable | Default | Description |
|
|
170
|
+
|----------|---------|-------------|
|
|
171
|
+
| `EMBEDSYNC_STATE_DB` | `.embedsync/state.db` | SQLite state database path |
|
|
172
|
+
| `EMBEDSYNC_LOG_LEVEL` | `INFO` | Log level |
|
|
173
|
+
| `EMBEDSYNC_DRY_RUN` | `false` | Default dry-run (CLI flag overrides) |
|
|
174
|
+
|
|
175
|
+
---
|
|
176
|
+
|
|
177
|
+
## 8. Acceptance Criteria (Met)
|
|
178
|
+
|
|
179
|
+
| # | Criterion | Verification |
|
|
180
|
+
|---|-----------|--------------|
|
|
181
|
+
| AC1 | `embedsync plan examples/docs` shows 2 ADD actions on first run | Manual + Docker `plan` |
|
|
182
|
+
| AC2 | `embedsync run examples/docs` indexes 2 documents in MemoryDestination | test_engine.py |
|
|
183
|
+
| AC3 | Second run with no changes produces 0 actions | Manual |
|
|
184
|
+
| AC4 | Content edit triggers UPDATE on subsequent plan | Manual |
|
|
185
|
+
| AC5 | Deleted file triggers DELETE on subsequent plan | Manual |
|
|
186
|
+
| AC6 | `--dry-run` applies no state or destination mutations | Manual |
|
|
187
|
+
| AC7 | `docker compose run --rm test` passes all tests | CI-ready |
|
|
188
|
+
| AC8 | No secrets or credentials in repository | .gitignore |
|
|
189
|
+
|
|
190
|
+
---
|
|
191
|
+
|
|
192
|
+
## 9. Known Limitations (v0.1)
|
|
193
|
+
|
|
194
|
+
- Local markdown files only (`**/*.md` glob)
|
|
195
|
+
- In-memory destination — no real vector DB writes
|
|
196
|
+
- Document-level hash only — any edit re-indexes entire document
|
|
197
|
+
- Naive fixed-size chunk count heuristic — no real chunking or embedding
|
|
198
|
+
- No embedding API calls — chunk_count is a placeholder integer
|
|
199
|
+
- No concurrent sync or locking — single-process CLI only
|
|
200
|
+
- No Notion, sitemap, or HTTP sources
|
|
201
|
+
|
|
202
|
+
---
|
|
203
|
+
|
|
204
|
+
## 10. Metrics at Completion
|
|
205
|
+
|
|
206
|
+
| Metric | Value |
|
|
207
|
+
|--------|-------|
|
|
208
|
+
| Source files | 7 Python modules |
|
|
209
|
+
| Test cases | 2 |
|
|
210
|
+
| CLI commands | 3 (`health`, `plan`, `run`) |
|
|
211
|
+
| Sync action types | 3 (add, update, delete) |
|
|
212
|
+
| Source types | 1 (local markdown) |
|
|
213
|
+
| Destination types | 1 (in-memory) |
|
|
214
|
+
| Docker services | 2 (`test`, `plan`) |
|
|
215
|
+
|
|
216
|
+
---
|
|
217
|
+
|
|
218
|
+
## 11. Sign-Off
|
|
219
|
+
|
|
220
|
+
v0.1 foundation is **complete and shippable** as an alpha OSS project. The sync engine, SQLite state, and dry-run CLI provide immediate value for planning document-to-index deltas without embedding cost. pgvector/Qdrant destinations, pluggable embedders, stable chunk IDs, and remote sources are deferred to v0.2 per roadmap.
|