garage-rag 1.5.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.
- garage_rag-1.5.0/.gitignore +36 -0
- garage_rag-1.5.0/LICENSE +21 -0
- garage_rag-1.5.0/PKG-INFO +158 -0
- garage_rag-1.5.0/README.md +82 -0
- garage_rag-1.5.0/hatch_build.py +36 -0
- garage_rag-1.5.0/pyproject.toml +145 -0
- garage_rag-1.5.0/src/garage_rag/__init__.py +15 -0
- garage_rag-1.5.0/src/garage_rag/_data/models/models.json +681 -0
- garage_rag-1.5.0/src/garage_rag/_data/sql/001_extensions.sql +18 -0
- garage_rag-1.5.0/src/garage_rag/_data/sql/002_types.sql +43 -0
- garage_rag-1.5.0/src/garage_rag/_data/sql/003_core.sql +163 -0
- garage_rag-1.5.0/src/garage_rag/_data/sql/004_registry.sql +57 -0
- garage_rag-1.5.0/src/garage_rag/_data/sql/005_conversations.sql +61 -0
- garage_rag-1.5.0/src/garage_rag/_data/sql/006_facts.sql +33 -0
- garage_rag-1.5.0/src/garage_rag/_data/sql/007_chunk_fact_link.sql +17 -0
- garage_rag-1.5.0/src/garage_rag/_data/sql/008_source_scan.sql +29 -0
- garage_rag-1.5.0/src/garage_rag/_data/sql/009_model_distance.sql +24 -0
- garage_rag-1.5.0/src/garage_rag/_data/sql/010_drop_cloud_enrichment.sql +10 -0
- garage_rag-1.5.0/src/garage_rag/_data/sql/011_drop_placeholder_documents.sql +16 -0
- garage_rag-1.5.0/src/garage_rag/_data/sql/012_ingest_outcomes.sql +23 -0
- garage_rag-1.5.0/src/garage_rag/_data/sql/013_fact_prompts.sql +36 -0
- garage_rag-1.5.0/src/garage_rag/_data/sql/014_chunk_direction.sql +30 -0
- garage_rag-1.5.0/src/garage_rag/attribute/__init__.py +1 -0
- garage_rag-1.5.0/src/garage_rag/attribute/git.py +232 -0
- garage_rag-1.5.0/src/garage_rag/attribute/pathrules.py +122 -0
- garage_rag-1.5.0/src/garage_rag/attribute/resolver.py +403 -0
- garage_rag-1.5.0/src/garage_rag/cli.py +1838 -0
- garage_rag-1.5.0/src/garage_rag/config/__init__.py +1012 -0
- garage_rag-1.5.0/src/garage_rag/config/fact_prompts.py +252 -0
- garage_rag-1.5.0/src/garage_rag/db/__init__.py +3 -0
- garage_rag-1.5.0/src/garage_rag/db/catalog.py +81 -0
- garage_rag-1.5.0/src/garage_rag/db/emb_tables.py +223 -0
- garage_rag-1.5.0/src/garage_rag/db/engine.py +115 -0
- garage_rag-1.5.0/src/garage_rag/db/migrate.py +258 -0
- garage_rag-1.5.0/src/garage_rag/db/models.py +386 -0
- garage_rag-1.5.0/src/garage_rag/db/registry.py +188 -0
- garage_rag-1.5.0/src/garage_rag/embed/__init__.py +19 -0
- garage_rag-1.5.0/src/garage_rag/embed/base.py +62 -0
- garage_rag-1.5.0/src/garage_rag/embed/factory.py +56 -0
- garage_rag-1.5.0/src/garage_rag/embed/llama_xpc.py +34 -0
- garage_rag-1.5.0/src/garage_rag/embed/lmstudio.py +49 -0
- garage_rag-1.5.0/src/garage_rag/embed/ollama.py +224 -0
- garage_rag-1.5.0/src/garage_rag/embed/xpc.py +111 -0
- garage_rag-1.5.0/src/garage_rag/enrich/__init__.py +1 -0
- garage_rag-1.5.0/src/garage_rag/enrich/facts.py +392 -0
- garage_rag-1.5.0/src/garage_rag/enrich/generation.py +192 -0
- garage_rag-1.5.0/src/garage_rag/enrich/langextract/LICENSE +202 -0
- garage_rag-1.5.0/src/garage_rag/enrich/langextract/__init__.py +34 -0
- garage_rag-1.5.0/src/garage_rag/enrich/langextract/annotation.py +606 -0
- garage_rag-1.5.0/src/garage_rag/enrich/langextract/base_model.py +231 -0
- garage_rag-1.5.0/src/garage_rag/enrich/langextract/chunking.py +511 -0
- garage_rag-1.5.0/src/garage_rag/enrich/langextract/data.py +270 -0
- garage_rag-1.5.0/src/garage_rag/enrich/langextract/exceptions.py +168 -0
- garage_rag-1.5.0/src/garage_rag/enrich/langextract/extraction.py +194 -0
- garage_rag-1.5.0/src/garage_rag/enrich/langextract/format_handler.py +477 -0
- garage_rag-1.5.0/src/garage_rag/enrich/langextract/prompting.py +280 -0
- garage_rag-1.5.0/src/garage_rag/enrich/langextract/resolver.py +1408 -0
- garage_rag-1.5.0/src/garage_rag/enrich/langextract/schema.py +164 -0
- garage_rag-1.5.0/src/garage_rag/enrich/langextract/tokenizer.py +646 -0
- garage_rag-1.5.0/src/garage_rag/enrich/langextract/types.py +81 -0
- garage_rag-1.5.0/src/garage_rag/enrich/local_provider.py +82 -0
- garage_rag-1.5.0/src/garage_rag/extract/__init__.py +1 -0
- garage_rag-1.5.0/src/garage_rag/extract/base.py +195 -0
- garage_rag-1.5.0/src/garage_rag/extract/contact_names.py +113 -0
- garage_rag-1.5.0/src/garage_rag/extract/dispatch.py +340 -0
- garage_rag-1.5.0/src/garage_rag/extract/image.py +114 -0
- garage_rag-1.5.0/src/garage_rag/extract/imageio.py +207 -0
- garage_rag-1.5.0/src/garage_rag/extract/mail.py +187 -0
- garage_rag-1.5.0/src/garage_rag/extract/messages.py +436 -0
- garage_rag-1.5.0/src/garage_rag/extract/nicknames.py +162 -0
- garage_rag-1.5.0/src/garage_rag/extract/office.py +212 -0
- garage_rag-1.5.0/src/garage_rag/extract/pdf.py +195 -0
- garage_rag-1.5.0/src/garage_rag/extract/placeholder.py +180 -0
- garage_rag-1.5.0/src/garage_rag/extract/quality.py +153 -0
- garage_rag-1.5.0/src/garage_rag/extract/tesseract.py +207 -0
- garage_rag-1.5.0/src/garage_rag/extract/text.py +162 -0
- garage_rag-1.5.0/src/garage_rag/inference/__init__.py +43 -0
- garage_rag-1.5.0/src/garage_rag/inference/bridge.py +91 -0
- garage_rag-1.5.0/src/garage_rag/inference/client.py +641 -0
- garage_rag-1.5.0/src/garage_rag/inference/transport.py +128 -0
- garage_rag-1.5.0/src/garage_rag/ingest/__init__.py +451 -0
- garage_rag-1.5.0/src/garage_rag/ingest/author_names.py +34 -0
- garage_rag-1.5.0/src/garage_rag/ingest/chunking.py +273 -0
- garage_rag-1.5.0/src/garage_rag/ingest/classify.py +109 -0
- garage_rag-1.5.0/src/garage_rag/ingest/conversations.py +388 -0
- garage_rag-1.5.0/src/garage_rag/ingest/gateway.py +1050 -0
- garage_rag-1.5.0/src/garage_rag/ingest/materialize.py +277 -0
- garage_rag-1.5.0/src/garage_rag/ingest/pipeline.py +651 -0
- garage_rag-1.5.0/src/garage_rag/ingest/reconcile.py +116 -0
- garage_rag-1.5.0/src/garage_rag/ingest/scanner.py +652 -0
- garage_rag-1.5.0/src/garage_rag/ingest/splitters.py +354 -0
- garage_rag-1.5.0/src/garage_rag/ingest/walker.py +292 -0
- garage_rag-1.5.0/src/garage_rag/libpq.py +50 -0
- garage_rag-1.5.0/src/garage_rag/mcp_server/__init__.py +1 -0
- garage_rag-1.5.0/src/garage_rag/mcp_server/agent.py +603 -0
- garage_rag-1.5.0/src/garage_rag/mcp_server/install.py +449 -0
- garage_rag-1.5.0/src/garage_rag/mcp_server/server.py +1060 -0
- garage_rag-1.5.0/src/garage_rag/native/__init__.py +38 -0
- garage_rag-1.5.0/src/garage_rag/net/__init__.py +1 -0
- garage_rag-1.5.0/src/garage_rag/net/egress.py +238 -0
- garage_rag-1.5.0/src/garage_rag/ops/__init__.py +8 -0
- garage_rag-1.5.0/src/garage_rag/ops/backfill.py +137 -0
- garage_rag-1.5.0/src/garage_rag/ops/facts.py +362 -0
- garage_rag-1.5.0/src/garage_rag/ops/mcp.py +210 -0
- garage_rag-1.5.0/src/garage_rag/ops/models.py +86 -0
- garage_rag-1.5.0/src/garage_rag/ops/settings.py +34 -0
- garage_rag-1.5.0/src/garage_rag/ops/sources.py +371 -0
- garage_rag-1.5.0/src/garage_rag/proto/__init__.py +9 -0
- garage_rag-1.5.0/src/garage_rag/proto/garage_pb2.py +244 -0
- garage_rag-1.5.0/src/garage_rag/proto/garage_pb2_grpc.py +1680 -0
- garage_rag-1.5.0/src/garage_rag/search/__init__.py +13 -0
- garage_rag-1.5.0/src/garage_rag/search/hybrid.py +397 -0
- garage_rag-1.5.0/src/garage_rag/service/__init__.py +15 -0
- garage_rag-1.5.0/src/garage_rag/service/auth.py +74 -0
- garage_rag-1.5.0/src/garage_rag/service/client.py +389 -0
- garage_rag-1.5.0/src/garage_rag/service/server.py +1666 -0
- garage_rag-1.5.0/src/garage_rag/xpc/__init__.py +15 -0
- garage_rag-1.5.0/src/garage_rag/xpc/host.py +167 -0
- garage_rag-1.5.0/src/garage_rag/xpc/llama_xpc.py +174 -0
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
*.pyc
|
|
2
|
+
.venv
|
|
3
|
+
.env
|
|
4
|
+
*.db
|
|
5
|
+
.build
|
|
6
|
+
macapp/build/
|
|
7
|
+
# Credential files (an LM Studio token, or a key left over from the retired cloud OCR)
|
|
8
|
+
*.key
|
|
9
|
+
anthropic.key
|
|
10
|
+
|
|
11
|
+
# Per-project config override (holds absolute paths and identity)
|
|
12
|
+
garage.json
|
|
13
|
+
|
|
14
|
+
*.bak-*
|
|
15
|
+
|
|
16
|
+
__pycache__
|
|
17
|
+
|
|
18
|
+
bazel-*
|
|
19
|
+
|
|
20
|
+
.bazelbsp/
|
|
21
|
+
*.xcodeproj
|
|
22
|
+
.output.txt
|
|
23
|
+
build/
|
|
24
|
+
# IDE data-source definitions embed the local database password
|
|
25
|
+
.idea/dataSources.xml
|
|
26
|
+
.idea/dataSources.local.xml
|
|
27
|
+
|
|
28
|
+
# Local Bazel overrides (see .bazelrc)
|
|
29
|
+
user.bazelrc
|
|
30
|
+
|
|
31
|
+
.ruff_cache/
|
|
32
|
+
.mypy_cache/
|
|
33
|
+
garage_python/dist/
|
|
34
|
+
|
|
35
|
+
# Release archives staged by //macapp/package:publish_appcast
|
|
36
|
+
/dist/
|
garage_rag-1.5.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Rick Mark-Penwell
|
|
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.
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: garage_rag
|
|
3
|
+
Version: 1.5.0
|
|
4
|
+
Summary: Local-first personal RAG pipeline over Postgres + pgvector, with authorship attribution and an MCP server
|
|
5
|
+
Project-URL: Homepage, https://garagerag.app
|
|
6
|
+
Project-URL: Source, https://github.com/rickmark/garage-rag
|
|
7
|
+
Project-URL: Issues, https://github.com/rickmark/garage-rag/issues
|
|
8
|
+
Author: Rick Mark-Penwell
|
|
9
|
+
License: MIT License
|
|
10
|
+
|
|
11
|
+
Copyright (c) 2026 Rick Mark-Penwell
|
|
12
|
+
|
|
13
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
14
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
15
|
+
in the Software without restriction, including without limitation the rights
|
|
16
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
17
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
18
|
+
furnished to do so, subject to the following conditions:
|
|
19
|
+
|
|
20
|
+
The above copyright notice and this permission notice shall be included in all
|
|
21
|
+
copies or substantial portions of the Software.
|
|
22
|
+
|
|
23
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
24
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
25
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
26
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
27
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
28
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
29
|
+
SOFTWARE.
|
|
30
|
+
License-File: LICENSE
|
|
31
|
+
Keywords: embeddings,mcp,pgvector,postgres,rag,search
|
|
32
|
+
Classifier: Development Status :: 4 - Beta
|
|
33
|
+
Classifier: Intended Audience :: End Users/Desktop
|
|
34
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
35
|
+
Classifier: Operating System :: MacOS
|
|
36
|
+
Classifier: Operating System :: POSIX :: Linux
|
|
37
|
+
Classifier: Programming Language :: Python :: 3
|
|
38
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
39
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
40
|
+
Classifier: Topic :: Text Processing :: Indexing
|
|
41
|
+
Requires-Python: <3.15,>=3.13
|
|
42
|
+
Requires-Dist: certifi>=2024.2.2
|
|
43
|
+
Requires-Dist: cryptography>=44
|
|
44
|
+
Requires-Dist: grpcio>=1.68.0
|
|
45
|
+
Requires-Dist: httpx>=0.28
|
|
46
|
+
Requires-Dist: mcp[cli]==2.1.0
|
|
47
|
+
Requires-Dist: openpyxl>=3.1.5
|
|
48
|
+
Requires-Dist: pdfplumber>=0.11.10
|
|
49
|
+
Requires-Dist: pgvector<0.6,>=0.5
|
|
50
|
+
Requires-Dist: pillow>=11.0
|
|
51
|
+
Requires-Dist: protobuf>=5.29.0
|
|
52
|
+
Requires-Dist: psycopg[pool]>=3.2
|
|
53
|
+
Requires-Dist: pydantic>=2.7.4
|
|
54
|
+
Requires-Dist: pypdf>=6.16
|
|
55
|
+
Requires-Dist: pytest>=9.1.1
|
|
56
|
+
Requires-Dist: python-docx>=1.2
|
|
57
|
+
Requires-Dist: python-pptx>=1.0.2
|
|
58
|
+
Requires-Dist: pyyaml>=6.0
|
|
59
|
+
Requires-Dist: regex>=2023.0.0
|
|
60
|
+
Requires-Dist: rich>=13.9
|
|
61
|
+
Requires-Dist: sqlalchemy>=2.0
|
|
62
|
+
Requires-Dist: truststore>=0.10
|
|
63
|
+
Requires-Dist: typer>=0.15
|
|
64
|
+
Requires-Dist: uvicorn>=0.30
|
|
65
|
+
Provides-Extra: build-tools
|
|
66
|
+
Requires-Dist: build; extra == 'build-tools'
|
|
67
|
+
Requires-Dist: setuptools; extra == 'build-tools'
|
|
68
|
+
Provides-Extra: dev
|
|
69
|
+
Requires-Dist: faker>=37; extra == 'dev'
|
|
70
|
+
Requires-Dist: grpcio-tools>=1.68.0; extra == 'dev'
|
|
71
|
+
Requires-Dist: pytest-asyncio>=0.24; extra == 'dev'
|
|
72
|
+
Requires-Dist: ruff>=0.8; extra == 'dev'
|
|
73
|
+
Requires-Dist: tree-sitter-swift>=0.7; extra == 'dev'
|
|
74
|
+
Requires-Dist: tree-sitter>=0.25; extra == 'dev'
|
|
75
|
+
Description-Content-Type: text/markdown
|
|
76
|
+
|
|
77
|
+
# garage_rag
|
|
78
|
+
|
|
79
|
+
The Python half of [Garage](https://github.com/rickmark/garage-rag#readme): a local-first personal RAG pipeline over
|
|
80
|
+
PostgreSQL + pgvector. It walks personal documents, code repositories and communications,
|
|
81
|
+
extracts and chunks their text, attributes authorship, embeds every chunk under each
|
|
82
|
+
registered model, distills documents into span-grounded facts, and serves the corpus over
|
|
83
|
+
MCP 2.0 and gRPC. Garage never sends communications off the machine (MCP clients you connect still receive what they retrieve); see [`docs/privacy.md`](https://github.com/rickmark/garage-rag/blob/main/docs/privacy.md).
|
|
84
|
+
|
|
85
|
+
## Entry points
|
|
86
|
+
|
|
87
|
+
| Command | Module | Role |
|
|
88
|
+
|---|---|---|
|
|
89
|
+
| `garage` | `garage_rag.cli:main_cli` | ingest, backfill, enrich-facts, search, config, model registry, `serve` (gRPC bridge for the macOS app) |
|
|
90
|
+
| `garage-mcp` | `garage_rag.mcp_server.server:main` | MCP 2.0 server over stdio, the command MCP clients spawn (`garage mcp-install --stdio` wires it into Claude Desktop/Code); `garage mcp-serve` runs the HTTP server |
|
|
91
|
+
|
|
92
|
+
Both are console scripts of the `garage_rag` package. The macOS app does not
|
|
93
|
+
ship them as standalone binaries: it bundles the package as
|
|
94
|
+
`site-python` inside `Frameworks/PythonXPCService.framework` (`//garage_python:site-packages`) and runs it through
|
|
95
|
+
Swift launchers that embed the bundled interpreter, packaged as helper bundles
|
|
96
|
+
(`Contents/Helpers/garage.app` and `garage-mcp.app`, `//macapp/Sources/GarageCLI:garage_app` and
|
|
97
|
+
`//macapp/Sources/GarageMCPCLI:garage_mcp_app`) and reached through the stable paths
|
|
98
|
+
`Contents/MacOS/garage` and `garage-mcp`. Those launchers find or start the app's database and
|
|
99
|
+
export `GARAGE_DATABASE_URL`; the console scripts from a venv do not.
|
|
100
|
+
|
|
101
|
+
## Layout
|
|
102
|
+
|
|
103
|
+
```
|
|
104
|
+
src/garage_rag/
|
|
105
|
+
ingest/ walker, materialization budget, chunking, scanner, pipeline, Messages threads,
|
|
106
|
+
the storage gateway (in-process SQLAlchemy, or gRPC for the XPC workers)
|
|
107
|
+
extract/ per-format extractors (text, PDF, Office, images via in-process Tesseract, mail) + quality gate
|
|
108
|
+
attribute/ git / metadata / path-rule / message-sender authorship signals
|
|
109
|
+
embed/ embedding backends (Ollama, LM Studio, llama XPC) and backfill
|
|
110
|
+
enrich/ fact distillation (vendored local LangExtract subset) and local generation
|
|
111
|
+
inference/ the one HTTP client for LM Studio, Ollama and the app's LlamaXPCService
|
|
112
|
+
net/ egress.py, the only module that opens outbound connections
|
|
113
|
+
search/ hybrid RRF search
|
|
114
|
+
mcp_server/ MCP tools and client registration (mcp-install)
|
|
115
|
+
service/ gRPC GarageService (proto/garage.proto) and its client
|
|
116
|
+
ops/ the operations the CLI and the gRPC handlers both present
|
|
117
|
+
xpc/ hooks for the app's XPC hosts (llama_xpc client, model loader)
|
|
118
|
+
native/ finds libpq / libtesseract already loaded into the process
|
|
119
|
+
db/ SQLAlchemy models mirroring data/sql/00*.sql, migrations, model registry
|
|
120
|
+
config/ garage.json settings and the generated JSON Schema
|
|
121
|
+
tests/ pytest suite, one Bazel py_test target per file
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
## Building and testing
|
|
125
|
+
|
|
126
|
+
Everything is driven by the Aspect CLI from the repository root:
|
|
127
|
+
|
|
128
|
+
```bash
|
|
129
|
+
aspect build //garage_python:site-packages
|
|
130
|
+
aspect test //garage_python/tests:suite
|
|
131
|
+
aspect test //garage_python/tests:test_facts --bazel-flag=--test_arg=-k --bazel-flag=--test_arg=some_case
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
`tests/BUILD.bazel` has one `py_test` per file (via `//tools/pytest:defs.bzl`, which drives
|
|
135
|
+
pytest); `aspect gazelle` writes the target for a new test file, and the `suite` list is kept by
|
|
136
|
+
hand. Third-party
|
|
137
|
+
dependencies live in `pyproject.toml` and are locked with `uv` (`../tools/repin`), exposed to
|
|
138
|
+
Bazel as `@pypi//<package>`.
|
|
139
|
+
|
|
140
|
+
For quick iteration outside Bazel, use the `uv`-managed virtualenv:
|
|
141
|
+
|
|
142
|
+
```bash
|
|
143
|
+
cd garage_python
|
|
144
|
+
uv sync --extra dev
|
|
145
|
+
uv run pytest -q
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
The lockfile resolves for macOS only; on Linux use
|
|
149
|
+
`uv venv --python 3.14 .venv && uv pip install -e '.[dev]'`, then `.venv/bin/pytest -q`.
|
|
150
|
+
`tests/test_postgres.py` runs against a real server when `GARAGE_TEST_DATABASE_URL` is set (see
|
|
151
|
+
"Testing against Postgres" in `../CLAUDE.md`).
|
|
152
|
+
|
|
153
|
+
## Documentation
|
|
154
|
+
|
|
155
|
+
`../docs/` — [architecture](https://github.com/rickmark/garage-rag/blob/main/docs/architecture.md), [schema](https://github.com/rickmark/garage-rag/blob/main/docs/schema.md),
|
|
156
|
+
[attribution](https://github.com/rickmark/garage-rag/blob/main/docs/attribution.md), [privacy](https://github.com/rickmark/garage-rag/blob/main/docs/privacy.md), plus support and
|
|
157
|
+
troubleshooting guides. The generated config schema is committed at
|
|
158
|
+
`../docs/.data/garage.schema.json` and served as `https://garagerag.app/.data/garage.schema.json`.
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
# garage_rag
|
|
2
|
+
|
|
3
|
+
The Python half of [Garage](https://github.com/rickmark/garage-rag#readme): a local-first personal RAG pipeline over
|
|
4
|
+
PostgreSQL + pgvector. It walks personal documents, code repositories and communications,
|
|
5
|
+
extracts and chunks their text, attributes authorship, embeds every chunk under each
|
|
6
|
+
registered model, distills documents into span-grounded facts, and serves the corpus over
|
|
7
|
+
MCP 2.0 and gRPC. Garage never sends communications off the machine (MCP clients you connect still receive what they retrieve); see [`docs/privacy.md`](https://github.com/rickmark/garage-rag/blob/main/docs/privacy.md).
|
|
8
|
+
|
|
9
|
+
## Entry points
|
|
10
|
+
|
|
11
|
+
| Command | Module | Role |
|
|
12
|
+
|---|---|---|
|
|
13
|
+
| `garage` | `garage_rag.cli:main_cli` | ingest, backfill, enrich-facts, search, config, model registry, `serve` (gRPC bridge for the macOS app) |
|
|
14
|
+
| `garage-mcp` | `garage_rag.mcp_server.server:main` | MCP 2.0 server over stdio, the command MCP clients spawn (`garage mcp-install --stdio` wires it into Claude Desktop/Code); `garage mcp-serve` runs the HTTP server |
|
|
15
|
+
|
|
16
|
+
Both are console scripts of the `garage_rag` package. The macOS app does not
|
|
17
|
+
ship them as standalone binaries: it bundles the package as
|
|
18
|
+
`site-python` inside `Frameworks/PythonXPCService.framework` (`//garage_python:site-packages`) and runs it through
|
|
19
|
+
Swift launchers that embed the bundled interpreter, packaged as helper bundles
|
|
20
|
+
(`Contents/Helpers/garage.app` and `garage-mcp.app`, `//macapp/Sources/GarageCLI:garage_app` and
|
|
21
|
+
`//macapp/Sources/GarageMCPCLI:garage_mcp_app`) and reached through the stable paths
|
|
22
|
+
`Contents/MacOS/garage` and `garage-mcp`. Those launchers find or start the app's database and
|
|
23
|
+
export `GARAGE_DATABASE_URL`; the console scripts from a venv do not.
|
|
24
|
+
|
|
25
|
+
## Layout
|
|
26
|
+
|
|
27
|
+
```
|
|
28
|
+
src/garage_rag/
|
|
29
|
+
ingest/ walker, materialization budget, chunking, scanner, pipeline, Messages threads,
|
|
30
|
+
the storage gateway (in-process SQLAlchemy, or gRPC for the XPC workers)
|
|
31
|
+
extract/ per-format extractors (text, PDF, Office, images via in-process Tesseract, mail) + quality gate
|
|
32
|
+
attribute/ git / metadata / path-rule / message-sender authorship signals
|
|
33
|
+
embed/ embedding backends (Ollama, LM Studio, llama XPC) and backfill
|
|
34
|
+
enrich/ fact distillation (vendored local LangExtract subset) and local generation
|
|
35
|
+
inference/ the one HTTP client for LM Studio, Ollama and the app's LlamaXPCService
|
|
36
|
+
net/ egress.py, the only module that opens outbound connections
|
|
37
|
+
search/ hybrid RRF search
|
|
38
|
+
mcp_server/ MCP tools and client registration (mcp-install)
|
|
39
|
+
service/ gRPC GarageService (proto/garage.proto) and its client
|
|
40
|
+
ops/ the operations the CLI and the gRPC handlers both present
|
|
41
|
+
xpc/ hooks for the app's XPC hosts (llama_xpc client, model loader)
|
|
42
|
+
native/ finds libpq / libtesseract already loaded into the process
|
|
43
|
+
db/ SQLAlchemy models mirroring data/sql/00*.sql, migrations, model registry
|
|
44
|
+
config/ garage.json settings and the generated JSON Schema
|
|
45
|
+
tests/ pytest suite, one Bazel py_test target per file
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
## Building and testing
|
|
49
|
+
|
|
50
|
+
Everything is driven by the Aspect CLI from the repository root:
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
aspect build //garage_python:site-packages
|
|
54
|
+
aspect test //garage_python/tests:suite
|
|
55
|
+
aspect test //garage_python/tests:test_facts --bazel-flag=--test_arg=-k --bazel-flag=--test_arg=some_case
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
`tests/BUILD.bazel` has one `py_test` per file (via `//tools/pytest:defs.bzl`, which drives
|
|
59
|
+
pytest); `aspect gazelle` writes the target for a new test file, and the `suite` list is kept by
|
|
60
|
+
hand. Third-party
|
|
61
|
+
dependencies live in `pyproject.toml` and are locked with `uv` (`../tools/repin`), exposed to
|
|
62
|
+
Bazel as `@pypi//<package>`.
|
|
63
|
+
|
|
64
|
+
For quick iteration outside Bazel, use the `uv`-managed virtualenv:
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
cd garage_python
|
|
68
|
+
uv sync --extra dev
|
|
69
|
+
uv run pytest -q
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
The lockfile resolves for macOS only; on Linux use
|
|
73
|
+
`uv venv --python 3.14 .venv && uv pip install -e '.[dev]'`, then `.venv/bin/pytest -q`.
|
|
74
|
+
`tests/test_postgres.py` runs against a real server when `GARAGE_TEST_DATABASE_URL` is set (see
|
|
75
|
+
"Testing against Postgres" in `../CLAUDE.md`).
|
|
76
|
+
|
|
77
|
+
## Documentation
|
|
78
|
+
|
|
79
|
+
`../docs/` — [architecture](https://github.com/rickmark/garage-rag/blob/main/docs/architecture.md), [schema](https://github.com/rickmark/garage-rag/blob/main/docs/schema.md),
|
|
80
|
+
[attribution](https://github.com/rickmark/garage-rag/blob/main/docs/attribution.md), [privacy](https://github.com/rickmark/garage-rag/blob/main/docs/privacy.md), plus support and
|
|
81
|
+
troubleshooting guides. The generated config schema is committed at
|
|
82
|
+
`../docs/.data/garage.schema.json` and served as `https://garagerag.app/.data/garage.schema.json`.
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
"""Hatch build hook: ship the repository's ``data/`` artifacts inside the package.
|
|
2
|
+
|
|
3
|
+
The pipeline reads the SQL migrations (``data/sql``) and the model catalog
|
|
4
|
+
(``data/models/models.json``) from the repository root, which a wheel installed
|
|
5
|
+
from PyPI does not have. This copies them into the package as
|
|
6
|
+
``garage_rag/_data``, where ``garage_rag.config.data_dir`` looks first.
|
|
7
|
+
|
|
8
|
+
It only acts when ``../data`` exists, that is, when building from the checkout.
|
|
9
|
+
The sdist carries the files under ``src/garage_rag/_data``, so a wheel built
|
|
10
|
+
from the sdist picks them up as ordinary package files.
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
from pathlib import Path
|
|
14
|
+
|
|
15
|
+
from hatchling.builders.hooks.plugin.interface import BuildHookInterface
|
|
16
|
+
|
|
17
|
+
# (source relative to the repository's data/, glob) for each artifact the package needs.
|
|
18
|
+
ARTIFACTS = [("sql", "*.sql"), ("models", "models.json")]
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class DataBuildHook(BuildHookInterface):
|
|
22
|
+
PLUGIN_NAME = "garage-data"
|
|
23
|
+
|
|
24
|
+
def initialize(self, version: str, build_data: dict) -> None:
|
|
25
|
+
if self.target_name == "wheel" and version != "standard":
|
|
26
|
+
return # editable installs read data/ from the checkout
|
|
27
|
+
data = Path(self.root).parent / "data"
|
|
28
|
+
if not data.is_dir():
|
|
29
|
+
return
|
|
30
|
+
prefix = "src/garage_rag/_data" if self.target_name == "sdist" else "garage_rag/_data"
|
|
31
|
+
for subdir, pattern in ARTIFACTS:
|
|
32
|
+
files = sorted((data / subdir).glob(pattern))
|
|
33
|
+
if not files:
|
|
34
|
+
raise FileNotFoundError(f"no {pattern} under {data / subdir}")
|
|
35
|
+
for path in files:
|
|
36
|
+
build_data["force_include"][str(path)] = f"{prefix}/{subdir}/{path.name}"
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "garage_rag"
|
|
3
|
+
version = "1.5.0"
|
|
4
|
+
description = "Local-first personal RAG pipeline over Postgres + pgvector, with authorship attribution and an MCP server"
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
license = { file = "LICENSE" }
|
|
7
|
+
authors = [{ name = "Rick Mark-Penwell" }]
|
|
8
|
+
requires-python = ">=3.13,<3.15"
|
|
9
|
+
keywords = ["rag", "mcp", "pgvector", "postgres", "embeddings", "search"]
|
|
10
|
+
classifiers = [
|
|
11
|
+
"Development Status :: 4 - Beta",
|
|
12
|
+
"Intended Audience :: End Users/Desktop",
|
|
13
|
+
"License :: OSI Approved :: MIT License",
|
|
14
|
+
"Operating System :: MacOS",
|
|
15
|
+
"Operating System :: POSIX :: Linux",
|
|
16
|
+
"Programming Language :: Python :: 3",
|
|
17
|
+
"Programming Language :: Python :: 3.13",
|
|
18
|
+
"Programming Language :: Python :: 3.14",
|
|
19
|
+
"Topic :: Text Processing :: Indexing",
|
|
20
|
+
]
|
|
21
|
+
|
|
22
|
+
dependencies = [
|
|
23
|
+
# storage
|
|
24
|
+
"sqlalchemy>=2.0",
|
|
25
|
+
"psycopg[pool]>=3.2",
|
|
26
|
+
"pgvector>=0.5,<0.6",
|
|
27
|
+
# document extraction (all permissive licenses; PyMuPDF avoided due to AGPL)
|
|
28
|
+
"pypdf>=6.16",
|
|
29
|
+
"pdfplumber>=0.11.10",
|
|
30
|
+
# Not imported directly: pulled in transitively by pdfminer-six (pdfplumber).
|
|
31
|
+
# The old <44 ceiling had no recorded reason and blocked the free-threaded
|
|
32
|
+
# build: 43.x has no cp314t wheels and does not build from source there.
|
|
33
|
+
"cryptography>=44",
|
|
34
|
+
"python-docx>=1.2",
|
|
35
|
+
"python-pptx>=1.0.2",
|
|
36
|
+
"openpyxl>=3.1.5",
|
|
37
|
+
# OCR
|
|
38
|
+
"pillow>=11.0",
|
|
39
|
+
# fact extraction: the vendored LangExtract subset (garage_rag/enrich/langextract)
|
|
40
|
+
# needs regex for its tokenizer.
|
|
41
|
+
"regex>=2023.0.0",
|
|
42
|
+
# the egress guard's HTTP client (garage_rag/net/egress.py), which every
|
|
43
|
+
# outbound HTTP request goes through, garage_rag/inference's included
|
|
44
|
+
"httpx>=0.28",
|
|
45
|
+
# MCP server (2.x renamed FastMCP -> MCPServer; 1.x is incompatible)
|
|
46
|
+
"mcp[cli]==2.1.0",
|
|
47
|
+
# HTTP transport for garage-mcp (mcp_server/server.py imports it directly)
|
|
48
|
+
"uvicorn>=0.30",
|
|
49
|
+
# plumbing
|
|
50
|
+
"typer>=0.15",
|
|
51
|
+
"pydantic>=2.7.4",
|
|
52
|
+
"rich>=13.9",
|
|
53
|
+
"pyyaml>=6.0",
|
|
54
|
+
# A runtime dep on purpose: the Bazel @pypi hub (--@pypi//dep_group=garage_rag)
|
|
55
|
+
# only exposes packages reachable from [project.dependencies], so the py_test
|
|
56
|
+
# targets can't resolve @pypi//pytest from an optional/dev group.
|
|
57
|
+
"pytest>=9.1.1",
|
|
58
|
+
# gRPC & Protobuf
|
|
59
|
+
"grpcio>=1.68.0",
|
|
60
|
+
"protobuf>=5.29.0",
|
|
61
|
+
# Not imported by garage_rag: the macOS app's embedded interpreter imports them at start-up so
|
|
62
|
+
# ssl's default contexts verify against the system trust store (certifi is the fallback). The
|
|
63
|
+
# bundled OpenSSL has no CA files of its own.
|
|
64
|
+
"truststore>=0.10",
|
|
65
|
+
"certifi>=2024.2.2",
|
|
66
|
+
]
|
|
67
|
+
|
|
68
|
+
[project.optional-dependencies]
|
|
69
|
+
# Local tooling for the uv venv only; the Bazel @pypi hub does not expose this group.
|
|
70
|
+
dev = [
|
|
71
|
+
"pytest-asyncio>=0.24",
|
|
72
|
+
"ruff>=0.8",
|
|
73
|
+
"grpcio-tools>=1.68.0",
|
|
74
|
+
# tools/swiftcheck: Swift syntax checking where no Swift toolchain exists (Linux, web sessions)
|
|
75
|
+
"tree-sitter>=0.25",
|
|
76
|
+
"tree-sitter-swift>=0.7",
|
|
77
|
+
# garage_python/tests/fake_messages.py: the invented people and messages in the fake chat.db
|
|
78
|
+
"faker>=37",
|
|
79
|
+
]
|
|
80
|
+
# aspect_rules_py builds sdists into wheels with `build` and `setuptools`, resolved from uv.lock
|
|
81
|
+
# and from the hub's dependency group. grpcio ships no free-threaded (cp314t) wheels, so the 3.14t
|
|
82
|
+
# build compiles it from source. The Bazel group below pulls this extra in.
|
|
83
|
+
build-tools = ["build", "setuptools"]
|
|
84
|
+
|
|
85
|
+
[dependency-groups]
|
|
86
|
+
# The @pypi hub's group (`--@pypi//dep_group=garage_rag` in .bazelrc). Without a
|
|
87
|
+
# [dependency-groups] table the hub derives it from the project alone; declaring one replaces that,
|
|
88
|
+
# so it names the project itself, plus the sdist build tools.
|
|
89
|
+
garage_rag = ["garage_rag[build-tools]"]
|
|
90
|
+
|
|
91
|
+
[project.urls]
|
|
92
|
+
Homepage = "https://garagerag.app"
|
|
93
|
+
Source = "https://github.com/rickmark/garage-rag"
|
|
94
|
+
Issues = "https://github.com/rickmark/garage-rag/issues"
|
|
95
|
+
|
|
96
|
+
[project.scripts]
|
|
97
|
+
garage = "garage_rag.cli:main_cli"
|
|
98
|
+
garage-mcp = "garage_rag.mcp_server.server:main"
|
|
99
|
+
|
|
100
|
+
[build-system]
|
|
101
|
+
requires = ["hatchling"]
|
|
102
|
+
build-backend = "hatchling.build"
|
|
103
|
+
|
|
104
|
+
[tool.hatch.build.targets.wheel]
|
|
105
|
+
packages = ["src/garage_rag"]
|
|
106
|
+
exclude = ["BUILD.bazel"]
|
|
107
|
+
|
|
108
|
+
# PyPI's sdist: the package and its build hook, without the tests (their corpora are most of the
|
|
109
|
+
# size) or the Bazel and uv files.
|
|
110
|
+
[tool.hatch.build.targets.sdist]
|
|
111
|
+
include = ["/src/garage_rag", "/hatch_build.py", "/README.md", "/LICENSE"]
|
|
112
|
+
exclude = ["BUILD.bazel"]
|
|
113
|
+
|
|
114
|
+
# hatch_build.py: copies ../data/sql and ../data/models/models.json into the package as
|
|
115
|
+
# garage_rag/_data, which a PyPI install reads in place of the repository's data/.
|
|
116
|
+
[tool.hatch.build.hooks.custom]
|
|
117
|
+
|
|
118
|
+
[tool.uv]
|
|
119
|
+
environments = ["sys_platform == 'darwin' and platform_machine == 'arm64'"]
|
|
120
|
+
|
|
121
|
+
[tool.ruff]
|
|
122
|
+
line-length = 120
|
|
123
|
+
target-version = "py313"
|
|
124
|
+
# Vendored third-party code keeps its upstream style (see its __init__ docstring), and the test
|
|
125
|
+
# corpora are upstream copies too (ruff also formats the code blocks in their Markdown).
|
|
126
|
+
extend-exclude = ["*_pb2.py", "*_pb2_grpc.py", "*_pb2.pyi", "src/garage_rag/enrich/langextract", "tests/corpora"]
|
|
127
|
+
|
|
128
|
+
[tool.ruff.lint]
|
|
129
|
+
select = ["E", "F", "I", "UP", "B", "SIM"]
|
|
130
|
+
|
|
131
|
+
[tool.ruff.lint.isort]
|
|
132
|
+
known-first-party = ["garage_rag"]
|
|
133
|
+
|
|
134
|
+
[tool.pytest.ini_options]
|
|
135
|
+
testpaths = ["tests"]
|
|
136
|
+
filterwarnings = ["error::DeprecationWarning"]
|
|
137
|
+
|
|
138
|
+
[tool.ty.src]
|
|
139
|
+
# Generated protobuf stubs are excluded from type checking, as they are from Ruff
|
|
140
|
+
# above. `exclude` lives under `src` for ty; a top-level `[tool.ty] exclude` is a
|
|
141
|
+
# hard config error that makes ty exit non-zero before it checks anything.
|
|
142
|
+
exclude = ["*_pb2.py", "*_pb2_grpc.py", "*_pb2.pyi", "src/garage_rag/enrich/langextract"]
|
|
143
|
+
|
|
144
|
+
[tool.ty.rules]
|
|
145
|
+
unresolved-import = "ignore"
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"""garage-rag: a local-first personal RAG pipeline.
|
|
2
|
+
|
|
3
|
+
Indexes personal documents into Postgres + pgvector, preserving authorship,
|
|
4
|
+
reference, and communication distinctions, and exposes the corpus to Claude
|
|
5
|
+
through an MCP server.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
__version__ = "1.5.0"
|
|
9
|
+
|
|
10
|
+
# Must run before psycopg is imported anywhere in the package: makes psycopg's
|
|
11
|
+
# ctypes based libpq lookup use the signed copy the app's framework has loaded
|
|
12
|
+
# instead of a Homebrew/system library that dyld rejects.
|
|
13
|
+
from . import libpq as _libpq # noqa: E402
|
|
14
|
+
|
|
15
|
+
_libpq.configure()
|