ragforge-framework 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.
- ragforge_framework-0.1.0/.gitignore +15 -0
- ragforge_framework-0.1.0/.python-version +1 -0
- ragforge_framework-0.1.0/PKG-INFO +148 -0
- ragforge_framework-0.1.0/README.md +120 -0
- ragforge_framework-0.1.0/app.py +15 -0
- ragforge_framework-0.1.0/data/data.txt +1 -0
- ragforge_framework-0.1.0/pyproject.toml +36 -0
- ragforge_framework-0.1.0/ragforge_framework/__init__.py +13 -0
- ragforge_framework-0.1.0/ragforge_framework/admin/__init__.py +1 -0
- ragforge_framework-0.1.0/ragforge_framework/admin/routes.py +224 -0
- ragforge_framework-0.1.0/ragforge_framework/app.py +88 -0
- ragforge_framework-0.1.0/ragforge_framework/cache/__init__.py +1 -0
- ragforge_framework-0.1.0/ragforge_framework/cache/repository.py +155 -0
- ragforge_framework-0.1.0/ragforge_framework/cache/response_cache.py +105 -0
- ragforge_framework-0.1.0/ragforge_framework/cache/user_manager.py +264 -0
- ragforge_framework-0.1.0/ragforge_framework/cli.py +91 -0
- ragforge_framework-0.1.0/ragforge_framework/config.py +50 -0
- ragforge_framework-0.1.0/ragforge_framework/core.py +42 -0
- ragforge_framework-0.1.0/ragforge_framework/llm/__init__.py +1 -0
- ragforge_framework-0.1.0/ragforge_framework/llm/gateway.py +95 -0
- ragforge_framework-0.1.0/ragforge_framework/llm/service.py +50 -0
- ragforge_framework-0.1.0/ragforge_framework/models/__init__.py +1 -0
- ragforge_framework-0.1.0/ragforge_framework/models/schemas.py +90 -0
- ragforge_framework-0.1.0/ragforge_framework/rag/__init__.py +1 -0
- ragforge_framework-0.1.0/ragforge_framework/rag/chain.py +28 -0
- ragforge_framework-0.1.0/ragforge_framework/rag/embeddings.py +10 -0
- ragforge_framework-0.1.0/ragforge_framework/rag/engine.py +31 -0
- ragforge_framework-0.1.0/ragforge_framework/rag/injest.py +81 -0
- ragforge_framework-0.1.0/ragforge_framework/rag/prompt.py +61 -0
- ragforge_framework-0.1.0/ragforge_framework/rag/retriever.py +19 -0
- ragforge_framework-0.1.0/ragforge_framework/rag/vectorstore.py +31 -0
- ragforge_framework-0.1.0/requirements.txt +19 -0
- ragforge_framework-0.1.0/uv.lock +3502 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
3.12
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: ragforge-framework
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Modular AI Backend Framework built with FastAPI, LiteLLM and LangChain
|
|
5
|
+
Requires-Python: >=3.12
|
|
6
|
+
Requires-Dist: diskcache>=5.6.3
|
|
7
|
+
Requires-Dist: faiss-cpu>=1.14.3
|
|
8
|
+
Requires-Dist: fastapi>=0.139.0
|
|
9
|
+
Requires-Dist: langchain-community>=0.4.2
|
|
10
|
+
Requires-Dist: langchain-google-genai>=4.2.7
|
|
11
|
+
Requires-Dist: langchain-groq>=1.1.3
|
|
12
|
+
Requires-Dist: langchain-huggingface>=1.2.2
|
|
13
|
+
Requires-Dist: langchain-litellm>=0.7.0
|
|
14
|
+
Requires-Dist: langchain-mistralai>=1.1.6
|
|
15
|
+
Requires-Dist: langchain-ollama>=1.1.0
|
|
16
|
+
Requires-Dist: langchain-openai>=1.3.5
|
|
17
|
+
Requires-Dist: langchain-redis>=0.2.5
|
|
18
|
+
Requires-Dist: langchain-text-splitters>=1.1.2
|
|
19
|
+
Requires-Dist: langchain>=1.3.12
|
|
20
|
+
Requires-Dist: langgraph>=1.2.9
|
|
21
|
+
Requires-Dist: litellm>=1.91.1
|
|
22
|
+
Requires-Dist: pandas>=3.0.3
|
|
23
|
+
Requires-Dist: pypdf>=6.14.2
|
|
24
|
+
Requires-Dist: python-dotenv>=1.2.2
|
|
25
|
+
Requires-Dist: sentence-transformers>=5.6.0
|
|
26
|
+
Requires-Dist: uvicorn>=0.51.0
|
|
27
|
+
Description-Content-Type: text/markdown
|
|
28
|
+
|
|
29
|
+
# 🚀 RAGForge Framework
|
|
30
|
+
|
|
31
|
+
### Production-Ready AI Backend Framework built with FastAPI, LiteLLM and LangChain
|
|
32
|
+
|
|
33
|
+
RAGForge is a modular Python RAG (Retrieval-Augmented Generation) framework that helps developers build and deploy production-ready AI backends with built-in semantic caching, IP-based rate limiting, automatic LLM fallbacks, and comprehensive administrative APIs.
|
|
34
|
+
|
|
35
|
+
---
|
|
36
|
+
|
|
37
|
+
## 📖 Features
|
|
38
|
+
|
|
39
|
+
- **Retrieval-Augmented Generation (RAG):** Powered by LangChain, FAISS Vector Store, and HuggingFace Embeddings.
|
|
40
|
+
- **LiteLLM Gateway:** Connect to Ollama, Groq, Mistral, OpenAI, Gemini, etc. with automatic LLM fallback switching.
|
|
41
|
+
- **Semantic caching:** Persistent cache layers using `diskcache` to reduce latency and API costs.
|
|
42
|
+
- **IP-Based Rate Limiting:** Daily limit controls to prevent abuse.
|
|
43
|
+
- **Admin APIs:** Built-in endpoints for viewing user statistics, resetting cache, tracking LLM provider metrics, and clearing cache.
|
|
44
|
+
- **Unified CLI:** Simple CLI tool to initialize, build, and run applications.
|
|
45
|
+
|
|
46
|
+
---
|
|
47
|
+
|
|
48
|
+
## ⚙️ Installation
|
|
49
|
+
|
|
50
|
+
To install RAGForge locally from source:
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
git clone https://github.com/yourusername/RAGForge.git
|
|
54
|
+
cd RAGForge
|
|
55
|
+
pip install -e .
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
---
|
|
59
|
+
|
|
60
|
+
## 🚀 Quick Start (CLI Mode)
|
|
61
|
+
|
|
62
|
+
RAGForge includes a CLI executable to quickly manage projects.
|
|
63
|
+
|
|
64
|
+
### 1. Initialize a new project
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
ragforge init
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
This generates a starter project structure:
|
|
71
|
+
|
|
72
|
+
- `data/data.txt`: Your source document repository.
|
|
73
|
+
- `app.py`: The entry point script configuring your framework.
|
|
74
|
+
|
|
75
|
+
### 2. Ingest documents
|
|
76
|
+
|
|
77
|
+
Add your knowledge text database to `data/data.txt` and generate the vector index:
|
|
78
|
+
|
|
79
|
+
```bash
|
|
80
|
+
ragforge ingest
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
### 3. Start the API server
|
|
84
|
+
|
|
85
|
+
```bash
|
|
86
|
+
ragforge run --port 8000
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
This launches a FastAPI server. Open [http://127.0.0.1:8000/docs](http://127.0.0.1:8000/docs) in your browser to inspect the interactive Swagger API documentation.
|
|
90
|
+
|
|
91
|
+
---
|
|
92
|
+
|
|
93
|
+
## 🐍 Programmatic Usage
|
|
94
|
+
|
|
95
|
+
You can import and configure `RAGForge` inside any Python application:
|
|
96
|
+
|
|
97
|
+
```python
|
|
98
|
+
from ragforge_framework import RAGForge
|
|
99
|
+
|
|
100
|
+
# 1. Initialize with custom configurations
|
|
101
|
+
forge = RAGForge(
|
|
102
|
+
data_path="data/data.txt",
|
|
103
|
+
faiss_path="faiss_index",
|
|
104
|
+
primary_model="ollama/llama3.2:latest",
|
|
105
|
+
fallback_models=["groq/llama-3.1-8b-instant"],
|
|
106
|
+
daily_limit=15,
|
|
107
|
+
admin_key="mysecretkey"
|
|
108
|
+
)
|
|
109
|
+
|
|
110
|
+
# Expose FastAPI application instance for uvicorn/gunicorn
|
|
111
|
+
app = forge.app
|
|
112
|
+
|
|
113
|
+
if __name__ == "__main__":
|
|
114
|
+
# 2. Run the application
|
|
115
|
+
forge.run(host="127.0.0.1", port=8000)
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
---
|
|
119
|
+
|
|
120
|
+
## 🔧 Configuration Options
|
|
121
|
+
|
|
122
|
+
The `RAGForge` orchestrator accepts the following keyword arguments during instantiation:
|
|
123
|
+
|
|
124
|
+
| Parameter | Type | Default | Description |
|
|
125
|
+
| :------------------- | :----- | :----------------------------------- | :--------------------------------------------- |
|
|
126
|
+
| `primary_model` | `str` | `"ollama/llama3.2:latest"` | The primary LLM to query. |
|
|
127
|
+
| `fallback_models` | `list` | `["groq/llama-3.1-8b-instant", ...]` | Backup LLMs to trigger if primary fails. |
|
|
128
|
+
| `daily_limit` | `int` | `10` | Daily LLM request quota per IP. |
|
|
129
|
+
| `admin_key` | `str` | `"yashodeep"` | The secret key to secure `/admin` endpoints. |
|
|
130
|
+
| `data_path` | `str` | `"data/data.txt"` | Path to the source text file to index. |
|
|
131
|
+
| `faiss_path` | `str` | `"faiss_index"` | Path to save the FAISS vector database. |
|
|
132
|
+
| `embedding_model` | `str` | `"sentence-transformers/..."` | HuggingFace embedding model name. |
|
|
133
|
+
| `response_cache_dir` | `str` | `"./response_cache"` | Directory to store query cache. |
|
|
134
|
+
| `user_cache_dir` | `str` | `"./user_cache"` | Directory to store user rate limit statistics. |
|
|
135
|
+
| `chunk_size` | `int` | `800` | Size of split text chunks. |
|
|
136
|
+
| `chunk_overlap` | `int` | `100` | Text chunk overlap tokens. |
|
|
137
|
+
|
|
138
|
+
---
|
|
139
|
+
|
|
140
|
+
## 📊 Administration Endpoints
|
|
141
|
+
|
|
142
|
+
Secure administrative APIs are exposed under `/admin/` (requires `x-admin-key` header matching your `admin_key`):
|
|
143
|
+
|
|
144
|
+
- `GET /admin/users`: Lists stats for all registered users.
|
|
145
|
+
- `GET /admin/stats`: Exposes global metrics (total queries, cache hits, provider splits).
|
|
146
|
+
- `GET /admin/cache`: Inspects all currently cached queries and responses.
|
|
147
|
+
- `DELETE /admin/cache`: Clears the response cache.
|
|
148
|
+
- `DELETE /admin/reset`: Clears all caches and user request quotas.
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
# 🚀 RAGForge Framework
|
|
2
|
+
|
|
3
|
+
### Production-Ready AI Backend Framework built with FastAPI, LiteLLM and LangChain
|
|
4
|
+
|
|
5
|
+
RAGForge is a modular Python RAG (Retrieval-Augmented Generation) framework that helps developers build and deploy production-ready AI backends with built-in semantic caching, IP-based rate limiting, automatic LLM fallbacks, and comprehensive administrative APIs.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## 📖 Features
|
|
10
|
+
|
|
11
|
+
- **Retrieval-Augmented Generation (RAG):** Powered by LangChain, FAISS Vector Store, and HuggingFace Embeddings.
|
|
12
|
+
- **LiteLLM Gateway:** Connect to Ollama, Groq, Mistral, OpenAI, Gemini, etc. with automatic LLM fallback switching.
|
|
13
|
+
- **Semantic caching:** Persistent cache layers using `diskcache` to reduce latency and API costs.
|
|
14
|
+
- **IP-Based Rate Limiting:** Daily limit controls to prevent abuse.
|
|
15
|
+
- **Admin APIs:** Built-in endpoints for viewing user statistics, resetting cache, tracking LLM provider metrics, and clearing cache.
|
|
16
|
+
- **Unified CLI:** Simple CLI tool to initialize, build, and run applications.
|
|
17
|
+
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
## ⚙️ Installation
|
|
21
|
+
|
|
22
|
+
To install RAGForge locally from source:
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
git clone https://github.com/yourusername/RAGForge.git
|
|
26
|
+
cd RAGForge
|
|
27
|
+
pip install -e .
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
---
|
|
31
|
+
|
|
32
|
+
## 🚀 Quick Start (CLI Mode)
|
|
33
|
+
|
|
34
|
+
RAGForge includes a CLI executable to quickly manage projects.
|
|
35
|
+
|
|
36
|
+
### 1. Initialize a new project
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
ragforge init
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
This generates a starter project structure:
|
|
43
|
+
|
|
44
|
+
- `data/data.txt`: Your source document repository.
|
|
45
|
+
- `app.py`: The entry point script configuring your framework.
|
|
46
|
+
|
|
47
|
+
### 2. Ingest documents
|
|
48
|
+
|
|
49
|
+
Add your knowledge text database to `data/data.txt` and generate the vector index:
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
ragforge ingest
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
### 3. Start the API server
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
ragforge run --port 8000
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
This launches a FastAPI server. Open [http://127.0.0.1:8000/docs](http://127.0.0.1:8000/docs) in your browser to inspect the interactive Swagger API documentation.
|
|
62
|
+
|
|
63
|
+
---
|
|
64
|
+
|
|
65
|
+
## 🐍 Programmatic Usage
|
|
66
|
+
|
|
67
|
+
You can import and configure `RAGForge` inside any Python application:
|
|
68
|
+
|
|
69
|
+
```python
|
|
70
|
+
from ragforge_framework import RAGForge
|
|
71
|
+
|
|
72
|
+
# 1. Initialize with custom configurations
|
|
73
|
+
forge = RAGForge(
|
|
74
|
+
data_path="data/data.txt",
|
|
75
|
+
faiss_path="faiss_index",
|
|
76
|
+
primary_model="ollama/llama3.2:latest",
|
|
77
|
+
fallback_models=["groq/llama-3.1-8b-instant"],
|
|
78
|
+
daily_limit=15,
|
|
79
|
+
admin_key="mysecretkey"
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
# Expose FastAPI application instance for uvicorn/gunicorn
|
|
83
|
+
app = forge.app
|
|
84
|
+
|
|
85
|
+
if __name__ == "__main__":
|
|
86
|
+
# 2. Run the application
|
|
87
|
+
forge.run(host="127.0.0.1", port=8000)
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
---
|
|
91
|
+
|
|
92
|
+
## 🔧 Configuration Options
|
|
93
|
+
|
|
94
|
+
The `RAGForge` orchestrator accepts the following keyword arguments during instantiation:
|
|
95
|
+
|
|
96
|
+
| Parameter | Type | Default | Description |
|
|
97
|
+
| :------------------- | :----- | :----------------------------------- | :--------------------------------------------- |
|
|
98
|
+
| `primary_model` | `str` | `"ollama/llama3.2:latest"` | The primary LLM to query. |
|
|
99
|
+
| `fallback_models` | `list` | `["groq/llama-3.1-8b-instant", ...]` | Backup LLMs to trigger if primary fails. |
|
|
100
|
+
| `daily_limit` | `int` | `10` | Daily LLM request quota per IP. |
|
|
101
|
+
| `admin_key` | `str` | `"yashodeep"` | The secret key to secure `/admin` endpoints. |
|
|
102
|
+
| `data_path` | `str` | `"data/data.txt"` | Path to the source text file to index. |
|
|
103
|
+
| `faiss_path` | `str` | `"faiss_index"` | Path to save the FAISS vector database. |
|
|
104
|
+
| `embedding_model` | `str` | `"sentence-transformers/..."` | HuggingFace embedding model name. |
|
|
105
|
+
| `response_cache_dir` | `str` | `"./response_cache"` | Directory to store query cache. |
|
|
106
|
+
| `user_cache_dir` | `str` | `"./user_cache"` | Directory to store user rate limit statistics. |
|
|
107
|
+
| `chunk_size` | `int` | `800` | Size of split text chunks. |
|
|
108
|
+
| `chunk_overlap` | `int` | `100` | Text chunk overlap tokens. |
|
|
109
|
+
|
|
110
|
+
---
|
|
111
|
+
|
|
112
|
+
## 📊 Administration Endpoints
|
|
113
|
+
|
|
114
|
+
Secure administrative APIs are exposed under `/admin/` (requires `x-admin-key` header matching your `admin_key`):
|
|
115
|
+
|
|
116
|
+
- `GET /admin/users`: Lists stats for all registered users.
|
|
117
|
+
- `GET /admin/stats`: Exposes global metrics (total queries, cache hits, provider splits).
|
|
118
|
+
- `GET /admin/cache`: Inspects all currently cached queries and responses.
|
|
119
|
+
- `DELETE /admin/cache`: Clears the response cache.
|
|
120
|
+
- `DELETE /admin/reset`: Clears all caches and user request quotas.
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
from ragforge_framework import RAGForge
|
|
2
|
+
|
|
3
|
+
# Instantiate the RAGForge framework config and services
|
|
4
|
+
forge = RAGForge(
|
|
5
|
+
data_path="data/data.txt",
|
|
6
|
+
faiss_path="faiss_index",
|
|
7
|
+
primary_model="ollama/llama3.2:latest"
|
|
8
|
+
)
|
|
9
|
+
|
|
10
|
+
# Expose fastapi app for production WSGI/ASGI servers (e.g. gunicorn/uvicorn)
|
|
11
|
+
app = forge.app
|
|
12
|
+
|
|
13
|
+
if __name__ == "__main__":
|
|
14
|
+
# Start the server locally when running app.py directly
|
|
15
|
+
forge.run(host="127.0.0.1", port=8000)
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
Yashodeep Hundiwale is an experienced AI Engineer and creator of RAGForge.
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "ragforge-framework"
|
|
3
|
+
version = "0.1.0"
|
|
4
|
+
description = "Modular AI Backend Framework built with FastAPI, LiteLLM and LangChain"
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
requires-python = ">=3.12"
|
|
7
|
+
dependencies = [
|
|
8
|
+
"diskcache>=5.6.3",
|
|
9
|
+
"faiss-cpu>=1.14.3",
|
|
10
|
+
"fastapi>=0.139.0",
|
|
11
|
+
"langchain>=1.3.12",
|
|
12
|
+
"langchain-community>=0.4.2",
|
|
13
|
+
"langchain-google-genai>=4.2.7",
|
|
14
|
+
"langchain-groq>=1.1.3",
|
|
15
|
+
"langchain-huggingface>=1.2.2",
|
|
16
|
+
"langchain-litellm>=0.7.0",
|
|
17
|
+
"langchain-mistralai>=1.1.6",
|
|
18
|
+
"langchain-ollama>=1.1.0",
|
|
19
|
+
"langchain-openai>=1.3.5",
|
|
20
|
+
"langchain-redis>=0.2.5",
|
|
21
|
+
"langchain-text-splitters>=1.1.2",
|
|
22
|
+
"langgraph>=1.2.9",
|
|
23
|
+
"litellm>=1.91.1",
|
|
24
|
+
"pandas>=3.0.3",
|
|
25
|
+
"pypdf>=6.14.2",
|
|
26
|
+
"python-dotenv>=1.2.2",
|
|
27
|
+
"sentence-transformers>=5.6.0",
|
|
28
|
+
"uvicorn>=0.51.0",
|
|
29
|
+
]
|
|
30
|
+
|
|
31
|
+
[project.scripts]
|
|
32
|
+
ragforge = "ragforge_framework.cli:main"
|
|
33
|
+
|
|
34
|
+
[build-system]
|
|
35
|
+
requires = ["hatchling"]
|
|
36
|
+
build-backend = "hatchling.build"
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
from .config import RAGForgeConfig
|
|
2
|
+
from .core import RAGForge
|
|
3
|
+
from .rag.engine import RAGEngine
|
|
4
|
+
from .cache.user_manager import UserManager
|
|
5
|
+
from .cache.response_cache import ResponseCache
|
|
6
|
+
|
|
7
|
+
__all__ = [
|
|
8
|
+
"RAGForge",
|
|
9
|
+
"RAGForgeConfig",
|
|
10
|
+
"RAGEngine",
|
|
11
|
+
"UserManager",
|
|
12
|
+
"ResponseCache",
|
|
13
|
+
]
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
# Admin Package Initialization
|
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
from fastapi import APIRouter, Header, HTTPException, Request
|
|
2
|
+
|
|
3
|
+
from ..models.schemas import (
|
|
4
|
+
UserResponse,
|
|
5
|
+
StatisticsResponse,
|
|
6
|
+
AllUsersResponse,
|
|
7
|
+
TopUsersResponse,
|
|
8
|
+
CacheInformationResponse
|
|
9
|
+
)
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def get_admin_router(forge) -> APIRouter:
|
|
13
|
+
"""
|
|
14
|
+
Admin Router Factory.
|
|
15
|
+
Builds the APIRouter instance dynamically, linking state dependencies to Request.app.state.
|
|
16
|
+
"""
|
|
17
|
+
router = APIRouter(
|
|
18
|
+
prefix="/admin",
|
|
19
|
+
tags=["Admin"]
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
def verify_admin(request: Request, key: str):
|
|
23
|
+
config = request.app.state.config
|
|
24
|
+
if key != config.ADMIN_KEY:
|
|
25
|
+
raise HTTPException(
|
|
26
|
+
status_code=403,
|
|
27
|
+
detail="Invalid Admin Key."
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
# ==========================================================
|
|
31
|
+
# HOME
|
|
32
|
+
# ==========================================================
|
|
33
|
+
|
|
34
|
+
@router.get("/")
|
|
35
|
+
def admin_home(
|
|
36
|
+
request: Request,
|
|
37
|
+
x_admin_key: str = Header(...)
|
|
38
|
+
):
|
|
39
|
+
verify_admin(request, x_admin_key)
|
|
40
|
+
return {
|
|
41
|
+
"message": "Admin API Running"
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
# ==========================================================
|
|
45
|
+
# ALL USERS
|
|
46
|
+
# ==========================================================
|
|
47
|
+
|
|
48
|
+
@router.get("/users", response_model=AllUsersResponse)
|
|
49
|
+
def get_all_users(
|
|
50
|
+
request: Request,
|
|
51
|
+
x_admin_key: str = Header(...)
|
|
52
|
+
):
|
|
53
|
+
verify_admin(request, x_admin_key)
|
|
54
|
+
user_manager = request.app.state.user_manager
|
|
55
|
+
users = user_manager.get_all_users()
|
|
56
|
+
|
|
57
|
+
return {
|
|
58
|
+
"total_users": len(users),
|
|
59
|
+
"users": users
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
# ==========================================================
|
|
63
|
+
# SINGLE USER
|
|
64
|
+
# ==========================================================
|
|
65
|
+
|
|
66
|
+
@router.get("/user/{user_id}", response_model=UserResponse)
|
|
67
|
+
def get_user(
|
|
68
|
+
request: Request,
|
|
69
|
+
user_id: str,
|
|
70
|
+
x_admin_key: str = Header(...)
|
|
71
|
+
):
|
|
72
|
+
verify_admin(request, x_admin_key)
|
|
73
|
+
user_manager = request.app.state.user_manager
|
|
74
|
+
user = user_manager.search_user(user_id)
|
|
75
|
+
|
|
76
|
+
if user is None:
|
|
77
|
+
raise HTTPException(
|
|
78
|
+
status_code=404,
|
|
79
|
+
detail="User not found."
|
|
80
|
+
)
|
|
81
|
+
return user
|
|
82
|
+
|
|
83
|
+
# ==========================================================
|
|
84
|
+
# RESET USER
|
|
85
|
+
# ==========================================================
|
|
86
|
+
|
|
87
|
+
@router.delete("/user/{user_id}")
|
|
88
|
+
def reset_user(
|
|
89
|
+
request: Request,
|
|
90
|
+
user_id: str,
|
|
91
|
+
x_admin_key: str = Header(...)
|
|
92
|
+
):
|
|
93
|
+
verify_admin(request, x_admin_key)
|
|
94
|
+
user_manager = request.app.state.user_manager
|
|
95
|
+
success = user_manager.reset_user(user_id)
|
|
96
|
+
|
|
97
|
+
if not success:
|
|
98
|
+
raise HTTPException(
|
|
99
|
+
status_code=404,
|
|
100
|
+
detail="User not found."
|
|
101
|
+
)
|
|
102
|
+
return {
|
|
103
|
+
"message": "User reset successfully."
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
# ==========================================================
|
|
107
|
+
# RESET ALL USERS
|
|
108
|
+
# ==========================================================
|
|
109
|
+
|
|
110
|
+
@router.delete("/users")
|
|
111
|
+
def reset_all_users(
|
|
112
|
+
request: Request,
|
|
113
|
+
x_admin_key: str = Header(...)
|
|
114
|
+
):
|
|
115
|
+
verify_admin(request, x_admin_key)
|
|
116
|
+
user_manager = request.app.state.user_manager
|
|
117
|
+
user_manager.reset_all()
|
|
118
|
+
|
|
119
|
+
return {
|
|
120
|
+
"message": "All users reset successfully."
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
# ==========================================================
|
|
124
|
+
# STATISTICS
|
|
125
|
+
# ==========================================================
|
|
126
|
+
|
|
127
|
+
@router.get("/stats", response_model=StatisticsResponse)
|
|
128
|
+
def statistics(
|
|
129
|
+
request: Request,
|
|
130
|
+
x_admin_key: str = Header(...)
|
|
131
|
+
):
|
|
132
|
+
verify_admin(request, x_admin_key)
|
|
133
|
+
user_manager = request.app.state.user_manager
|
|
134
|
+
|
|
135
|
+
return user_manager.get_statistics()
|
|
136
|
+
|
|
137
|
+
# ==========================================================
|
|
138
|
+
# TOP USERS
|
|
139
|
+
# ==========================================================
|
|
140
|
+
|
|
141
|
+
@router.get("/top-users", response_model=TopUsersResponse)
|
|
142
|
+
def top_users(
|
|
143
|
+
request: Request,
|
|
144
|
+
limit: int = 10,
|
|
145
|
+
x_admin_key: str = Header(...)
|
|
146
|
+
):
|
|
147
|
+
verify_admin(request, x_admin_key)
|
|
148
|
+
user_manager = request.app.state.user_manager
|
|
149
|
+
|
|
150
|
+
return {
|
|
151
|
+
"users": user_manager.get_top_users(limit)
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
# ==========================================================
|
|
155
|
+
# ACTIVE USERS
|
|
156
|
+
# ==========================================================
|
|
157
|
+
|
|
158
|
+
@router.get("/active-users", response_model=TopUsersResponse)
|
|
159
|
+
def active_users(
|
|
160
|
+
request: Request,
|
|
161
|
+
x_admin_key: str = Header(...)
|
|
162
|
+
):
|
|
163
|
+
verify_admin(request, x_admin_key)
|
|
164
|
+
user_manager = request.app.state.user_manager
|
|
165
|
+
|
|
166
|
+
return {
|
|
167
|
+
"users": user_manager.active_users()
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
# ==========================================================
|
|
171
|
+
# CACHE INFO
|
|
172
|
+
# ==========================================================
|
|
173
|
+
|
|
174
|
+
@router.get("/cache", response_model=CacheInformationResponse)
|
|
175
|
+
def cache_information(
|
|
176
|
+
request: Request,
|
|
177
|
+
x_admin_key: str = Header(...)
|
|
178
|
+
):
|
|
179
|
+
verify_admin(request, x_admin_key)
|
|
180
|
+
response_cache = request.app.state.response_cache
|
|
181
|
+
|
|
182
|
+
return {
|
|
183
|
+
"cached_responses": response_cache.total_cached_responses(),
|
|
184
|
+
"responses": response_cache.get_all()
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
# ==========================================================
|
|
188
|
+
# CLEAR RESPONSE CACHE
|
|
189
|
+
# ==========================================================
|
|
190
|
+
|
|
191
|
+
@router.delete("/cache")
|
|
192
|
+
def clear_cache(
|
|
193
|
+
request: Request,
|
|
194
|
+
x_admin_key: str = Header(...)
|
|
195
|
+
):
|
|
196
|
+
verify_admin(request, x_admin_key)
|
|
197
|
+
response_cache = request.app.state.response_cache
|
|
198
|
+
response_cache.clear()
|
|
199
|
+
|
|
200
|
+
return {
|
|
201
|
+
"message": "Response cache cleared."
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
# ==========================================================
|
|
205
|
+
# RESET EVERYTHING
|
|
206
|
+
# ==========================================================
|
|
207
|
+
|
|
208
|
+
@router.delete("/reset")
|
|
209
|
+
def reset_everything(
|
|
210
|
+
request: Request,
|
|
211
|
+
x_admin_key: str = Header(...)
|
|
212
|
+
):
|
|
213
|
+
verify_admin(request, x_admin_key)
|
|
214
|
+
user_manager = request.app.state.user_manager
|
|
215
|
+
response_cache = request.app.state.response_cache
|
|
216
|
+
|
|
217
|
+
user_manager.reset_all()
|
|
218
|
+
response_cache.clear()
|
|
219
|
+
|
|
220
|
+
return {
|
|
221
|
+
"message": "Users and response cache reset successfully."
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
return router
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
from fastapi import FastAPI, Request, HTTPException
|
|
2
|
+
from .models.schemas import ChatRequest, ChatResponse
|
|
3
|
+
from .admin.routes import get_admin_router
|
|
4
|
+
|
|
5
|
+
def create_app(forge) -> FastAPI:
|
|
6
|
+
"""
|
|
7
|
+
App Factory.
|
|
8
|
+
Builds the FastAPI application at runtime and loads configuration and services
|
|
9
|
+
into app.state.
|
|
10
|
+
"""
|
|
11
|
+
app = FastAPI(
|
|
12
|
+
title="RAGForge Framework Backend",
|
|
13
|
+
version="1.1.0"
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
# Attach orchestrator and configuration to app state
|
|
17
|
+
app.state.forge = forge
|
|
18
|
+
app.state.config = forge.config
|
|
19
|
+
app.state.user_manager = forge.user_manager
|
|
20
|
+
app.state.response_cache = forge.response_cache
|
|
21
|
+
app.state.engine = forge.engine
|
|
22
|
+
|
|
23
|
+
# Mount admin route router
|
|
24
|
+
app.include_router(get_admin_router(forge))
|
|
25
|
+
|
|
26
|
+
@app.get("/")
|
|
27
|
+
def home():
|
|
28
|
+
return {
|
|
29
|
+
"message": "🚀 RAGForge Framework Running Successfully"
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
@app.post("/chat", response_model=ChatResponse)
|
|
33
|
+
def chat(
|
|
34
|
+
data: ChatRequest,
|
|
35
|
+
request: Request
|
|
36
|
+
):
|
|
37
|
+
prompt = data.prompt.strip()
|
|
38
|
+
|
|
39
|
+
# Resolve request IP (check proxy headers first)
|
|
40
|
+
ip = request.headers.get(
|
|
41
|
+
"X-Forwarded-For",
|
|
42
|
+
request.client.host
|
|
43
|
+
).split(",")[0].strip()
|
|
44
|
+
|
|
45
|
+
# Load references from app.state
|
|
46
|
+
user_manager = request.app.state.user_manager
|
|
47
|
+
response_cache = request.app.state.response_cache
|
|
48
|
+
engine = request.app.state.engine
|
|
49
|
+
config = request.app.state.config
|
|
50
|
+
|
|
51
|
+
# 1. Register User Request
|
|
52
|
+
user_manager.register_request(ip)
|
|
53
|
+
|
|
54
|
+
# 2. Check Cache
|
|
55
|
+
cached = response_cache.get(ip, prompt)
|
|
56
|
+
if cached:
|
|
57
|
+
user = user_manager.get_user(ip)
|
|
58
|
+
return {
|
|
59
|
+
"success": True,
|
|
60
|
+
"cached": True,
|
|
61
|
+
"provider": "cache",
|
|
62
|
+
"remaining_questions": user["remaining"],
|
|
63
|
+
"answer": cached["answer"]
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
# 3. Check Rate Limit
|
|
67
|
+
allowed, user = user_manager.allow_request(ip)
|
|
68
|
+
if not allowed:
|
|
69
|
+
raise HTTPException(
|
|
70
|
+
status_code=429,
|
|
71
|
+
detail=f"Daily limit reached. ({user['llm_requests']}/{config.DAILY_LIMIT})"
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
# 4. Invoke RAG engine
|
|
75
|
+
answer = engine.chat(prompt, ip=ip)
|
|
76
|
+
|
|
77
|
+
# 5. Save response to cache
|
|
78
|
+
response_cache.save(prompt, answer)
|
|
79
|
+
|
|
80
|
+
return {
|
|
81
|
+
"success": True,
|
|
82
|
+
"cached": False,
|
|
83
|
+
"provider": "rag",
|
|
84
|
+
"remaining_questions": user["remaining"],
|
|
85
|
+
"answer": answer
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
return app
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
# Cache Package Initialization
|