ragdoll-ai 0.2.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- ragdoll_ai-0.2.0/LICENSE +21 -0
- ragdoll_ai-0.2.0/PKG-INFO +220 -0
- ragdoll_ai-0.2.0/README.md +188 -0
- ragdoll_ai-0.2.0/pyproject.toml +68 -0
- ragdoll_ai-0.2.0/setup.cfg +4 -0
- ragdoll_ai-0.2.0/src/ragdoll/__init__.py +6 -0
- ragdoll_ai-0.2.0/src/ragdoll/api.py +57 -0
- ragdoll_ai-0.2.0/src/ragdoll/cli.py +399 -0
- ragdoll_ai-0.2.0/src/ragdoll/config.py +148 -0
- ragdoll_ai-0.2.0/src/ragdoll/ingest/__init__.py +1 -0
- ragdoll_ai-0.2.0/src/ragdoll/ingest/chunker.py +176 -0
- ragdoll_ai-0.2.0/src/ragdoll/ingest/code.py +216 -0
- ragdoll_ai-0.2.0/src/ragdoll/ingest/jira.py +86 -0
- ragdoll_ai-0.2.0/src/ragdoll/ingest/pdf.py +57 -0
- ragdoll_ai-0.2.0/src/ragdoll/llm/__init__.py +1 -0
- ragdoll_ai-0.2.0/src/ragdoll/llm/ollama.py +16 -0
- ragdoll_ai-0.2.0/src/ragdoll/query/__init__.py +1 -0
- ragdoll_ai-0.2.0/src/ragdoll/query/rag.py +167 -0
- ragdoll_ai-0.2.0/src/ragdoll/query/retriever.py +99 -0
- ragdoll_ai-0.2.0/src/ragdoll/store/__init__.py +1 -0
- ragdoll_ai-0.2.0/src/ragdoll/store/vectordb.py +62 -0
- ragdoll_ai-0.2.0/src/ragdoll/ui.py +37 -0
- ragdoll_ai-0.2.0/src/ragdoll_ai.egg-info/PKG-INFO +220 -0
- ragdoll_ai-0.2.0/src/ragdoll_ai.egg-info/SOURCES.txt +27 -0
- ragdoll_ai-0.2.0/src/ragdoll_ai.egg-info/dependency_links.txt +1 -0
- ragdoll_ai-0.2.0/src/ragdoll_ai.egg-info/entry_points.txt +2 -0
- ragdoll_ai-0.2.0/src/ragdoll_ai.egg-info/requires.txt +22 -0
- ragdoll_ai-0.2.0/src/ragdoll_ai.egg-info/top_level.txt +1 -0
- ragdoll_ai-0.2.0/tests/test_chunker.py +52 -0
ragdoll_ai-0.2.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Rui Xue
|
|
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,220 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: ragdoll-ai
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Summary: Retrieval-Augmented Generation Driven by Offline Local LLMs โ a fully-local RAG system for JIRA tickets, code, and PDFs, powered by Ollama.
|
|
5
|
+
Author-email: Rui Xue <rx.astro@gmail.com>
|
|
6
|
+
License: MIT
|
|
7
|
+
Requires-Python: >=3.11
|
|
8
|
+
Description-Content-Type: text/markdown
|
|
9
|
+
License-File: LICENSE
|
|
10
|
+
Requires-Dist: chromadb>=0.6
|
|
11
|
+
Requires-Dist: pymupdf>=1.25
|
|
12
|
+
Requires-Dist: jira>=3.8
|
|
13
|
+
Requires-Dist: click>=8.1
|
|
14
|
+
Requires-Dist: rich>=13.0
|
|
15
|
+
Requires-Dist: pydantic-settings>=2.0
|
|
16
|
+
Requires-Dist: httpx>=0.27
|
|
17
|
+
Requires-Dist: pytest
|
|
18
|
+
Requires-Dist: fastapi
|
|
19
|
+
Requires-Dist: uvicorn
|
|
20
|
+
Requires-Dist: gradio
|
|
21
|
+
Requires-Dist: llama-index
|
|
22
|
+
Requires-Dist: llama-index-vector-stores-chroma
|
|
23
|
+
Requires-Dist: llama-index-llms-ollama
|
|
24
|
+
Requires-Dist: llama-index-embeddings-ollama
|
|
25
|
+
Requires-Dist: llama-index-readers-jira
|
|
26
|
+
Provides-Extra: docs
|
|
27
|
+
Requires-Dist: sphinx>=7.0; extra == "docs"
|
|
28
|
+
Requires-Dist: furo; extra == "docs"
|
|
29
|
+
Requires-Dist: myst-parser>=3.0; extra == "docs"
|
|
30
|
+
Requires-Dist: sphinx-autodoc-typehints; extra == "docs"
|
|
31
|
+
Dynamic: license-file
|
|
32
|
+
|
|
33
|
+
# ๐งถ Ragdoll
|
|
34
|
+
|
|
35
|
+
**Retrieval-Augmented Generation Driven by Offline Local LLMs**
|
|
36
|
+
|
|
37
|
+
A fully-local RAG system that ingests JIRA tickets, PDF documents, and Python
|
|
38
|
+
source code, indexes them for semantic search, and connects to a local LLM via
|
|
39
|
+
[Ollama](https://ollama.ai) for interactive Q&A, summarization, and chat.
|
|
40
|
+
|
|
41
|
+
> **Privacy-first:** All data stays on your machine โ nothing is sent to
|
|
42
|
+
> external services.
|
|
43
|
+
|
|
44
|
+
## Prerequisites
|
|
45
|
+
|
|
46
|
+
- **Python 3.12+**
|
|
47
|
+
- **[Ollama](https://ollama.ai)** running locally with:
|
|
48
|
+
- An embedding model (e.g. `nomic-embed-text`)
|
|
49
|
+
- A chat model (e.g. `gpt-oss:20b`, `deepseek-r1:32b`)
|
|
50
|
+
- **[pixi](https://pixi.sh)** for environment management
|
|
51
|
+
|
|
52
|
+
## Quick Start
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
# Clone and enter the project
|
|
56
|
+
cd ragdoll
|
|
57
|
+
|
|
58
|
+
# Install with pixi (creates isolated env + editable install)
|
|
59
|
+
pixi install
|
|
60
|
+
|
|
61
|
+
# Set up user-level configuration
|
|
62
|
+
mkdir -p ~/.ragdoll && chmod 700 ~/.ragdoll
|
|
63
|
+
cat > ~/.ragdoll/config.toml << 'EOF'
|
|
64
|
+
jira_url = "https://your-jira.example.com"
|
|
65
|
+
jira_user = "your.user"
|
|
66
|
+
jira_token = "YOUR_PAT_TOKEN"
|
|
67
|
+
jira_auth_method = "pat" # "pat" for JIRA Data Center, "basic" for Cloud
|
|
68
|
+
EOF
|
|
69
|
+
chmod 600 ~/.ragdoll/config.toml
|
|
70
|
+
|
|
71
|
+
# Check everything is connected
|
|
72
|
+
pixi run ragdoll status
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
## Usage
|
|
76
|
+
|
|
77
|
+
### Ingest Data
|
|
78
|
+
|
|
79
|
+
```bash
|
|
80
|
+
# Ingest PDF files or directories
|
|
81
|
+
pixi run ragdoll ingest pdf ./docs/technical_handbook.pdf
|
|
82
|
+
pixi run ragdoll ingest pdf ./reports/
|
|
83
|
+
|
|
84
|
+
# Ingest JIRA issues via JQL
|
|
85
|
+
pixi run ragdoll ingest jira --jql "project = CAS AND updated >= -30d"
|
|
86
|
+
pixi run ragdoll ingest jira --jql "project = PIPE AND updated >= -60d" --max-results 100
|
|
87
|
+
|
|
88
|
+
# Ingest from a different JIRA instance (multi-site)
|
|
89
|
+
pixi run ragdoll ingest jira \
|
|
90
|
+
--url https://other-jira.example.com \
|
|
91
|
+
--token OTHER_PAT \
|
|
92
|
+
--jql "project = EXT AND updated >= -30d"
|
|
93
|
+
|
|
94
|
+
# Ingest Python source code (AST-parsed per function/class)
|
|
95
|
+
pixi run ragdoll ingest code ./src/
|
|
96
|
+
pixi run ragdoll ingest code ./path/to/project/
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
#### Reingesting Data (LlamaIndex Update)
|
|
100
|
+
If you are upgrading from an older version of `ragdoll` to the LlamaIndex-backed version, your existing ChromaDB data is fully backward compatible. However, it is highly recommended to wipe the old index and reingest your data to take advantage of LlamaIndex's superior semantic chunking (which splits by sentences instead of fixed character limits).
|
|
101
|
+
|
|
102
|
+
To clear your database and start fresh:
|
|
103
|
+
```bash
|
|
104
|
+
# Delete the old ChromaDB collection
|
|
105
|
+
rm -rf ~/.ragdoll/data/chroma
|
|
106
|
+
|
|
107
|
+
# Re-run your ingestion commands
|
|
108
|
+
pixi run ragdoll ingest jira --jql "project = CAS AND updated >= -30d"
|
|
109
|
+
pixi run ragdoll ingest pdf ./docs/
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
### Search
|
|
113
|
+
|
|
114
|
+
```bash
|
|
115
|
+
# Semantic search across all ingested data
|
|
116
|
+
pixi run ragdoll search "tclean performance regression"
|
|
117
|
+
pixi run ragdoll search "AsdmStMan lazy import" --source jira
|
|
118
|
+
pixi run ragdoll search "calibration pipeline" --source pdf -n 5
|
|
119
|
+
pixi run ragdoll search "embedding function" --source code
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
### Summarize
|
|
123
|
+
|
|
124
|
+
```bash
|
|
125
|
+
# Summarize a topic from ingested data
|
|
126
|
+
pixi run ragdoll summarize "What are the known issues with AsdmStMan?"
|
|
127
|
+
pixi run ragdoll summarize "tclean parallelization" --source jira
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
### Interactive Chat
|
|
131
|
+
|
|
132
|
+
```bash
|
|
133
|
+
# Start an interactive RAG chat session
|
|
134
|
+
pixi run ragdoll chat
|
|
135
|
+
pixi run ragdoll chat --source jira # only use JIRA context
|
|
136
|
+
pixi run ragdoll chat --source code # only use source code context
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
Chat features:
|
|
140
|
+
- **Persistent history** โ arrow-up recalls previous questions across sessions
|
|
141
|
+
(stored in `~/.ragdoll/chat_history`)
|
|
142
|
+
- **Line editing** โ full readline support (backspace, arrows, Home/End)
|
|
143
|
+
- **Multi-turn** โ context accumulates within a session
|
|
144
|
+
|
|
145
|
+
## Configuration
|
|
146
|
+
|
|
147
|
+
Ragdoll uses a **4-layer precedence** configuration strategy:
|
|
148
|
+
|
|
149
|
+
| Priority | Source | Purpose |
|
|
150
|
+
|----------|--------|---------|
|
|
151
|
+
| 1 (highest) | `RAGDOLL_*` environment variables | CI/ephemeral overrides |
|
|
152
|
+
| 2a | `./ragdoll.toml` in the project directory | Project-level settings |
|
|
153
|
+
| 2b | `./.env` in the project directory | Project-level secrets |
|
|
154
|
+
| 3 | `~/.ragdoll/config.toml` | User-level defaults & credentials |
|
|
155
|
+
| 4 (lowest) | Package defaults | Hardcoded fallbacks |
|
|
156
|
+
|
|
157
|
+
### Settings Reference
|
|
158
|
+
|
|
159
|
+
| Variable / TOML key | Default | Description |
|
|
160
|
+
|---------------------|---------|-------------|
|
|
161
|
+
| `jira_url` | โ | JIRA server URL |
|
|
162
|
+
| `jira_user` | โ | JIRA username |
|
|
163
|
+
| `jira_token` | โ | JIRA API token or PAT |
|
|
164
|
+
| `jira_auth_method` | `pat` | `"pat"` for Data Center, `"basic"` for Cloud |
|
|
165
|
+
| `jira_batch_size` | `50` | Issues per API request |
|
|
166
|
+
| `ollama_host` | `http://localhost:11434` | Ollama API endpoint |
|
|
167
|
+
| `embed_model` | `nomic-embed-text` | Embedding model |
|
|
168
|
+
| `chat_model` | `gpt-oss:20b` | Chat / generation model |
|
|
169
|
+
| `temperature` | `0.3` | LLM sampling temperature |
|
|
170
|
+
| `data_dir` | `~/.ragdoll/data` | ChromaDB storage directory |
|
|
171
|
+
| `collection_name` | `ragdoll` | ChromaDB collection name |
|
|
172
|
+
| `chunk_size` | `1000` | Characters per chunk |
|
|
173
|
+
| `chunk_overlap` | `200` | Overlap between consecutive chunks |
|
|
174
|
+
| `top_k` | `20` | Default retrieval count |
|
|
175
|
+
|
|
176
|
+
## Architecture
|
|
177
|
+
|
|
178
|
+
```
|
|
179
|
+
Source Data Pipeline Storage
|
|
180
|
+
โโโโโโโโโโโ โโโโโโโโ โโโโโโโ
|
|
181
|
+
PDF files โโ
|
|
182
|
+
JIRA tickets โโผโโ Ingestor โ Chunker โ Embedder โ ChromaDB
|
|
183
|
+
Python code โโ (AST-aware) (Ollama) (local)
|
|
184
|
+
โ
|
|
185
|
+
Query Flow โ
|
|
186
|
+
โโโโโโโโโโ โ
|
|
187
|
+
CLI / Chat โ Embed query โ Retriever โโโโโโโโโโโโโโโโโโโโ
|
|
188
|
+
โ
|
|
189
|
+
LLM (Ollama) โ Streamed answer
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
### Data Sources
|
|
193
|
+
|
|
194
|
+
| Source | Module | Strategy |
|
|
195
|
+
|--------|--------|----------|
|
|
196
|
+
| **PDF** | `ragdoll.ingest.pdf` | PyMuPDF text extraction โ recursive character splitter |
|
|
197
|
+
| **JIRA** | `ragdoll.ingest.jira` | REST API with JQL โ structured text per issue |
|
|
198
|
+
| **Code** | `ragdoll.ingest.code` | AST parsing โ one Document per function/class/module docstring |
|
|
199
|
+
|
|
200
|
+
### Key Components
|
|
201
|
+
|
|
202
|
+
- **Config** (`ragdoll.config`) โ Pydantic Settings with 4-layer precedence
|
|
203
|
+
- **Chunker** (`ragdoll.ingest.chunker`) โ Recursive character text splitter
|
|
204
|
+
- **Embedder** (`ragdoll.llm.ollama`) โ Ollama HTTP client for embeddings and generation
|
|
205
|
+
- **Vector Store** (`ragdoll.store.vectordb`) โ ChromaDB with cosine similarity
|
|
206
|
+
- **Retriever** (`ragdoll.query.retriever`) โ Semantic search with source filtering
|
|
207
|
+
- **RAG Chain** (`ragdoll.query.rag`) โ Context-augmented generation and chat
|
|
208
|
+
- **CLI** (`ragdoll.cli`) โ Click-based interface with Rich formatting
|
|
209
|
+
|
|
210
|
+
## Documentation
|
|
211
|
+
|
|
212
|
+
Full documentation is available under `docs/` and can be built with Sphinx:
|
|
213
|
+
|
|
214
|
+
```bash
|
|
215
|
+
pixi run docs
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
## License
|
|
219
|
+
|
|
220
|
+
MIT
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
# ๐งถ Ragdoll
|
|
2
|
+
|
|
3
|
+
**Retrieval-Augmented Generation Driven by Offline Local LLMs**
|
|
4
|
+
|
|
5
|
+
A fully-local RAG system that ingests JIRA tickets, PDF documents, and Python
|
|
6
|
+
source code, indexes them for semantic search, and connects to a local LLM via
|
|
7
|
+
[Ollama](https://ollama.ai) for interactive Q&A, summarization, and chat.
|
|
8
|
+
|
|
9
|
+
> **Privacy-first:** All data stays on your machine โ nothing is sent to
|
|
10
|
+
> external services.
|
|
11
|
+
|
|
12
|
+
## Prerequisites
|
|
13
|
+
|
|
14
|
+
- **Python 3.12+**
|
|
15
|
+
- **[Ollama](https://ollama.ai)** running locally with:
|
|
16
|
+
- An embedding model (e.g. `nomic-embed-text`)
|
|
17
|
+
- A chat model (e.g. `gpt-oss:20b`, `deepseek-r1:32b`)
|
|
18
|
+
- **[pixi](https://pixi.sh)** for environment management
|
|
19
|
+
|
|
20
|
+
## Quick Start
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
# Clone and enter the project
|
|
24
|
+
cd ragdoll
|
|
25
|
+
|
|
26
|
+
# Install with pixi (creates isolated env + editable install)
|
|
27
|
+
pixi install
|
|
28
|
+
|
|
29
|
+
# Set up user-level configuration
|
|
30
|
+
mkdir -p ~/.ragdoll && chmod 700 ~/.ragdoll
|
|
31
|
+
cat > ~/.ragdoll/config.toml << 'EOF'
|
|
32
|
+
jira_url = "https://your-jira.example.com"
|
|
33
|
+
jira_user = "your.user"
|
|
34
|
+
jira_token = "YOUR_PAT_TOKEN"
|
|
35
|
+
jira_auth_method = "pat" # "pat" for JIRA Data Center, "basic" for Cloud
|
|
36
|
+
EOF
|
|
37
|
+
chmod 600 ~/.ragdoll/config.toml
|
|
38
|
+
|
|
39
|
+
# Check everything is connected
|
|
40
|
+
pixi run ragdoll status
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
## Usage
|
|
44
|
+
|
|
45
|
+
### Ingest Data
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
# Ingest PDF files or directories
|
|
49
|
+
pixi run ragdoll ingest pdf ./docs/technical_handbook.pdf
|
|
50
|
+
pixi run ragdoll ingest pdf ./reports/
|
|
51
|
+
|
|
52
|
+
# Ingest JIRA issues via JQL
|
|
53
|
+
pixi run ragdoll ingest jira --jql "project = CAS AND updated >= -30d"
|
|
54
|
+
pixi run ragdoll ingest jira --jql "project = PIPE AND updated >= -60d" --max-results 100
|
|
55
|
+
|
|
56
|
+
# Ingest from a different JIRA instance (multi-site)
|
|
57
|
+
pixi run ragdoll ingest jira \
|
|
58
|
+
--url https://other-jira.example.com \
|
|
59
|
+
--token OTHER_PAT \
|
|
60
|
+
--jql "project = EXT AND updated >= -30d"
|
|
61
|
+
|
|
62
|
+
# Ingest Python source code (AST-parsed per function/class)
|
|
63
|
+
pixi run ragdoll ingest code ./src/
|
|
64
|
+
pixi run ragdoll ingest code ./path/to/project/
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
#### Reingesting Data (LlamaIndex Update)
|
|
68
|
+
If you are upgrading from an older version of `ragdoll` to the LlamaIndex-backed version, your existing ChromaDB data is fully backward compatible. However, it is highly recommended to wipe the old index and reingest your data to take advantage of LlamaIndex's superior semantic chunking (which splits by sentences instead of fixed character limits).
|
|
69
|
+
|
|
70
|
+
To clear your database and start fresh:
|
|
71
|
+
```bash
|
|
72
|
+
# Delete the old ChromaDB collection
|
|
73
|
+
rm -rf ~/.ragdoll/data/chroma
|
|
74
|
+
|
|
75
|
+
# Re-run your ingestion commands
|
|
76
|
+
pixi run ragdoll ingest jira --jql "project = CAS AND updated >= -30d"
|
|
77
|
+
pixi run ragdoll ingest pdf ./docs/
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
### Search
|
|
81
|
+
|
|
82
|
+
```bash
|
|
83
|
+
# Semantic search across all ingested data
|
|
84
|
+
pixi run ragdoll search "tclean performance regression"
|
|
85
|
+
pixi run ragdoll search "AsdmStMan lazy import" --source jira
|
|
86
|
+
pixi run ragdoll search "calibration pipeline" --source pdf -n 5
|
|
87
|
+
pixi run ragdoll search "embedding function" --source code
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
### Summarize
|
|
91
|
+
|
|
92
|
+
```bash
|
|
93
|
+
# Summarize a topic from ingested data
|
|
94
|
+
pixi run ragdoll summarize "What are the known issues with AsdmStMan?"
|
|
95
|
+
pixi run ragdoll summarize "tclean parallelization" --source jira
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
### Interactive Chat
|
|
99
|
+
|
|
100
|
+
```bash
|
|
101
|
+
# Start an interactive RAG chat session
|
|
102
|
+
pixi run ragdoll chat
|
|
103
|
+
pixi run ragdoll chat --source jira # only use JIRA context
|
|
104
|
+
pixi run ragdoll chat --source code # only use source code context
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
Chat features:
|
|
108
|
+
- **Persistent history** โ arrow-up recalls previous questions across sessions
|
|
109
|
+
(stored in `~/.ragdoll/chat_history`)
|
|
110
|
+
- **Line editing** โ full readline support (backspace, arrows, Home/End)
|
|
111
|
+
- **Multi-turn** โ context accumulates within a session
|
|
112
|
+
|
|
113
|
+
## Configuration
|
|
114
|
+
|
|
115
|
+
Ragdoll uses a **4-layer precedence** configuration strategy:
|
|
116
|
+
|
|
117
|
+
| Priority | Source | Purpose |
|
|
118
|
+
|----------|--------|---------|
|
|
119
|
+
| 1 (highest) | `RAGDOLL_*` environment variables | CI/ephemeral overrides |
|
|
120
|
+
| 2a | `./ragdoll.toml` in the project directory | Project-level settings |
|
|
121
|
+
| 2b | `./.env` in the project directory | Project-level secrets |
|
|
122
|
+
| 3 | `~/.ragdoll/config.toml` | User-level defaults & credentials |
|
|
123
|
+
| 4 (lowest) | Package defaults | Hardcoded fallbacks |
|
|
124
|
+
|
|
125
|
+
### Settings Reference
|
|
126
|
+
|
|
127
|
+
| Variable / TOML key | Default | Description |
|
|
128
|
+
|---------------------|---------|-------------|
|
|
129
|
+
| `jira_url` | โ | JIRA server URL |
|
|
130
|
+
| `jira_user` | โ | JIRA username |
|
|
131
|
+
| `jira_token` | โ | JIRA API token or PAT |
|
|
132
|
+
| `jira_auth_method` | `pat` | `"pat"` for Data Center, `"basic"` for Cloud |
|
|
133
|
+
| `jira_batch_size` | `50` | Issues per API request |
|
|
134
|
+
| `ollama_host` | `http://localhost:11434` | Ollama API endpoint |
|
|
135
|
+
| `embed_model` | `nomic-embed-text` | Embedding model |
|
|
136
|
+
| `chat_model` | `gpt-oss:20b` | Chat / generation model |
|
|
137
|
+
| `temperature` | `0.3` | LLM sampling temperature |
|
|
138
|
+
| `data_dir` | `~/.ragdoll/data` | ChromaDB storage directory |
|
|
139
|
+
| `collection_name` | `ragdoll` | ChromaDB collection name |
|
|
140
|
+
| `chunk_size` | `1000` | Characters per chunk |
|
|
141
|
+
| `chunk_overlap` | `200` | Overlap between consecutive chunks |
|
|
142
|
+
| `top_k` | `20` | Default retrieval count |
|
|
143
|
+
|
|
144
|
+
## Architecture
|
|
145
|
+
|
|
146
|
+
```
|
|
147
|
+
Source Data Pipeline Storage
|
|
148
|
+
โโโโโโโโโโโ โโโโโโโโ โโโโโโโ
|
|
149
|
+
PDF files โโ
|
|
150
|
+
JIRA tickets โโผโโ Ingestor โ Chunker โ Embedder โ ChromaDB
|
|
151
|
+
Python code โโ (AST-aware) (Ollama) (local)
|
|
152
|
+
โ
|
|
153
|
+
Query Flow โ
|
|
154
|
+
โโโโโโโโโโ โ
|
|
155
|
+
CLI / Chat โ Embed query โ Retriever โโโโโโโโโโโโโโโโโโโโ
|
|
156
|
+
โ
|
|
157
|
+
LLM (Ollama) โ Streamed answer
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
### Data Sources
|
|
161
|
+
|
|
162
|
+
| Source | Module | Strategy |
|
|
163
|
+
|--------|--------|----------|
|
|
164
|
+
| **PDF** | `ragdoll.ingest.pdf` | PyMuPDF text extraction โ recursive character splitter |
|
|
165
|
+
| **JIRA** | `ragdoll.ingest.jira` | REST API with JQL โ structured text per issue |
|
|
166
|
+
| **Code** | `ragdoll.ingest.code` | AST parsing โ one Document per function/class/module docstring |
|
|
167
|
+
|
|
168
|
+
### Key Components
|
|
169
|
+
|
|
170
|
+
- **Config** (`ragdoll.config`) โ Pydantic Settings with 4-layer precedence
|
|
171
|
+
- **Chunker** (`ragdoll.ingest.chunker`) โ Recursive character text splitter
|
|
172
|
+
- **Embedder** (`ragdoll.llm.ollama`) โ Ollama HTTP client for embeddings and generation
|
|
173
|
+
- **Vector Store** (`ragdoll.store.vectordb`) โ ChromaDB with cosine similarity
|
|
174
|
+
- **Retriever** (`ragdoll.query.retriever`) โ Semantic search with source filtering
|
|
175
|
+
- **RAG Chain** (`ragdoll.query.rag`) โ Context-augmented generation and chat
|
|
176
|
+
- **CLI** (`ragdoll.cli`) โ Click-based interface with Rich formatting
|
|
177
|
+
|
|
178
|
+
## Documentation
|
|
179
|
+
|
|
180
|
+
Full documentation is available under `docs/` and can be built with Sphinx:
|
|
181
|
+
|
|
182
|
+
```bash
|
|
183
|
+
pixi run docs
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
## License
|
|
187
|
+
|
|
188
|
+
MIT
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=61.0"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "ragdoll-ai"
|
|
7
|
+
version = "0.2.0"
|
|
8
|
+
description = "Retrieval-Augmented Generation Driven by Offline Local LLMs โ a fully-local RAG system for JIRA tickets, code, and PDFs, powered by Ollama."
|
|
9
|
+
authors = [{name = "Rui Xue", email = "rx.astro@gmail.com"}]
|
|
10
|
+
requires-python = ">= 3.11"
|
|
11
|
+
license = {text = "MIT"}
|
|
12
|
+
readme = "README.md"
|
|
13
|
+
dependencies = [
|
|
14
|
+
"chromadb >= 0.6",
|
|
15
|
+
"pymupdf >= 1.25",
|
|
16
|
+
"jira >= 3.8",
|
|
17
|
+
"click >= 8.1",
|
|
18
|
+
"rich >= 13.0",
|
|
19
|
+
"pydantic-settings >= 2.0",
|
|
20
|
+
"httpx >= 0.27",
|
|
21
|
+
"pytest",
|
|
22
|
+
"fastapi",
|
|
23
|
+
"uvicorn",
|
|
24
|
+
"gradio", "llama-index", "llama-index-vector-stores-chroma", "llama-index-llms-ollama", "llama-index-embeddings-ollama", "llama-index-readers-jira",
|
|
25
|
+
]
|
|
26
|
+
|
|
27
|
+
[project.optional-dependencies]
|
|
28
|
+
docs = [
|
|
29
|
+
"sphinx >= 7.0",
|
|
30
|
+
"furo",
|
|
31
|
+
"myst-parser >= 3.0",
|
|
32
|
+
"sphinx-autodoc-typehints",
|
|
33
|
+
]
|
|
34
|
+
|
|
35
|
+
[project.scripts]
|
|
36
|
+
ragdoll = "ragdoll.cli:cli"
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
[tool.setuptools.packages.find]
|
|
41
|
+
where = ["src"]
|
|
42
|
+
|
|
43
|
+
[tool.pixi.workspace]
|
|
44
|
+
channels = ["conda-forge"]
|
|
45
|
+
platforms = ["linux-64", "osx-arm64"]
|
|
46
|
+
|
|
47
|
+
[tool.pixi.pypi-dependencies]
|
|
48
|
+
ragdoll = { path = ".", editable = true }
|
|
49
|
+
sphinx = ">=7.0"
|
|
50
|
+
furo = "*"
|
|
51
|
+
myst-parser = ">=3.0"
|
|
52
|
+
sphinx-autodoc-typehints = "*"
|
|
53
|
+
|
|
54
|
+
[tool.pixi.tasks]
|
|
55
|
+
ragdoll = "ragdoll"
|
|
56
|
+
ingest-pdf = { cmd = "ragdoll ingest pdf", description = "Ingest PDF documents" }
|
|
57
|
+
search = { cmd = "ragdoll search", description = "Semantic search" }
|
|
58
|
+
chat = { cmd = "ragdoll chat", description = "Interactive RAG chat" }
|
|
59
|
+
docs = { cmd = "sphinx-build -b html docs docs/_build/html", description = "Build Sphinx documentation" }
|
|
60
|
+
|
|
61
|
+
[tool.pixi.dependencies]
|
|
62
|
+
python = ">=3.12"
|
|
63
|
+
gradio = "*"
|
|
64
|
+
fastapi = "*"
|
|
65
|
+
uvicorn = "*"
|
|
66
|
+
|
|
67
|
+
[tool.pytest.ini_options]
|
|
68
|
+
testpaths = ["tests"]
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import json
|
|
2
|
+
from fastapi import FastAPI, Request
|
|
3
|
+
from fastapi.responses import StreamingResponse
|
|
4
|
+
|
|
5
|
+
from ragdoll.query.rag import chat_with_context
|
|
6
|
+
|
|
7
|
+
app = FastAPI(title="Ragdoll API", description="OpenAI-compatible local RAG API")
|
|
8
|
+
|
|
9
|
+
@app.get("/v1/models")
|
|
10
|
+
async def list_models():
|
|
11
|
+
"""Returns a dummy model list so Open WebUI can populate its model dropdown."""
|
|
12
|
+
return {
|
|
13
|
+
"object": "list",
|
|
14
|
+
"data": [
|
|
15
|
+
{
|
|
16
|
+
"id": "ragdoll-context-engine",
|
|
17
|
+
"object": "model",
|
|
18
|
+
"created": 1700000000,
|
|
19
|
+
"owned_by": "ragdoll",
|
|
20
|
+
}
|
|
21
|
+
]
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
@app.post("/v1/chat/completions")
|
|
25
|
+
async def chat_completions(request: Request):
|
|
26
|
+
"""OpenAI-compatible endpoint for chat completions.
|
|
27
|
+
|
|
28
|
+
Allows drop-in integration with Open WebUI and other AI frontends.
|
|
29
|
+
|
|
30
|
+
Args:
|
|
31
|
+
request (Request): The incoming FastAPI request object containing the JSON payload with messages.
|
|
32
|
+
|
|
33
|
+
Returns:
|
|
34
|
+
StreamingResponse: An SSE streaming response containing the generated text chunks.
|
|
35
|
+
"""
|
|
36
|
+
body = await request.json()
|
|
37
|
+
messages = body.get("messages", [])
|
|
38
|
+
|
|
39
|
+
# We only support streaming for now, to provide the best UX
|
|
40
|
+
# If the client didn't request a stream, we could collect it, but streaming is standard for Web UIs.
|
|
41
|
+
|
|
42
|
+
def token_generator():
|
|
43
|
+
# Chat_with_context expects standard OpenAI message format
|
|
44
|
+
for chunk in chat_with_context(messages, stream=True):
|
|
45
|
+
# Format the output as an OpenAI-compatible SSE stream
|
|
46
|
+
response_obj = {
|
|
47
|
+
"choices": [{"delta": {"content": chunk}}]
|
|
48
|
+
}
|
|
49
|
+
yield f"data: {json.dumps(response_obj)}\n\n"
|
|
50
|
+
yield "data: [DONE]\n\n"
|
|
51
|
+
|
|
52
|
+
return StreamingResponse(token_generator(), media_type="text/event-stream")
|
|
53
|
+
|
|
54
|
+
def run_server(host: str = "0.0.0.0", port: int = 8000):
|
|
55
|
+
"""Start the FastAPI uvicorn server."""
|
|
56
|
+
import uvicorn
|
|
57
|
+
uvicorn.run(app, host=host, port=port)
|