ragit 0.7.5__py3-none-any.whl → 0.8.1__py3-none-any.whl

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.
@@ -1,553 +0,0 @@
1
- Metadata-Version: 2.4
2
- Name: ragit
3
- Version: 0.7.5
4
- Summary: Automatic RAG Pattern Optimization Engine
5
- Author: RODMENA LIMITED
6
- Maintainer-email: RODMENA LIMITED <info@rodmena.co.uk>
7
- License-Expression: Apache-2.0
8
- Project-URL: Homepage, https://github.com/rodmena-limited/ragit
9
- Project-URL: Repository, https://github.com/rodmena-limited/ragit
10
- Project-URL: Issues, https://github.com/rodmena-limited/ragit/issues
11
- Keywords: AI,RAG,LLM,GenAI,Optimization,Ollama
12
- Classifier: Development Status :: 2 - Pre-Alpha
13
- Classifier: Natural Language :: English
14
- Classifier: Programming Language :: Python :: 3.12
15
- Classifier: Programming Language :: Python :: 3.13
16
- Classifier: Programming Language :: Python :: 3.14
17
- Classifier: Operating System :: MacOS :: MacOS X
18
- Classifier: Operating System :: POSIX :: Linux
19
- Requires-Python: <3.14,>=3.12
20
- Description-Content-Type: text/markdown
21
- License-File: LICENSE
22
- Requires-Dist: requests>=2.31.0
23
- Requires-Dist: numpy>=1.26.0
24
- Requires-Dist: pandas>=2.2.0
25
- Requires-Dist: pydantic>=2.0.0
26
- Requires-Dist: python-dotenv>=1.0.0
27
- Requires-Dist: scikit-learn>=1.5.0
28
- Requires-Dist: tqdm>=4.66.0
29
- Requires-Dist: trio>=0.24.0
30
- Requires-Dist: httpx>=0.27.0
31
- Provides-Extra: dev
32
- Requires-Dist: ragit[test]; extra == "dev"
33
- Requires-Dist: pytest; extra == "dev"
34
- Requires-Dist: pytest-cov; extra == "dev"
35
- Requires-Dist: issuedb[web]; extra == "dev"
36
- Requires-Dist: ruff; extra == "dev"
37
- Requires-Dist: mypy; extra == "dev"
38
- Provides-Extra: test
39
- Requires-Dist: pytest; extra == "test"
40
- Requires-Dist: pytest-cov; extra == "test"
41
- Requires-Dist: pytest-mock; extra == "test"
42
- Provides-Extra: docs
43
- Requires-Dist: sphinx>=7.0; extra == "docs"
44
- Requires-Dist: sphinx-rtd-theme>=2.0; extra == "docs"
45
- Requires-Dist: sphinx-copybutton>=0.5; extra == "docs"
46
- Dynamic: license-file
47
-
48
- # ragit
49
-
50
- A Python toolkit for building Retrieval-Augmented Generation (RAG) applications. Ragit provides document loading, chunking, vector search, and LLM integration out of the box, allowing you to build document Q&A systems and code generators with minimal boilerplate.
51
-
52
- ## Table of Contents
53
-
54
- 1. [Installation](#installation)
55
- 2. [Configuration](#configuration)
56
- 3. [Tutorial: Using Ragit](#tutorial-using-ragit)
57
- - [Loading Documents](#loading-documents)
58
- - [The RAGAssistant Class](#the-ragassistant-class)
59
- - [Asking Questions](#asking-questions)
60
- - [Generating Code](#generating-code)
61
- - [Custom Retrieval](#custom-retrieval)
62
- 4. [Tutorial: Platform Integration](#tutorial-platform-integration)
63
- - [Flask Integration](#flask-integration)
64
- - [FastAPI Integration](#fastapi-integration)
65
- - [Command-Line Tools](#command-line-tools)
66
- - [Batch Processing](#batch-processing)
67
- 5. [Advanced: Hyperparameter Optimization](#advanced-hyperparameter-optimization)
68
- 6. [API Reference](#api-reference)
69
- 7. [License](#license)
70
-
71
- ## Installation
72
-
73
- ```bash
74
- pip install ragit
75
- ```
76
-
77
- Ragit requires an Ollama-compatible API for embeddings and LLM inference. You can use:
78
- - A local Ollama instance (https://ollama.ai)
79
- - A cloud-hosted Ollama API
80
- - Any OpenAI-compatible API endpoint
81
-
82
- ## Configuration
83
-
84
- Ragit reads configuration from environment variables. Create a `.env` file in your project root:
85
-
86
- ```bash
87
- # LLM API (cloud or local)
88
- OLLAMA_BASE_URL=https://your-ollama-api.com
89
- OLLAMA_API_KEY=your-api-key
90
-
91
- # Embedding API (can be different from LLM)
92
- OLLAMA_EMBEDDING_URL=http://localhost:11434
93
-
94
- # Default models
95
- RAGIT_DEFAULT_LLM_MODEL=llama3.1:8b
96
- RAGIT_DEFAULT_EMBEDDING_MODEL=mxbai-embed-large
97
- ```
98
-
99
- A common setup is to use a cloud API for LLM inference (faster, more capable models) while running embeddings locally (lower latency, no API costs for indexing).
100
-
101
- ## Tutorial: Using Ragit
102
-
103
- This section covers the core functionality of ragit: loading documents, creating a RAG assistant, and querying your knowledge base.
104
-
105
- ### Loading Documents
106
-
107
- Ragit provides several functions for loading and chunking documents.
108
-
109
- **Loading a single file:**
110
-
111
- ```python
112
- from ragit import load_text
113
-
114
- doc = load_text("docs/api-reference.md")
115
- print(doc.id) # "api-reference"
116
- print(doc.content) # Full file contents
117
- ```
118
-
119
- **Loading a directory:**
120
-
121
- ```python
122
- from ragit import load_directory
123
-
124
- # Load all markdown files
125
- docs = load_directory("docs/", "*.md")
126
-
127
- # Load recursively
128
- docs = load_directory("docs/", "**/*.md", recursive=True)
129
-
130
- # Load multiple file types
131
- txt_docs = load_directory("docs/", "*.txt")
132
- rst_docs = load_directory("docs/", "*.rst")
133
- all_docs = txt_docs + rst_docs
134
- ```
135
-
136
- **Custom chunking:**
137
-
138
- For fine-grained control over how documents are split:
139
-
140
- ```python
141
- from ragit import chunk_text, chunk_by_separator, chunk_rst_sections
142
-
143
- # Fixed-size chunks with overlap
144
- chunks = chunk_text(
145
- text,
146
- chunk_size=512, # Characters per chunk
147
- chunk_overlap=50, # Overlap between chunks
148
- doc_id="my-doc"
149
- )
150
-
151
- # Split by paragraph
152
- chunks = chunk_by_separator(text, separator="\n\n")
153
-
154
- # Split RST documents by section headers
155
- chunks = chunk_rst_sections(rst_content, doc_id="tutorial")
156
- ```
157
-
158
- ### The RAGAssistant Class
159
-
160
- The `RAGAssistant` class is the main interface for RAG operations. It handles document indexing, retrieval, and generation in a single object.
161
-
162
- ```python
163
- from ragit import RAGAssistant
164
-
165
- # Create from a directory
166
- assistant = RAGAssistant("docs/")
167
-
168
- # Create from a single file
169
- assistant = RAGAssistant("docs/tutorial.rst")
170
-
171
- # Create from Document objects
172
- from ragit import Document
173
-
174
- docs = [
175
- Document(id="intro", content="Introduction to the API..."),
176
- Document(id="auth", content="Authentication uses JWT tokens..."),
177
- Document(id="endpoints", content="Available endpoints: /users, /items..."),
178
- ]
179
- assistant = RAGAssistant(docs)
180
- ```
181
-
182
- **Configuration options:**
183
-
184
- ```python
185
- assistant = RAGAssistant(
186
- "docs/",
187
- embedding_model="mxbai-embed-large", # Model for embeddings
188
- llm_model="llama3.1:70b", # Model for generation
189
- chunk_size=512, # Characters per chunk
190
- chunk_overlap=50, # Overlap between chunks
191
- )
192
- ```
193
-
194
- ### Asking Questions
195
-
196
- The `ask()` method retrieves relevant context and generates an answer:
197
-
198
- ```python
199
- assistant = RAGAssistant("docs/")
200
-
201
- answer = assistant.ask("How do I authenticate API requests?")
202
- print(answer)
203
- ```
204
-
205
- **Customizing the query:**
206
-
207
- ```python
208
- answer = assistant.ask(
209
- "How do I authenticate API requests?",
210
- top_k=5, # Number of chunks to retrieve
211
- temperature=0.3, # Lower = more focused answers
212
- system_prompt="You are a technical documentation assistant. "
213
- "Answer concisely and include code examples."
214
- )
215
- ```
216
-
217
- ### Generating Code
218
-
219
- The `generate_code()` method is optimized for producing clean, runnable code:
220
-
221
- ```python
222
- assistant = RAGAssistant("framework-docs/")
223
-
224
- code = assistant.generate_code(
225
- "Create a REST API endpoint for user registration",
226
- language="python"
227
- )
228
- print(code)
229
- ```
230
-
231
- The output is clean code without markdown formatting. The assistant uses your documentation as context to generate framework-specific, idiomatic code.
232
-
233
- ### Custom Retrieval
234
-
235
- For advanced use cases, you can access the retrieval and generation steps separately:
236
-
237
- ```python
238
- assistant = RAGAssistant("docs/")
239
-
240
- # Step 1: Retrieve relevant chunks
241
- results = assistant.retrieve("authentication", top_k=5)
242
- for chunk, score in results:
243
- print(f"Score: {score:.3f}")
244
- print(f"Content: {chunk.content[:200]}...")
245
- print()
246
-
247
- # Step 2: Get formatted context string
248
- context = assistant.get_context("authentication", top_k=3)
249
-
250
- # Step 3: Generate with custom prompt
251
- prompt = f"""Based on this documentation:
252
-
253
- {context}
254
-
255
- Write a Python function that validates a JWT token."""
256
-
257
- response = assistant.generate(
258
- prompt,
259
- system_prompt="You are an expert Python developer.",
260
- temperature=0.2
261
- )
262
- ```
263
-
264
- ## Tutorial: Platform Integration
265
-
266
- This section shows how to integrate ragit into web applications and other platforms.
267
-
268
- ### Flask Integration
269
-
270
- ```python
271
- from flask import Flask, request, jsonify
272
- from ragit import RAGAssistant
273
-
274
- app = Flask(__name__)
275
-
276
- # Initialize once at startup
277
- assistant = RAGAssistant("docs/")
278
-
279
- @app.route("/ask", methods=["POST"])
280
- def ask():
281
- data = request.get_json()
282
- question = data.get("question", "")
283
-
284
- if not question:
285
- return jsonify({"error": "question is required"}), 400
286
-
287
- answer = assistant.ask(question, top_k=3)
288
- return jsonify({"answer": answer})
289
-
290
- @app.route("/search", methods=["GET"])
291
- def search():
292
- query = request.args.get("q", "")
293
- top_k = int(request.args.get("top_k", 5))
294
-
295
- results = assistant.retrieve(query, top_k=top_k)
296
- return jsonify({
297
- "results": [
298
- {"content": chunk.content, "score": score}
299
- for chunk, score in results
300
- ]
301
- })
302
-
303
- if __name__ == "__main__":
304
- app.run(debug=True)
305
- ```
306
-
307
- ### FastAPI Integration
308
-
309
- ```python
310
- from fastapi import FastAPI, HTTPException
311
- from pydantic import BaseModel
312
- from ragit import RAGAssistant
313
-
314
- app = FastAPI()
315
-
316
- # Initialize once at startup
317
- assistant = RAGAssistant("docs/")
318
-
319
- class Question(BaseModel):
320
- question: str
321
- top_k: int = 3
322
- temperature: float = 0.7
323
-
324
- class Answer(BaseModel):
325
- answer: str
326
-
327
- @app.post("/ask", response_model=Answer)
328
- async def ask(q: Question):
329
- if not q.question.strip():
330
- raise HTTPException(status_code=400, detail="question is required")
331
-
332
- answer = assistant.ask(
333
- q.question,
334
- top_k=q.top_k,
335
- temperature=q.temperature
336
- )
337
- return Answer(answer=answer)
338
-
339
- @app.get("/search")
340
- async def search(q: str, top_k: int = 5):
341
- results = assistant.retrieve(q, top_k=top_k)
342
- return {
343
- "results": [
344
- {"content": chunk.content, "score": score}
345
- for chunk, score in results
346
- ]
347
- }
348
- ```
349
-
350
- ### Command-Line Tools
351
-
352
- Build CLI tools using argparse or click:
353
-
354
- ```python
355
- #!/usr/bin/env python3
356
- import argparse
357
- from ragit import RAGAssistant
358
-
359
- def main():
360
- parser = argparse.ArgumentParser(description="Query documentation")
361
- parser.add_argument("question", help="Question to ask")
362
- parser.add_argument("--docs", default="docs/", help="Documentation path")
363
- parser.add_argument("--top-k", type=int, default=3, help="Context chunks")
364
- args = parser.parse_args()
365
-
366
- assistant = RAGAssistant(args.docs)
367
- answer = assistant.ask(args.question, top_k=args.top_k)
368
- print(answer)
369
-
370
- if __name__ == "__main__":
371
- main()
372
- ```
373
-
374
- Usage:
375
-
376
- ```bash
377
- python ask.py "How do I configure logging?"
378
- python ask.py "What are the API rate limits?" --docs api-docs/ --top-k 5
379
- ```
380
-
381
- ### Batch Processing
382
-
383
- Process multiple questions or generate reports:
384
-
385
- ```python
386
- from ragit import RAGAssistant
387
-
388
- assistant = RAGAssistant("docs/")
389
-
390
- questions = [
391
- "What authentication methods are supported?",
392
- "How do I handle errors?",
393
- "What are the rate limits?",
394
- ]
395
-
396
- # Process questions
397
- results = {}
398
- for question in questions:
399
- results[question] = assistant.ask(question)
400
-
401
- # Generate a report
402
- with open("qa-report.md", "w") as f:
403
- f.write("# Documentation Q&A Report\n\n")
404
- for question, answer in results.items():
405
- f.write(f"## {question}\n\n")
406
- f.write(f"{answer}\n\n")
407
- ```
408
-
409
- ## Advanced: Hyperparameter Optimization
410
-
411
- Ragit includes tools to find the optimal RAG configuration for your specific documents and use case.
412
-
413
- ```python
414
- from ragit import RagitExperiment, Document, BenchmarkQuestion
415
-
416
- # Your documents
417
- documents = [
418
- Document(id="auth", content="Authentication uses Bearer tokens..."),
419
- Document(id="api", content="The API supports GET, POST, PUT, DELETE..."),
420
- ]
421
-
422
- # Benchmark questions with expected answers
423
- benchmark = [
424
- BenchmarkQuestion(
425
- question="What authentication method does the API use?",
426
- ground_truth="The API uses Bearer token authentication."
427
- ),
428
- BenchmarkQuestion(
429
- question="What HTTP methods are supported?",
430
- ground_truth="GET, POST, PUT, and DELETE methods are supported."
431
- ),
432
- ]
433
-
434
- # Run optimization
435
- experiment = RagitExperiment(documents, benchmark)
436
- results = experiment.run(max_configs=20)
437
-
438
- # Get the best configuration
439
- best = results[0]
440
- print(f"Best config: chunk_size={best.config.chunk_size}, "
441
- f"chunk_overlap={best.config.chunk_overlap}, "
442
- f"top_k={best.config.top_k}")
443
- print(f"Score: {best.score:.3f}")
444
- ```
445
-
446
- The experiment tests different combinations of chunk sizes, overlaps, and retrieval parameters to find what works best for your content.
447
-
448
- ## Performance Features
449
-
450
- Ragit includes several optimizations for production workloads:
451
-
452
- ### Connection Pooling
453
-
454
- `OllamaProvider` uses HTTP connection pooling via `requests.Session()` for faster sequential requests:
455
-
456
- ```python
457
- from ragit.providers import OllamaProvider
458
-
459
- provider = OllamaProvider()
460
-
461
- # All requests reuse the same connection pool
462
- for text in texts:
463
- provider.embed(text, model="mxbai-embed-large")
464
-
465
- # Explicitly close when done (optional, auto-closes on garbage collection)
466
- provider.close()
467
- ```
468
-
469
- ### Async Parallel Embedding
470
-
471
- For large batches, use `embed_batch_async()` with trio for 5-10x faster embedding:
472
-
473
- ```python
474
- import trio
475
- from ragit.providers import OllamaProvider
476
-
477
- provider = OllamaProvider()
478
-
479
- async def embed_documents():
480
- texts = ["doc1...", "doc2...", "doc3...", ...] # hundreds of texts
481
- embeddings = await provider.embed_batch_async(
482
- texts,
483
- model="mxbai-embed-large",
484
- max_concurrent=10 # Adjust based on server capacity
485
- )
486
- return embeddings
487
-
488
- # Run with trio
489
- results = trio.run(embed_documents)
490
- ```
491
-
492
- ### Embedding Cache
493
-
494
- Repeated embedding calls are cached automatically (2048 entries LRU):
495
-
496
- ```python
497
- from ragit.providers import OllamaProvider
498
-
499
- provider = OllamaProvider(use_cache=True) # Default
500
-
501
- # First call hits the API
502
- provider.embed("Hello world", model="mxbai-embed-large")
503
-
504
- # Second call returns cached result instantly
505
- provider.embed("Hello world", model="mxbai-embed-large")
506
-
507
- # View cache statistics
508
- print(OllamaProvider.embedding_cache_info())
509
- # {'hits': 1, 'misses': 1, 'maxsize': 2048, 'currsize': 1}
510
-
511
- # Clear cache if needed
512
- OllamaProvider.clear_embedding_cache()
513
- ```
514
-
515
- ### Pre-normalized Embeddings
516
-
517
- Vector similarity uses pre-normalized embeddings, making cosine similarity a simple dot product (O(1) per comparison).
518
-
519
- ## API Reference
520
-
521
- ### Document Loading
522
-
523
- | Function | Description |
524
- |----------|-------------|
525
- | `load_text(path)` | Load a single text file as a Document |
526
- | `load_directory(path, pattern, recursive=False)` | Load files matching a glob pattern |
527
- | `chunk_text(text, chunk_size, chunk_overlap, doc_id)` | Split text into overlapping chunks |
528
- | `chunk_document(doc, chunk_size, chunk_overlap)` | Split a Document into chunks |
529
- | `chunk_by_separator(text, separator, doc_id)` | Split text by a delimiter |
530
- | `chunk_rst_sections(text, doc_id)` | Split RST by section headers |
531
-
532
- ### RAGAssistant
533
-
534
- | Method | Description |
535
- |--------|-------------|
536
- | `retrieve(query, top_k=3)` | Return list of (Chunk, score) tuples |
537
- | `get_context(query, top_k=3)` | Return formatted context string |
538
- | `generate(prompt, system_prompt, temperature)` | Generate text without retrieval |
539
- | `ask(question, system_prompt, top_k, temperature)` | Retrieve context and generate answer |
540
- | `generate_code(request, language, top_k, temperature)` | Generate clean code |
541
-
542
- ### Properties
543
-
544
- | Property | Description |
545
- |----------|-------------|
546
- | `assistant.num_documents` | Number of loaded documents |
547
- | `assistant.num_chunks` | Number of indexed chunks |
548
- | `assistant.embedding_model` | Current embedding model |
549
- | `assistant.llm_model` | Current LLM model |
550
-
551
- ## License
552
-
553
- Apache-2.0 - RODMENA LIMITED
@@ -1,18 +0,0 @@
1
- ragit/__init__.py,sha256=PjQogIWMlydZFWVECqhmxw-X9i7lEXdUTe2XlT6qYUQ,2213
2
- ragit/assistant.py,sha256=lXjZRUr_WsYLP3XLOktabgfPVyKOZPdREzyL7cSRufk,11251
3
- ragit/config.py,sha256=aSGWQGiaRm6hrjssvCjhqZOa76pxegeOtcFbFRlQx4M,1501
4
- ragit/loaders.py,sha256=keusuPzXPBiLDVj4hKfPCcge-rm-cnzNRk50fGXvTJs,5571
5
- ragit/version.py,sha256=Vj5ogQMaioIPZOEL7StQIcdzW1RI4gnuLlRkcVqW7qk,97
6
- ragit/core/__init__.py,sha256=j53PFfoSMXwSbK1rRHpMbo8mX2i4R1LJ5kvTxBd7-0w,100
7
- ragit/core/experiment/__init__.py,sha256=4vAPOOYlY5Dcr2gOolyhBSPGIUxZKwEkgQffxS9BodA,452
8
- ragit/core/experiment/experiment.py,sha256=Qh1NJkY9LbKaidRfiI8GOwBZqopjK-MSVBuD_JEgO-k,16582
9
- ragit/core/experiment/results.py,sha256=KHpN3YSLJ83_JUfIMccRPS-q7LEt0S9p8ehDRawk_4k,3487
10
- ragit/providers/__init__.py,sha256=iliJt74Lt3mFUlKGfSFW-D0cMonUygY6sRZ6lLjeU7M,435
11
- ragit/providers/base.py,sha256=MJ8mVeXuGWhkX2XGTbkWIY3cVoTOPr4h5XBXw8rAX2Q,3434
12
- ragit/providers/ollama.py,sha256=bGZfcmlfchnVP5851noWaf3c1weMhknGOs7Fu69Oz4E,15404
13
- ragit/utils/__init__.py,sha256=-UsE5oJSnmEnBDswl-ph0A09Iu8yKNbPhd1-_7Lcb8Y,3051
14
- ragit-0.7.5.dist-info/licenses/LICENSE,sha256=tAkwu8-AdEyGxGoSvJ2gVmQdcicWw3j1ZZueVV74M-E,11357
15
- ragit-0.7.5.dist-info/METADATA,sha256=T_wNuarfzzkfhViVmigIe8n4Kz5FLFCbVj3oWAA_D9w,15528
16
- ragit-0.7.5.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
17
- ragit-0.7.5.dist-info/top_level.txt,sha256=pkPbG7yrw61wt9_y_xcLE2vq2a55fzockASD0yq0g4s,6
18
- ragit-0.7.5.dist-info/RECORD,,
File without changes