pydantic-rag-agent 0.1.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.
- pydantic_rag_agent-0.1.0/LICENSE +21 -0
- pydantic_rag_agent-0.1.0/PKG-INFO +247 -0
- pydantic_rag_agent-0.1.0/README.md +195 -0
- pydantic_rag_agent-0.1.0/pyproject.toml +138 -0
- pydantic_rag_agent-0.1.0/pyproject.toml.orig +144 -0
- pydantic_rag_agent-0.1.0/src/pydantic_rag_agent/__init__.py +99 -0
- pydantic_rag_agent-0.1.0/src/pydantic_rag_agent/adapters/__init__.py +1 -0
- pydantic_rag_agent-0.1.0/src/pydantic_rag_agent/adapters/agent.py +400 -0
- pydantic_rag_agent-0.1.0/src/pydantic_rag_agent/adapters/captioning.py +93 -0
- pydantic_rag_agent-0.1.0/src/pydantic_rag_agent/adapters/chroma_store.py +277 -0
- pydantic_rag_agent-0.1.0/src/pydantic_rag_agent/adapters/compaction.py +107 -0
- pydantic_rag_agent-0.1.0/src/pydantic_rag_agent/adapters/conversations.py +115 -0
- pydantic_rag_agent-0.1.0/src/pydantic_rag_agent/adapters/document_store/__init__.py +1 -0
- pydantic_rag_agent-0.1.0/src/pydantic_rag_agent/adapters/document_store/_keys.py +43 -0
- pydantic_rag_agent-0.1.0/src/pydantic_rag_agent/adapters/document_store/local.py +76 -0
- pydantic_rag_agent-0.1.0/src/pydantic_rag_agent/adapters/document_store/s3.py +109 -0
- pydantic_rag_agent-0.1.0/src/pydantic_rag_agent/adapters/image_store.py +68 -0
- pydantic_rag_agent-0.1.0/src/pydantic_rag_agent/adapters/loaders/__init__.py +18 -0
- pydantic_rag_agent-0.1.0/src/pydantic_rag_agent/adapters/loaders/_tables.py +27 -0
- pydantic_rag_agent-0.1.0/src/pydantic_rag_agent/adapters/loaders/docx.py +209 -0
- pydantic_rag_agent-0.1.0/src/pydantic_rag_agent/adapters/loaders/pdf.py +132 -0
- pydantic_rag_agent-0.1.0/src/pydantic_rag_agent/adapters/loaders/pptx.py +150 -0
- pydantic_rag_agent-0.1.0/src/pydantic_rag_agent/adapters/loaders/text.py +94 -0
- pydantic_rag_agent-0.1.0/src/pydantic_rag_agent/adapters/memory_store.py +149 -0
- pydantic_rag_agent-0.1.0/src/pydantic_rag_agent/agent.py +648 -0
- pydantic_rag_agent-0.1.0/src/pydantic_rag_agent/application/__init__.py +4 -0
- pydantic_rag_agent-0.1.0/src/pydantic_rag_agent/application/chunking.py +180 -0
- pydantic_rag_agent-0.1.0/src/pydantic_rag_agent/application/citations.py +59 -0
- pydantic_rag_agent-0.1.0/src/pydantic_rag_agent/application/embedding.py +78 -0
- pydantic_rag_agent-0.1.0/src/pydantic_rag_agent/application/evaluation.py +182 -0
- pydantic_rag_agent-0.1.0/src/pydantic_rag_agent/application/index_guard.py +41 -0
- pydantic_rag_agent-0.1.0/src/pydantic_rag_agent/application/ingestion.py +421 -0
- pydantic_rag_agent-0.1.0/src/pydantic_rag_agent/application/retrieval.py +88 -0
- pydantic_rag_agent-0.1.0/src/pydantic_rag_agent/cli.py +699 -0
- pydantic_rag_agent-0.1.0/src/pydantic_rag_agent/client.py +200 -0
- pydantic_rag_agent-0.1.0/src/pydantic_rag_agent/config.py +342 -0
- pydantic_rag_agent-0.1.0/src/pydantic_rag_agent/domain/__init__.py +1 -0
- pydantic_rag_agent-0.1.0/src/pydantic_rag_agent/domain/errors.py +37 -0
- pydantic_rag_agent-0.1.0/src/pydantic_rag_agent/domain/extraction.py +58 -0
- pydantic_rag_agent-0.1.0/src/pydantic_rag_agent/domain/models.py +240 -0
- pydantic_rag_agent-0.1.0/src/pydantic_rag_agent/domain/ports.py +130 -0
- pydantic_rag_agent-0.1.0/src/pydantic_rag_agent/extras.py +43 -0
- pydantic_rag_agent-0.1.0/src/pydantic_rag_agent/in_process.py +270 -0
- pydantic_rag_agent-0.1.0/src/pydantic_rag_agent/integrations/__init__.py +1 -0
- pydantic_rag_agent-0.1.0/src/pydantic_rag_agent/integrations/fastapi/__init__.py +14 -0
- pydantic_rag_agent-0.1.0/src/pydantic_rag_agent/integrations/fastapi/app.py +333 -0
- pydantic_rag_agent-0.1.0/src/pydantic_rag_agent/integrations/fastapi/openai.py +338 -0
- pydantic_rag_agent-0.1.0/src/pydantic_rag_agent/integrations/fastapi/schemas.py +97 -0
- pydantic_rag_agent-0.1.0/src/pydantic_rag_agent/py.typed +0 -0
- pydantic_rag_agent-0.1.0/src/pydantic_rag_agent/ui/__init__.py +1 -0
- pydantic_rag_agent-0.1.0/src/pydantic_rag_agent/ui/streamlit_app.py +259 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Nick
|
|
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,247 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: pydantic-rag-agent
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: A RAG agent built on Pydantic.
|
|
5
|
+
Keywords: rag,retrieval-augmented-generation,pydantic,pydantic-ai,llm,vector-search
|
|
6
|
+
Author: Nick
|
|
7
|
+
Author-email: Nick <98120512+hanku4u@users.noreply.github.com>
|
|
8
|
+
License-Expression: MIT
|
|
9
|
+
License-File: LICENSE
|
|
10
|
+
Classifier: Development Status :: 3 - Alpha
|
|
11
|
+
Classifier: Environment :: Console
|
|
12
|
+
Classifier: Framework :: FastAPI
|
|
13
|
+
Classifier: Framework :: Pydantic
|
|
14
|
+
Classifier: Framework :: Pydantic :: 2
|
|
15
|
+
Classifier: Intended Audience :: Developers
|
|
16
|
+
Classifier: Operating System :: OS Independent
|
|
17
|
+
Classifier: Programming Language :: Python :: 3
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
19
|
+
Classifier: Typing :: Typed
|
|
20
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
21
|
+
Classifier: Topic :: Text Processing :: Indexing
|
|
22
|
+
Requires-Dist: httpx>=0.28.1
|
|
23
|
+
Requires-Dist: pillow>=13.0.0
|
|
24
|
+
Requires-Dist: pydantic>=2.14.1
|
|
25
|
+
Requires-Dist: pydantic-ai-slim[openai]>=2.61,<3
|
|
26
|
+
Requires-Dist: pydantic-settings>=2.16.1
|
|
27
|
+
Requires-Dist: pypdfium2>=5.14.0
|
|
28
|
+
Requires-Dist: python-docx>=1.2.0
|
|
29
|
+
Requires-Dist: python-dotenv>=1.2.4
|
|
30
|
+
Requires-Dist: python-pptx>=1.0.2
|
|
31
|
+
Requires-Dist: rich>=15.0.0
|
|
32
|
+
Requires-Dist: tiktoken>=0.14.0
|
|
33
|
+
Requires-Dist: typer>=0.27.3
|
|
34
|
+
Requires-Dist: pydantic-rag-agent[chroma,api,ui,s3] ; extra == 'all'
|
|
35
|
+
Requires-Dist: fastapi>=0.162.0 ; extra == 'api'
|
|
36
|
+
Requires-Dist: python-multipart>=0.0.32 ; extra == 'api'
|
|
37
|
+
Requires-Dist: uvicorn[standard]>=0.54.0 ; extra == 'api'
|
|
38
|
+
Requires-Dist: chromadb>=1.5.9 ; extra == 'chroma'
|
|
39
|
+
Requires-Dist: boto3>=1.35 ; extra == 's3'
|
|
40
|
+
Requires-Dist: streamlit>=1.66.1 ; extra == 'ui'
|
|
41
|
+
Requires-Python: >=3.11
|
|
42
|
+
Project-URL: Homepage, https://github.com/hanku4u/pydantic-rag-agent
|
|
43
|
+
Project-URL: Repository, https://github.com/hanku4u/pydantic-rag-agent
|
|
44
|
+
Project-URL: Documentation, https://github.com/hanku4u/pydantic-rag-agent/blob/main/docs/README.md
|
|
45
|
+
Project-URL: Issues, https://github.com/hanku4u/pydantic-rag-agent/issues
|
|
46
|
+
Provides-Extra: all
|
|
47
|
+
Provides-Extra: api
|
|
48
|
+
Provides-Extra: chroma
|
|
49
|
+
Provides-Extra: s3
|
|
50
|
+
Provides-Extra: ui
|
|
51
|
+
Description-Content-Type: text/markdown
|
|
52
|
+
|
|
53
|
+
# pydantic-rag-agent
|
|
54
|
+
|
|
55
|
+
A retrieval-augmented generation (RAG) agent built on [Pydantic](https://docs.pydantic.dev/)
|
|
56
|
+
and managed with [uv](https://docs.astral.sh/uv/).
|
|
57
|
+
|
|
58
|
+
> **Status:** feature-complete for a local, single-user deployment. M0–M5 are done and
|
|
59
|
+
> M6 is closed apart from OPS-1 (instrumentation), which is deliberately out of scope.
|
|
60
|
+
> Ingestion, retrieval, the agent, the API, the CLI and the Streamlit UI all work; the
|
|
61
|
+
> suite runs offline at 92% branch coverage. `main` is the state described in
|
|
62
|
+
> [`docs/overview.md`](https://github.com/hanku4u/pydantic-rag-agent/blob/main/docs/overview.md#current-state); the ordered plan behind it is in
|
|
63
|
+
> [`docs/features/`](https://github.com/hanku4u/pydantic-rag-agent/blob/main/docs/features/README.md) and
|
|
64
|
+
> [`docs/roadmap.md`](https://github.com/hanku4u/pydantic-rag-agent/blob/main/docs/roadmap.md).
|
|
65
|
+
|
|
66
|
+
## Why uv?
|
|
67
|
+
|
|
68
|
+
Dependency resolution and virtualenv handling live in one fast tool, and `uv.lock`
|
|
69
|
+
pins the exact graph so your CI and your laptop agree on what "it works" means.
|
|
70
|
+
|
|
71
|
+
## Documentation
|
|
72
|
+
|
|
73
|
+
Full documentation lives in [`docs/`](https://github.com/hanku4u/pydantic-rag-agent/blob/main/docs/README.md) — start with the
|
|
74
|
+
[documentation index](https://github.com/hanku4u/pydantic-rag-agent/blob/main/docs/README.md) or the [project overview](https://github.com/hanku4u/pydantic-rag-agent/blob/main/docs/overview.md).
|
|
75
|
+
Design rationale is recorded as [decision records](https://github.com/hanku4u/pydantic-rag-agent/blob/main/docs/decisions/README.md).
|
|
76
|
+
|
|
77
|
+
Coding agents should read [`AGENTS.md`](https://github.com/hanku4u/pydantic-rag-agent/blob/main/AGENTS.md) first.
|
|
78
|
+
|
|
79
|
+
## Requirements
|
|
80
|
+
|
|
81
|
+
- [uv](https://docs.astral.sh/uv/getting-started/installation/) 0.13 or newer
|
|
82
|
+
- Python 3.11+ — uv will fetch and manage the interpreter for you if needed
|
|
83
|
+
|
|
84
|
+
## Install
|
|
85
|
+
|
|
86
|
+
```bash
|
|
87
|
+
uv add "pydantic-rag-agent" # core: agent, loaders, CLI, in-memory store
|
|
88
|
+
uv add "pydantic-rag-agent[chroma]" # plus the default persistent store
|
|
89
|
+
uv add "pydantic-rag-agent[chroma,ui]" # plus the Streamlit UI
|
|
90
|
+
uv add "pydantic-rag-agent[chroma,api]" # plus the FastAPI integration
|
|
91
|
+
uv add "pydantic-rag-agent[chroma,s3]" # plus originals in a bucket
|
|
92
|
+
uv add "pydantic-rag-agent[all]" # everything
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
The core install has no vector database: it runs with the in-memory store, which is fine
|
|
96
|
+
for a script that ingests and asks in one go. Anything the agent does not need at import
|
|
97
|
+
time is an extra, and a missing one tells you which to install.
|
|
98
|
+
|
|
99
|
+
> **Not on PyPI yet.** The first release is milestone 7 — see
|
|
100
|
+
> [`docs/features/`](https://github.com/hanku4u/pydantic-rag-agent/blob/main/docs/features/README.md) and
|
|
101
|
+
> [ADR 0013](https://github.com/hanku4u/pydantic-rag-agent/blob/main/docs/decisions/0013-packaging-and-release.md). Until then, install from the
|
|
102
|
+
> repository as below.
|
|
103
|
+
|
|
104
|
+
As a library, from the repository today (and from PyPI once published):
|
|
105
|
+
|
|
106
|
+
```python
|
|
107
|
+
from pydantic_rag_agent import RagAgent
|
|
108
|
+
|
|
109
|
+
agent = RagAgent.from_env()
|
|
110
|
+
agent.ingest_sync("./docs")
|
|
111
|
+
print(agent.ask_sync("How long are backups retained?").text)
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
The async methods (`await agent.ask(...)`, `async for update in agent.stream(...)`) are
|
|
115
|
+
what a web app wants; the sync helpers exist so a script does not have to become async to
|
|
116
|
+
ask one question. Answers carry citations back to the source document, including the
|
|
117
|
+
locator of the stored original (`file://` or `s3://`).
|
|
118
|
+
|
|
119
|
+
Already have a FastAPI app? Mount the router instead of running a second service:
|
|
120
|
+
|
|
121
|
+
```python
|
|
122
|
+
from contextlib import asynccontextmanager
|
|
123
|
+
|
|
124
|
+
from fastapi import FastAPI
|
|
125
|
+
|
|
126
|
+
from pydantic_rag_agent import RagAgent
|
|
127
|
+
from pydantic_rag_agent.integrations.fastapi import create_router
|
|
128
|
+
|
|
129
|
+
agent = RagAgent.from_env()
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
@asynccontextmanager
|
|
133
|
+
async def lifespan(app: FastAPI):
|
|
134
|
+
await agent.check_index()
|
|
135
|
+
try:
|
|
136
|
+
yield
|
|
137
|
+
finally:
|
|
138
|
+
await agent.aclose()
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
app = FastAPI(lifespan=lifespan)
|
|
142
|
+
app.include_router(create_router(agent), prefix="/knowledge")
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
The router brings the routes; your app keeps its lifespan, its auth and its shutdown.
|
|
146
|
+
|
|
147
|
+
Any OpenAI-speaking chat UI can be pointed at the agent instead of a bespoke frontend:
|
|
148
|
+
|
|
149
|
+
```python
|
|
150
|
+
from pydantic_rag_agent.integrations.fastapi import create_openai_router
|
|
151
|
+
|
|
152
|
+
app.include_router(create_openai_router(agent, model_id="acme-knowledge"), prefix="/openai")
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
Point the client at `https://your-host/openai/v1`. Requests are stateless (the client
|
|
156
|
+
sends the thread), caller-supplied system messages are ignored, and answers carry a
|
|
157
|
+
`Sources:` footer. `serve` already exposes the same route at `/v1`, so a chat UI can be
|
|
158
|
+
pointed at a running server without writing any code — `--openai-model-id` chooses the name
|
|
159
|
+
the client shows. See [ADR 0014](https://github.com/hanku4u/pydantic-rag-agent/blob/main/docs/decisions/0014-openai-compatible-endpoint.md).
|
|
160
|
+
|
|
161
|
+
## Getting started from the repository
|
|
162
|
+
|
|
163
|
+
```bash
|
|
164
|
+
# Clone and enter the project
|
|
165
|
+
git clone git@github.com:hanku4u/pydantic-rag-agent.git
|
|
166
|
+
cd pydantic-rag-agent
|
|
167
|
+
|
|
168
|
+
# Create .venv and install the project plus its dependencies
|
|
169
|
+
uv sync
|
|
170
|
+
|
|
171
|
+
# Provide the one credential: chat, captions, and embeddings all use it
|
|
172
|
+
cp .env.example .env
|
|
173
|
+
# then edit .env and set OPENROUTER_API_KEY
|
|
174
|
+
|
|
175
|
+
# Index and ask — the agent runs in this process, no server needed
|
|
176
|
+
uv run pydantic-rag-agent ingest ./docs
|
|
177
|
+
uv run pydantic-rag-agent ask "How long are backups retained?"
|
|
178
|
+
|
|
179
|
+
# Optional: the API and web UI together
|
|
180
|
+
uv run pydantic-rag-agent dev
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
The terminal answer streams and ends with a table of the sources it cited. The chat UI
|
|
184
|
+
from `dev` is at <http://127.0.0.1:8501>. `--api-url` makes any command talk to a running
|
|
185
|
+
server instead of building the agent locally.
|
|
186
|
+
|
|
187
|
+
## Day-to-day commands
|
|
188
|
+
|
|
189
|
+
| Task | Command |
|
|
190
|
+
| --- | --- |
|
|
191
|
+
| Install or refresh dependencies | `uv sync` |
|
|
192
|
+
| Add a runtime dependency | `uv add <package>` |
|
|
193
|
+
| Add a dev-only dependency | `uv add --dev <package>` |
|
|
194
|
+
| Remove a dependency | `uv remove <package>` |
|
|
195
|
+
| Run a command inside the venv | `uv run <command>` |
|
|
196
|
+
| Run a script without installing it | `uv run path/to/script.py` |
|
|
197
|
+
|
|
198
|
+
## Project layout
|
|
199
|
+
|
|
200
|
+
```text
|
|
201
|
+
pydantic-rag-agent/
|
|
202
|
+
├── pyproject.toml # project metadata, dependencies, CLI entry point
|
|
203
|
+
├── uv.lock # exact resolved versions — commit this
|
|
204
|
+
├── .python-version # interpreter uv should use
|
|
205
|
+
├── AGENTS.md # orientation for coding agents
|
|
206
|
+
├── docs/ # design, backlog, decisions — start at docs/README.md
|
|
207
|
+
├── src/
|
|
208
|
+
│ └── pydantic_rag_agent/
|
|
209
|
+
│ ├── __init__.py # package marker, no import-time side effects
|
|
210
|
+
│ └── cli.py # command-line entry point (`main`)
|
|
211
|
+
└── README.md
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
The `src/` layout keeps the package importable from an installed build rather
|
|
215
|
+
than by accident from the working directory, which is what makes
|
|
216
|
+
`uv run pytest` behave the same as a real install.
|
|
217
|
+
|
|
218
|
+
## Configuration
|
|
219
|
+
|
|
220
|
+
Settings are a tree of six nodes — provider, embeddings, vector store, storage,
|
|
221
|
+
ingestion, agent — read from `PRAGENT_*` environment variables via `pydantic-settings`.
|
|
222
|
+
Nesting is addressed with a double underscore: `PRAGENT_PROVIDER__MODEL`,
|
|
223
|
+
`PRAGENT_AGENT__TOP_K`. Each node can also be constructed in code, which is how the
|
|
224
|
+
library is used.
|
|
225
|
+
|
|
226
|
+
Credentials keep their conventional names (`OPENROUTER_API_KEY`) and are never read from
|
|
227
|
+
a committed file. Every variable, its default, and the mapping from the pre-0.1.0 flat
|
|
228
|
+
names are in [`docs/reference/configuration.md`](https://github.com/hanku4u/pydantic-rag-agent/blob/main/docs/reference/configuration.md).
|
|
229
|
+
|
|
230
|
+
The `.gitignore` ignores `.env` and any `.env.*` file except `.env.example`.
|
|
231
|
+
|
|
232
|
+
## Contributing
|
|
233
|
+
|
|
234
|
+
1. `uv sync` to get a working environment.
|
|
235
|
+
2. Pick a feature ID from the [backlog](https://github.com/hanku4u/pydantic-rag-agent/blob/main/docs/features/README.md) and reference it in
|
|
236
|
+
your commit subject.
|
|
237
|
+
3. Add tests under `tests/` next to the module they cover.
|
|
238
|
+
4. `uv run pytest` before opening a pull request.
|
|
239
|
+
|
|
240
|
+
## License
|
|
241
|
+
|
|
242
|
+
[MIT](https://github.com/hanku4u/pydantic-rag-agent/blob/main/LICENSE). Use it, fork it, ship it — just keep the copyright notice.
|
|
243
|
+
|
|
244
|
+
The dependency stack is deliberately permissive end to end; the one AGPL package that
|
|
245
|
+
previously blocked this was replaced. See
|
|
246
|
+
[`docs/reference/dependencies.md`](https://github.com/hanku4u/pydantic-rag-agent/blob/main/docs/reference/dependencies.md) for the details, and
|
|
247
|
+
[ADR 0007](https://github.com/hanku4u/pydantic-rag-agent/blob/main/docs/decisions/0007-mit-licence-and-pypdfium2.md) for the reasoning.
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
# pydantic-rag-agent
|
|
2
|
+
|
|
3
|
+
A retrieval-augmented generation (RAG) agent built on [Pydantic](https://docs.pydantic.dev/)
|
|
4
|
+
and managed with [uv](https://docs.astral.sh/uv/).
|
|
5
|
+
|
|
6
|
+
> **Status:** feature-complete for a local, single-user deployment. M0–M5 are done and
|
|
7
|
+
> M6 is closed apart from OPS-1 (instrumentation), which is deliberately out of scope.
|
|
8
|
+
> Ingestion, retrieval, the agent, the API, the CLI and the Streamlit UI all work; the
|
|
9
|
+
> suite runs offline at 92% branch coverage. `main` is the state described in
|
|
10
|
+
> [`docs/overview.md`](https://github.com/hanku4u/pydantic-rag-agent/blob/main/docs/overview.md#current-state); the ordered plan behind it is in
|
|
11
|
+
> [`docs/features/`](https://github.com/hanku4u/pydantic-rag-agent/blob/main/docs/features/README.md) and
|
|
12
|
+
> [`docs/roadmap.md`](https://github.com/hanku4u/pydantic-rag-agent/blob/main/docs/roadmap.md).
|
|
13
|
+
|
|
14
|
+
## Why uv?
|
|
15
|
+
|
|
16
|
+
Dependency resolution and virtualenv handling live in one fast tool, and `uv.lock`
|
|
17
|
+
pins the exact graph so your CI and your laptop agree on what "it works" means.
|
|
18
|
+
|
|
19
|
+
## Documentation
|
|
20
|
+
|
|
21
|
+
Full documentation lives in [`docs/`](https://github.com/hanku4u/pydantic-rag-agent/blob/main/docs/README.md) — start with the
|
|
22
|
+
[documentation index](https://github.com/hanku4u/pydantic-rag-agent/blob/main/docs/README.md) or the [project overview](https://github.com/hanku4u/pydantic-rag-agent/blob/main/docs/overview.md).
|
|
23
|
+
Design rationale is recorded as [decision records](https://github.com/hanku4u/pydantic-rag-agent/blob/main/docs/decisions/README.md).
|
|
24
|
+
|
|
25
|
+
Coding agents should read [`AGENTS.md`](https://github.com/hanku4u/pydantic-rag-agent/blob/main/AGENTS.md) first.
|
|
26
|
+
|
|
27
|
+
## Requirements
|
|
28
|
+
|
|
29
|
+
- [uv](https://docs.astral.sh/uv/getting-started/installation/) 0.13 or newer
|
|
30
|
+
- Python 3.11+ — uv will fetch and manage the interpreter for you if needed
|
|
31
|
+
|
|
32
|
+
## Install
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
uv add "pydantic-rag-agent" # core: agent, loaders, CLI, in-memory store
|
|
36
|
+
uv add "pydantic-rag-agent[chroma]" # plus the default persistent store
|
|
37
|
+
uv add "pydantic-rag-agent[chroma,ui]" # plus the Streamlit UI
|
|
38
|
+
uv add "pydantic-rag-agent[chroma,api]" # plus the FastAPI integration
|
|
39
|
+
uv add "pydantic-rag-agent[chroma,s3]" # plus originals in a bucket
|
|
40
|
+
uv add "pydantic-rag-agent[all]" # everything
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
The core install has no vector database: it runs with the in-memory store, which is fine
|
|
44
|
+
for a script that ingests and asks in one go. Anything the agent does not need at import
|
|
45
|
+
time is an extra, and a missing one tells you which to install.
|
|
46
|
+
|
|
47
|
+
> **Not on PyPI yet.** The first release is milestone 7 — see
|
|
48
|
+
> [`docs/features/`](https://github.com/hanku4u/pydantic-rag-agent/blob/main/docs/features/README.md) and
|
|
49
|
+
> [ADR 0013](https://github.com/hanku4u/pydantic-rag-agent/blob/main/docs/decisions/0013-packaging-and-release.md). Until then, install from the
|
|
50
|
+
> repository as below.
|
|
51
|
+
|
|
52
|
+
As a library, from the repository today (and from PyPI once published):
|
|
53
|
+
|
|
54
|
+
```python
|
|
55
|
+
from pydantic_rag_agent import RagAgent
|
|
56
|
+
|
|
57
|
+
agent = RagAgent.from_env()
|
|
58
|
+
agent.ingest_sync("./docs")
|
|
59
|
+
print(agent.ask_sync("How long are backups retained?").text)
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
The async methods (`await agent.ask(...)`, `async for update in agent.stream(...)`) are
|
|
63
|
+
what a web app wants; the sync helpers exist so a script does not have to become async to
|
|
64
|
+
ask one question. Answers carry citations back to the source document, including the
|
|
65
|
+
locator of the stored original (`file://` or `s3://`).
|
|
66
|
+
|
|
67
|
+
Already have a FastAPI app? Mount the router instead of running a second service:
|
|
68
|
+
|
|
69
|
+
```python
|
|
70
|
+
from contextlib import asynccontextmanager
|
|
71
|
+
|
|
72
|
+
from fastapi import FastAPI
|
|
73
|
+
|
|
74
|
+
from pydantic_rag_agent import RagAgent
|
|
75
|
+
from pydantic_rag_agent.integrations.fastapi import create_router
|
|
76
|
+
|
|
77
|
+
agent = RagAgent.from_env()
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
@asynccontextmanager
|
|
81
|
+
async def lifespan(app: FastAPI):
|
|
82
|
+
await agent.check_index()
|
|
83
|
+
try:
|
|
84
|
+
yield
|
|
85
|
+
finally:
|
|
86
|
+
await agent.aclose()
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
app = FastAPI(lifespan=lifespan)
|
|
90
|
+
app.include_router(create_router(agent), prefix="/knowledge")
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
The router brings the routes; your app keeps its lifespan, its auth and its shutdown.
|
|
94
|
+
|
|
95
|
+
Any OpenAI-speaking chat UI can be pointed at the agent instead of a bespoke frontend:
|
|
96
|
+
|
|
97
|
+
```python
|
|
98
|
+
from pydantic_rag_agent.integrations.fastapi import create_openai_router
|
|
99
|
+
|
|
100
|
+
app.include_router(create_openai_router(agent, model_id="acme-knowledge"), prefix="/openai")
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
Point the client at `https://your-host/openai/v1`. Requests are stateless (the client
|
|
104
|
+
sends the thread), caller-supplied system messages are ignored, and answers carry a
|
|
105
|
+
`Sources:` footer. `serve` already exposes the same route at `/v1`, so a chat UI can be
|
|
106
|
+
pointed at a running server without writing any code — `--openai-model-id` chooses the name
|
|
107
|
+
the client shows. See [ADR 0014](https://github.com/hanku4u/pydantic-rag-agent/blob/main/docs/decisions/0014-openai-compatible-endpoint.md).
|
|
108
|
+
|
|
109
|
+
## Getting started from the repository
|
|
110
|
+
|
|
111
|
+
```bash
|
|
112
|
+
# Clone and enter the project
|
|
113
|
+
git clone git@github.com:hanku4u/pydantic-rag-agent.git
|
|
114
|
+
cd pydantic-rag-agent
|
|
115
|
+
|
|
116
|
+
# Create .venv and install the project plus its dependencies
|
|
117
|
+
uv sync
|
|
118
|
+
|
|
119
|
+
# Provide the one credential: chat, captions, and embeddings all use it
|
|
120
|
+
cp .env.example .env
|
|
121
|
+
# then edit .env and set OPENROUTER_API_KEY
|
|
122
|
+
|
|
123
|
+
# Index and ask — the agent runs in this process, no server needed
|
|
124
|
+
uv run pydantic-rag-agent ingest ./docs
|
|
125
|
+
uv run pydantic-rag-agent ask "How long are backups retained?"
|
|
126
|
+
|
|
127
|
+
# Optional: the API and web UI together
|
|
128
|
+
uv run pydantic-rag-agent dev
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
The terminal answer streams and ends with a table of the sources it cited. The chat UI
|
|
132
|
+
from `dev` is at <http://127.0.0.1:8501>. `--api-url` makes any command talk to a running
|
|
133
|
+
server instead of building the agent locally.
|
|
134
|
+
|
|
135
|
+
## Day-to-day commands
|
|
136
|
+
|
|
137
|
+
| Task | Command |
|
|
138
|
+
| --- | --- |
|
|
139
|
+
| Install or refresh dependencies | `uv sync` |
|
|
140
|
+
| Add a runtime dependency | `uv add <package>` |
|
|
141
|
+
| Add a dev-only dependency | `uv add --dev <package>` |
|
|
142
|
+
| Remove a dependency | `uv remove <package>` |
|
|
143
|
+
| Run a command inside the venv | `uv run <command>` |
|
|
144
|
+
| Run a script without installing it | `uv run path/to/script.py` |
|
|
145
|
+
|
|
146
|
+
## Project layout
|
|
147
|
+
|
|
148
|
+
```text
|
|
149
|
+
pydantic-rag-agent/
|
|
150
|
+
├── pyproject.toml # project metadata, dependencies, CLI entry point
|
|
151
|
+
├── uv.lock # exact resolved versions — commit this
|
|
152
|
+
├── .python-version # interpreter uv should use
|
|
153
|
+
├── AGENTS.md # orientation for coding agents
|
|
154
|
+
├── docs/ # design, backlog, decisions — start at docs/README.md
|
|
155
|
+
├── src/
|
|
156
|
+
│ └── pydantic_rag_agent/
|
|
157
|
+
│ ├── __init__.py # package marker, no import-time side effects
|
|
158
|
+
│ └── cli.py # command-line entry point (`main`)
|
|
159
|
+
└── README.md
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
The `src/` layout keeps the package importable from an installed build rather
|
|
163
|
+
than by accident from the working directory, which is what makes
|
|
164
|
+
`uv run pytest` behave the same as a real install.
|
|
165
|
+
|
|
166
|
+
## Configuration
|
|
167
|
+
|
|
168
|
+
Settings are a tree of six nodes — provider, embeddings, vector store, storage,
|
|
169
|
+
ingestion, agent — read from `PRAGENT_*` environment variables via `pydantic-settings`.
|
|
170
|
+
Nesting is addressed with a double underscore: `PRAGENT_PROVIDER__MODEL`,
|
|
171
|
+
`PRAGENT_AGENT__TOP_K`. Each node can also be constructed in code, which is how the
|
|
172
|
+
library is used.
|
|
173
|
+
|
|
174
|
+
Credentials keep their conventional names (`OPENROUTER_API_KEY`) and are never read from
|
|
175
|
+
a committed file. Every variable, its default, and the mapping from the pre-0.1.0 flat
|
|
176
|
+
names are in [`docs/reference/configuration.md`](https://github.com/hanku4u/pydantic-rag-agent/blob/main/docs/reference/configuration.md).
|
|
177
|
+
|
|
178
|
+
The `.gitignore` ignores `.env` and any `.env.*` file except `.env.example`.
|
|
179
|
+
|
|
180
|
+
## Contributing
|
|
181
|
+
|
|
182
|
+
1. `uv sync` to get a working environment.
|
|
183
|
+
2. Pick a feature ID from the [backlog](https://github.com/hanku4u/pydantic-rag-agent/blob/main/docs/features/README.md) and reference it in
|
|
184
|
+
your commit subject.
|
|
185
|
+
3. Add tests under `tests/` next to the module they cover.
|
|
186
|
+
4. `uv run pytest` before opening a pull request.
|
|
187
|
+
|
|
188
|
+
## License
|
|
189
|
+
|
|
190
|
+
[MIT](https://github.com/hanku4u/pydantic-rag-agent/blob/main/LICENSE). Use it, fork it, ship it — just keep the copyright notice.
|
|
191
|
+
|
|
192
|
+
The dependency stack is deliberately permissive end to end; the one AGPL package that
|
|
193
|
+
previously blocked this was replaced. See
|
|
194
|
+
[`docs/reference/dependencies.md`](https://github.com/hanku4u/pydantic-rag-agent/blob/main/docs/reference/dependencies.md) for the details, and
|
|
195
|
+
[ADR 0007](https://github.com/hanku4u/pydantic-rag-agent/blob/main/docs/decisions/0007-mit-licence-and-pypdfium2.md) for the reasoning.
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "pydantic-rag-agent"
|
|
3
|
+
version = "0.1.0"
|
|
4
|
+
description = "A RAG agent built on Pydantic."
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
license = "MIT"
|
|
7
|
+
license-files = ["LICENSE"]
|
|
8
|
+
keywords = [
|
|
9
|
+
"rag",
|
|
10
|
+
"retrieval-augmented-generation",
|
|
11
|
+
"pydantic",
|
|
12
|
+
"pydantic-ai",
|
|
13
|
+
"llm",
|
|
14
|
+
"vector-search",
|
|
15
|
+
]
|
|
16
|
+
classifiers = [
|
|
17
|
+
"Development Status :: 3 - Alpha",
|
|
18
|
+
"Environment :: Console",
|
|
19
|
+
"Framework :: FastAPI",
|
|
20
|
+
"Framework :: Pydantic",
|
|
21
|
+
"Framework :: Pydantic :: 2",
|
|
22
|
+
"Intended Audience :: Developers",
|
|
23
|
+
"Operating System :: OS Independent",
|
|
24
|
+
"Programming Language :: Python :: 3",
|
|
25
|
+
"Programming Language :: Python :: 3.11",
|
|
26
|
+
"Typing :: Typed",
|
|
27
|
+
"Topic :: Software Development :: Libraries :: Python Modules",
|
|
28
|
+
"Topic :: Text Processing :: Indexing",
|
|
29
|
+
]
|
|
30
|
+
requires-python = ">=3.11"
|
|
31
|
+
dependencies = [
|
|
32
|
+
"httpx>=0.28.1",
|
|
33
|
+
"pillow>=13.0.0",
|
|
34
|
+
"pydantic>=2.14.1",
|
|
35
|
+
"pydantic-ai-slim[openai]>=2.61,<3",
|
|
36
|
+
"pydantic-settings>=2.16.1",
|
|
37
|
+
"pypdfium2>=5.14.0",
|
|
38
|
+
"python-docx>=1.2.0",
|
|
39
|
+
"python-dotenv>=1.2.4",
|
|
40
|
+
"python-pptx>=1.0.2",
|
|
41
|
+
"rich>=15.0.0",
|
|
42
|
+
"tiktoken>=0.14.0",
|
|
43
|
+
"typer>=0.27.3",
|
|
44
|
+
]
|
|
45
|
+
|
|
46
|
+
[[project.authors]]
|
|
47
|
+
name = "Nick"
|
|
48
|
+
email = "98120512+hanku4u@users.noreply.github.com"
|
|
49
|
+
|
|
50
|
+
[project.optional-dependencies]
|
|
51
|
+
chroma = ["chromadb>=1.5.9"]
|
|
52
|
+
api = [
|
|
53
|
+
"fastapi>=0.162.0",
|
|
54
|
+
"python-multipart>=0.0.32",
|
|
55
|
+
"uvicorn[standard]>=0.54.0",
|
|
56
|
+
]
|
|
57
|
+
ui = ["streamlit>=1.66.1"]
|
|
58
|
+
s3 = ["boto3>=1.35"]
|
|
59
|
+
all = ["pydantic-rag-agent[chroma,api,ui,s3]"]
|
|
60
|
+
|
|
61
|
+
[project.scripts]
|
|
62
|
+
pydantic-rag-agent = "pydantic_rag_agent.cli:main"
|
|
63
|
+
|
|
64
|
+
[project.urls]
|
|
65
|
+
Homepage = "https://github.com/hanku4u/pydantic-rag-agent"
|
|
66
|
+
Repository = "https://github.com/hanku4u/pydantic-rag-agent"
|
|
67
|
+
Documentation = "https://github.com/hanku4u/pydantic-rag-agent/blob/main/docs/README.md"
|
|
68
|
+
Issues = "https://github.com/hanku4u/pydantic-rag-agent/issues"
|
|
69
|
+
|
|
70
|
+
[build-system]
|
|
71
|
+
requires = ["uv_build>=0.13.1,<0.14.0"]
|
|
72
|
+
build-backend = "uv_build"
|
|
73
|
+
|
|
74
|
+
[dependency-groups]
|
|
75
|
+
dev = [
|
|
76
|
+
"mypy>=2.4.0",
|
|
77
|
+
"pytest>=9.1.1",
|
|
78
|
+
"pytest-asyncio>=1.4.0",
|
|
79
|
+
"pytest-cov>=7.1.0",
|
|
80
|
+
"ruff>=0.17.2",
|
|
81
|
+
"boto3>=1.35",
|
|
82
|
+
"chromadb>=1.5.9",
|
|
83
|
+
"fastapi>=0.162.0",
|
|
84
|
+
"python-multipart>=0.0.32",
|
|
85
|
+
"streamlit>=1.66.1",
|
|
86
|
+
"uvicorn[standard]>=0.54.0",
|
|
87
|
+
]
|
|
88
|
+
|
|
89
|
+
[tool.ruff]
|
|
90
|
+
line-length = 100
|
|
91
|
+
target-version = "py311"
|
|
92
|
+
|
|
93
|
+
[tool.ruff.lint]
|
|
94
|
+
select = [
|
|
95
|
+
"E",
|
|
96
|
+
"F",
|
|
97
|
+
"I",
|
|
98
|
+
"UP",
|
|
99
|
+
"B",
|
|
100
|
+
"SIM",
|
|
101
|
+
]
|
|
102
|
+
|
|
103
|
+
[tool.pytest.ini_options]
|
|
104
|
+
testpaths = ["tests"]
|
|
105
|
+
asyncio_mode = "auto"
|
|
106
|
+
pythonpath = ["tests"]
|
|
107
|
+
python_functions = [
|
|
108
|
+
"test_*",
|
|
109
|
+
"*_should_*",
|
|
110
|
+
]
|
|
111
|
+
|
|
112
|
+
[tool.coverage.run]
|
|
113
|
+
source = ["pydantic_rag_agent"]
|
|
114
|
+
branch = true
|
|
115
|
+
|
|
116
|
+
[tool.coverage.report]
|
|
117
|
+
fail_under = 85
|
|
118
|
+
exclude_lines = [
|
|
119
|
+
"pragma: no cover",
|
|
120
|
+
"if TYPE_CHECKING:",
|
|
121
|
+
"raise NotImplementedError",
|
|
122
|
+
"if __name__ == .__main__.:",
|
|
123
|
+
]
|
|
124
|
+
omit = ["*/ui/*"]
|
|
125
|
+
|
|
126
|
+
[tool.mypy]
|
|
127
|
+
python_version = "3.11"
|
|
128
|
+
disallow_untyped_defs = true
|
|
129
|
+
warn_unused_ignores = true
|
|
130
|
+
warn_redundant_casts = true
|
|
131
|
+
plugins = ["pydantic.mypy"]
|
|
132
|
+
|
|
133
|
+
[[tool.mypy.overrides]]
|
|
134
|
+
module = [
|
|
135
|
+
"pypdfium2",
|
|
136
|
+
"pypdfium2.*",
|
|
137
|
+
]
|
|
138
|
+
ignore_missing_imports = true
|