Spaces:
Running
Running
Commit ·
bf7338b
1
Parent(s): 1b08040
Fix file upload UI refresh and add Metadata Filters UI
Browse files- Dockerfile +37 -0
- RAG_FULL_APPLICATION_BACKEND/Dockerfile +2 -2
- RAG_FULL_APPLICATION_BACKEND/README.md +23 -0
- RAG_FULL_APPLICATION_BACKEND/app/config.py +2 -2
- RAG_FULL_APPLICATION_BACKEND/app/main.py +19 -1
- RAG_FULL_APPLICATION_BACKEND/app/routers/auth.py +3 -5
- RAG_FULL_APPLICATION_BACKEND/app/techniques/metadata_filter.py +4 -0
- RAG_FULL_APPLICATION_BACKEND/requirements.txt +1 -0
- RAG_FULL_APPLICATION_FRONTEND/src/App.jsx +32 -6
- RAG_FULL_APPLICATION_FRONTEND/src/api/client.js +1 -1
- RAG_FULL_APPLICATION_FRONTEND/src/components/FileUpload.jsx +3 -1
- RAG_FULL_APPLICATION_FRONTEND/src/store/pipelineStore.js +2 -0
- README.md +23 -0
- patch_auth.py +26 -0
Dockerfile
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Stage 1: Build frontend
|
| 2 |
+
FROM node:20-alpine AS frontend-builder
|
| 3 |
+
WORKDIR /frontend
|
| 4 |
+
COPY RAG_FULL_APPLICATION_FRONTEND/package*.json ./
|
| 5 |
+
RUN npm install
|
| 6 |
+
COPY RAG_FULL_APPLICATION_FRONTEND/ ./
|
| 7 |
+
RUN npm run build
|
| 8 |
+
|
| 9 |
+
# Stage 2: Build backend & final monolithic image
|
| 10 |
+
FROM python:3.12-slim
|
| 11 |
+
WORKDIR /app
|
| 12 |
+
|
| 13 |
+
# System dependencies for python-docx, tiktoken, Tesseract OCR, etc.
|
| 14 |
+
RUN apt-get update && apt-get install -y \
|
| 15 |
+
build-essential libpq-dev tesseract-ocr libmagic1 libgl1 && \
|
| 16 |
+
rm -rf /var/lib/apt/lists/*
|
| 17 |
+
|
| 18 |
+
COPY RAG_FULL_APPLICATION_BACKEND/requirements.txt ./
|
| 19 |
+
RUN pip install --no-cache-dir -r requirements.txt
|
| 20 |
+
|
| 21 |
+
# Download cross-encoder model at build time to avoid slow cold start
|
| 22 |
+
RUN python -c "from sentence_transformers import CrossEncoder; \
|
| 23 |
+
CrossEncoder('cross-encoder/ms-marco-MiniLM-L-6-v2')"
|
| 24 |
+
|
| 25 |
+
# Copy backend files
|
| 26 |
+
COPY RAG_FULL_APPLICATION_BACKEND/ ./
|
| 27 |
+
|
| 28 |
+
# Copy built frontend assets to the static directory
|
| 29 |
+
COPY --from=frontend-builder /frontend/dist ./static
|
| 30 |
+
|
| 31 |
+
# Create data directories and set permissions for Hugging Face non-root user (UID 1000)
|
| 32 |
+
RUN mkdir -p data/uploads data/bm25_indexes data/cache && \
|
| 33 |
+
chown -R 1000:1000 /app
|
| 34 |
+
|
| 35 |
+
EXPOSE 7860
|
| 36 |
+
|
| 37 |
+
CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "7860", "--workers", "2"]
|
RAG_FULL_APPLICATION_BACKEND/Dockerfile
CHANGED
|
@@ -19,6 +19,6 @@ COPY . .
|
|
| 19 |
# Create data dirs
|
| 20 |
RUN mkdir -p data/uploads data/bm25_indexes data/cache
|
| 21 |
|
| 22 |
-
EXPOSE
|
| 23 |
|
| 24 |
-
CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "
|
|
|
|
| 19 |
# Create data dirs
|
| 20 |
RUN mkdir -p data/uploads data/bm25_indexes data/cache
|
| 21 |
|
| 22 |
+
EXPOSE 7860
|
| 23 |
|
| 24 |
+
CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "7860", "--workers", "2"]
|
RAG_FULL_APPLICATION_BACKEND/README.md
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
---
|
| 2 |
+
title: RAG Backend
|
| 3 |
+
emoji: 🚀
|
| 4 |
+
colorFrom: blue
|
| 5 |
+
colorTo: indigo
|
| 6 |
+
sdk: docker
|
| 7 |
+
app_port: 7860
|
| 8 |
+
pinned: false
|
| 9 |
+
---
|
| 10 |
+
|
| 11 |
+
# RAG Multimodal Pipeline Backend
|
| 12 |
+
This is the backend API for the JEE/NEET RAG pipeline, hosted on Hugging Face Spaces.
|
| 13 |
+
|
| 14 |
+
## Configuration
|
| 15 |
+
All environment variables are managed via Hugging Face Secrets.
|
| 16 |
+
|
| 17 |
+
## Tech Stack
|
| 18 |
+
- FastAPI
|
| 19 |
+
- Uvicorn
|
| 20 |
+
- Docker
|
| 21 |
+
- Sentence Transformers
|
| 22 |
+
- Redis (External via Upstash)
|
| 23 |
+
- Supabase (External)
|
RAG_FULL_APPLICATION_BACKEND/app/config.py
CHANGED
|
@@ -31,7 +31,7 @@ class Settings(BaseSettings):
|
|
| 31 |
VISION_SPACE_URL: str = "Qwen/Qwen3-VL-30B-A3B-Demo"
|
| 32 |
|
| 33 |
# Redis
|
| 34 |
-
REDIS_URL: str
|
| 35 |
CACHE_TTL_SECONDS: int = 3600
|
| 36 |
|
| 37 |
# Auth
|
|
@@ -53,7 +53,7 @@ class Settings(BaseSettings):
|
|
| 53 |
MAX_FILE_SIZE_MB: int = 50
|
| 54 |
|
| 55 |
# CORS
|
| 56 |
-
CORS_ORIGINS: str
|
| 57 |
|
| 58 |
class Config:
|
| 59 |
env_file = ".env"
|
|
|
|
| 31 |
VISION_SPACE_URL: str = "Qwen/Qwen3-VL-30B-A3B-Demo"
|
| 32 |
|
| 33 |
# Redis
|
| 34 |
+
REDIS_URL: str = ""
|
| 35 |
CACHE_TTL_SECONDS: int = 3600
|
| 36 |
|
| 37 |
# Auth
|
|
|
|
| 53 |
MAX_FILE_SIZE_MB: int = 50
|
| 54 |
|
| 55 |
# CORS
|
| 56 |
+
CORS_ORIGINS: str = ""
|
| 57 |
|
| 58 |
class Config:
|
| 59 |
env_file = ".env"
|
RAG_FULL_APPLICATION_BACKEND/app/main.py
CHANGED
|
@@ -1,8 +1,11 @@
|
|
| 1 |
from fastapi import FastAPI, WebSocket, Depends
|
| 2 |
from fastapi.middleware.cors import CORSMiddleware
|
|
|
|
|
|
|
| 3 |
from .config import settings
|
| 4 |
from .utils.ws_manager import ws_manager
|
| 5 |
import logging
|
|
|
|
| 6 |
|
| 7 |
# Setup Logger
|
| 8 |
logging.basicConfig(level=logging.INFO)
|
|
@@ -11,7 +14,8 @@ logger = logging.getLogger(__name__)
|
|
| 11 |
app = FastAPI(title="RAG Pipeline API", version="3.0.0")
|
| 12 |
|
| 13 |
# CORS
|
| 14 |
-
origins = settings.CORS_ORIGINS.split(",")
|
|
|
|
| 15 |
app.add_middleware(
|
| 16 |
CORSMiddleware,
|
| 17 |
allow_origins=origins,
|
|
@@ -44,3 +48,17 @@ async def pipeline_ws(websocket: WebSocket, job_id: str, token: str):
|
|
| 44 |
logger.error(f"WebSocket error for job {job_id}: {e}")
|
| 45 |
finally:
|
| 46 |
await ws_manager.disconnect(job_id, "anonymous")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
from fastapi import FastAPI, WebSocket, Depends
|
| 2 |
from fastapi.middleware.cors import CORSMiddleware
|
| 3 |
+
from fastapi.staticfiles import StaticFiles
|
| 4 |
+
from fastapi.responses import FileResponse
|
| 5 |
from .config import settings
|
| 6 |
from .utils.ws_manager import ws_manager
|
| 7 |
import logging
|
| 8 |
+
import os
|
| 9 |
|
| 10 |
# Setup Logger
|
| 11 |
logging.basicConfig(level=logging.INFO)
|
|
|
|
| 14 |
app = FastAPI(title="RAG Pipeline API", version="3.0.0")
|
| 15 |
|
| 16 |
# CORS
|
| 17 |
+
# origins = settings.CORS_ORIGINS.split(",")
|
| 18 |
+
origins = settings.CORS_ORIGINS.split(",") if settings.CORS_ORIGINS else ["*"]
|
| 19 |
app.add_middleware(
|
| 20 |
CORSMiddleware,
|
| 21 |
allow_origins=origins,
|
|
|
|
| 48 |
logger.error(f"WebSocket error for job {job_id}: {e}")
|
| 49 |
finally:
|
| 50 |
await ws_manager.disconnect(job_id, "anonymous")
|
| 51 |
+
|
| 52 |
+
# Serve frontend static files in production monolith
|
| 53 |
+
static_dir = os.path.join(os.path.dirname(os.path.abspath(__file__)), "..", "static")
|
| 54 |
+
if os.path.exists(static_dir):
|
| 55 |
+
app.mount("/assets", StaticFiles(directory=os.path.join(static_dir, "assets")), name="assets")
|
| 56 |
+
|
| 57 |
+
@app.get("/{catchall:path}")
|
| 58 |
+
async def serve_frontend(catchall: str):
|
| 59 |
+
# Prevent catching API calls
|
| 60 |
+
if catchall.startswith(("auth", "ingest", "query", "health", "ws")):
|
| 61 |
+
return None
|
| 62 |
+
index_file = os.path.join(static_dir, "index.html")
|
| 63 |
+
if os.path.exists(index_file):
|
| 64 |
+
return FileResponse(index_file)
|
RAG_FULL_APPLICATION_BACKEND/app/routers/auth.py
CHANGED
|
@@ -46,12 +46,10 @@ async def login(form_data: OAuth2PasswordRequestForm = Depends()):
|
|
| 46 |
|
| 47 |
@router.post("/seed_admin")
|
| 48 |
async def seed_admin():
|
| 49 |
-
|
| 50 |
-
hashed = get_password_hash("admin123")
|
| 51 |
try:
|
| 52 |
-
|
| 53 |
supabase_service.client.table("users").delete().eq("username", "admin").execute()
|
| 54 |
-
|
| 55 |
supabase_service.client.table("users").insert({
|
| 56 |
"username": "admin",
|
| 57 |
"password_hash": hashed
|
|
@@ -60,4 +58,4 @@ async def seed_admin():
|
|
| 60 |
return {"msg": "Admin user created/reset (admin / admin123)"}
|
| 61 |
except Exception as e:
|
| 62 |
logger.error(f"Seeding failed: {e}")
|
| 63 |
-
return {"
|
|
|
|
| 46 |
|
| 47 |
@router.post("/seed_admin")
|
| 48 |
async def seed_admin():
|
| 49 |
+
import traceback
|
|
|
|
| 50 |
try:
|
| 51 |
+
hashed = get_password_hash("admin123")
|
| 52 |
supabase_service.client.table("users").delete().eq("username", "admin").execute()
|
|
|
|
| 53 |
supabase_service.client.table("users").insert({
|
| 54 |
"username": "admin",
|
| 55 |
"password_hash": hashed
|
|
|
|
| 58 |
return {"msg": "Admin user created/reset (admin / admin123)"}
|
| 59 |
except Exception as e:
|
| 60 |
logger.error(f"Seeding failed: {e}")
|
| 61 |
+
return {"error": str(e), "traceback": traceback.format_exc()}
|
RAG_FULL_APPLICATION_BACKEND/app/techniques/metadata_filter.py
CHANGED
|
@@ -6,6 +6,10 @@ class MetadataFilter(BaseRAGTechnique):
|
|
| 6 |
async def retrieve(self, query: str, document_id: str, top_k: int, **kwargs) -> List[Dict[str, Any]]:
|
| 7 |
filters = kwargs.get("filters", {})
|
| 8 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 9 |
# 1. SQL Pre-filtering
|
| 10 |
await self.emit("FILTER", "#D97706", f"SQL filter: {filters}...")
|
| 11 |
matching_ids = await self.supabase.filter_chunk_ids(document_id, self.user_id, filters)
|
|
|
|
| 6 |
async def retrieve(self, query: str, document_id: str, top_k: int, **kwargs) -> List[Dict[str, Any]]:
|
| 7 |
filters = kwargs.get("filters", {})
|
| 8 |
|
| 9 |
+
if not filters:
|
| 10 |
+
await self.emit("DONE", "#EF4444", "Metadata Filter selected but no filters provided.")
|
| 11 |
+
return []
|
| 12 |
+
|
| 13 |
# 1. SQL Pre-filtering
|
| 14 |
await self.emit("FILTER", "#D97706", f"SQL filter: {filters}...")
|
| 15 |
matching_ids = await self.supabase.filter_chunk_ids(document_id, self.user_id, filters)
|
RAG_FULL_APPLICATION_BACKEND/requirements.txt
CHANGED
|
@@ -7,6 +7,7 @@ asyncpg==0.29.0
|
|
| 7 |
redis==5.0.6
|
| 8 |
python-jose[cryptography]==3.3.0
|
| 9 |
passlib[bcrypt]==1.7.4
|
|
|
|
| 10 |
python-multipart==0.0.9
|
| 11 |
httpx==0.27.0
|
| 12 |
gradio_client>=1.0.0
|
|
|
|
| 7 |
redis==5.0.6
|
| 8 |
python-jose[cryptography]==3.3.0
|
| 9 |
passlib[bcrypt]==1.7.4
|
| 10 |
+
bcrypt==4.0.1
|
| 11 |
python-multipart==0.0.9
|
| 12 |
httpx==0.27.0
|
| 13 |
gradio_client>=1.0.0
|
RAG_FULL_APPLICATION_FRONTEND/src/App.jsx
CHANGED
|
@@ -15,7 +15,7 @@ function App() {
|
|
| 15 |
const [password, setPassword] = useState('admin123');
|
| 16 |
const [query, setQuery] = useState('');
|
| 17 |
const [technique, setTechnique] = useState('hybrid');
|
| 18 |
-
const [
|
| 19 |
|
| 20 |
const {
|
| 21 |
documents, setDocuments,
|
|
@@ -23,7 +23,8 @@ function App() {
|
|
| 23 |
currentAnswer, setAnswer,
|
| 24 |
sources, setSources,
|
| 25 |
steps, addStep, clearSteps,
|
| 26 |
-
setQuerying, isQuerying
|
|
|
|
| 27 |
} = usePipelineStore();
|
| 28 |
|
| 29 |
const ws = useRef(null);
|
|
@@ -39,7 +40,7 @@ function App() {
|
|
| 39 |
useEffect(() => {
|
| 40 |
if (activeJob && isAuthenticated) {
|
| 41 |
const token = sessionStorage.getItem('token');
|
| 42 |
-
const apiBase = import.meta.env.VITE_API_BASE_URL ||
|
| 43 |
const wsProtocol = apiBase.startsWith('https') ? 'wss' : 'ws';
|
| 44 |
const wsHost = apiBase.replace(/^https?:\/\//, '');
|
| 45 |
const wsUrl = `${wsProtocol}://${wsHost}/ws/pipeline/${activeJob}?token=${token}`;
|
|
@@ -82,17 +83,29 @@ function App() {
|
|
| 82 |
setShowSettings(false);
|
| 83 |
|
| 84 |
try {
|
| 85 |
-
const
|
| 86 |
query,
|
| 87 |
document_id: selectedDoc.id,
|
| 88 |
technique
|
| 89 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 90 |
setAnswer(data.answer);
|
| 91 |
setSources(data.sources);
|
| 92 |
setActiveJob(data.job_id);
|
| 93 |
} catch (error) {
|
| 94 |
console.error('Search failed', error);
|
| 95 |
-
addStep({ step: 'ERROR', detail: 'Search failed. Please try again.', color: '#EF4444' });
|
| 96 |
} finally {
|
| 97 |
setQuerying(false);
|
| 98 |
}
|
|
@@ -271,6 +284,19 @@ function App() {
|
|
| 271 |
<label className="text-[10px] text-gray-600 font-bold uppercase">LLM Temp</label>
|
| 272 |
<input type="number" defaultValue={0.1} step={0.1} className="w-full bg-surface-800 border border-surface-700 rounded px-2 py-1 text-xs outline-none focus:border-accent-500" />
|
| 273 |
</div>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 274 |
</div>
|
| 275 |
</motion.div>
|
| 276 |
)}
|
|
|
|
| 15 |
const [password, setPassword] = useState('admin123');
|
| 16 |
const [query, setQuery] = useState('');
|
| 17 |
const [technique, setTechnique] = useState('hybrid');
|
| 18 |
+
const [metadataFilters, setMetadataFilters] = useState('{}');
|
| 19 |
|
| 20 |
const {
|
| 21 |
documents, setDocuments,
|
|
|
|
| 23 |
currentAnswer, setAnswer,
|
| 24 |
sources, setSources,
|
| 25 |
steps, addStep, clearSteps,
|
| 26 |
+
setQuerying, isQuerying,
|
| 27 |
+
activeJob, setActiveJob
|
| 28 |
} = usePipelineStore();
|
| 29 |
|
| 30 |
const ws = useRef(null);
|
|
|
|
| 40 |
useEffect(() => {
|
| 41 |
if (activeJob && isAuthenticated) {
|
| 42 |
const token = sessionStorage.getItem('token');
|
| 43 |
+
const apiBase = import.meta.env.VITE_API_BASE_URL || window.location.origin;
|
| 44 |
const wsProtocol = apiBase.startsWith('https') ? 'wss' : 'ws';
|
| 45 |
const wsHost = apiBase.replace(/^https?:\/\//, '');
|
| 46 |
const wsUrl = `${wsProtocol}://${wsHost}/ws/pipeline/${activeJob}?token=${token}`;
|
|
|
|
| 83 |
setShowSettings(false);
|
| 84 |
|
| 85 |
try {
|
| 86 |
+
const payload = {
|
| 87 |
query,
|
| 88 |
document_id: selectedDoc.id,
|
| 89 |
technique
|
| 90 |
+
};
|
| 91 |
+
|
| 92 |
+
if (technique === 'meta') {
|
| 93 |
+
try {
|
| 94 |
+
payload.filters = JSON.parse(metadataFilters);
|
| 95 |
+
} catch (e) {
|
| 96 |
+
alert("Invalid JSON format for Metadata Filters. Please check your input.");
|
| 97 |
+
setQuerying(false);
|
| 98 |
+
return;
|
| 99 |
+
}
|
| 100 |
+
}
|
| 101 |
+
|
| 102 |
+
const { data } = await api.post('/query/search', payload);
|
| 103 |
setAnswer(data.answer);
|
| 104 |
setSources(data.sources);
|
| 105 |
setActiveJob(data.job_id);
|
| 106 |
} catch (error) {
|
| 107 |
console.error('Search failed', error);
|
| 108 |
+
addStep({ step: 'ERROR', detail: error?.response?.data?.detail || 'Search failed. Please try again.', color: '#EF4444' });
|
| 109 |
} finally {
|
| 110 |
setQuerying(false);
|
| 111 |
}
|
|
|
|
| 284 |
<label className="text-[10px] text-gray-600 font-bold uppercase">LLM Temp</label>
|
| 285 |
<input type="number" defaultValue={0.1} step={0.1} className="w-full bg-surface-800 border border-surface-700 rounded px-2 py-1 text-xs outline-none focus:border-accent-500" />
|
| 286 |
</div>
|
| 287 |
+
<div className="col-span-2 space-y-1 pt-2 border-t border-surface-800">
|
| 288 |
+
<label className="text-[10px] text-gray-600 font-bold uppercase flex justify-between">
|
| 289 |
+
<span>Metadata Filters (JSON)</span>
|
| 290 |
+
{technique === 'meta' && <span className="text-orange-500">Required for Meta search</span>}
|
| 291 |
+
</label>
|
| 292 |
+
<input
|
| 293 |
+
type="text"
|
| 294 |
+
value={metadataFilters}
|
| 295 |
+
onChange={e => setMetadataFilters(e.target.value)}
|
| 296 |
+
placeholder='e.g., {"page": 1, "section": "Introduction"}'
|
| 297 |
+
className={`w-full bg-surface-800 border ${technique === 'meta' ? 'border-orange-500/50 focus:border-orange-500' : 'border-surface-700 focus:border-accent-500'} rounded px-2 py-1 text-xs outline-none font-mono text-gray-400`}
|
| 298 |
+
/>
|
| 299 |
+
</div>
|
| 300 |
</div>
|
| 301 |
</motion.div>
|
| 302 |
)}
|
RAG_FULL_APPLICATION_FRONTEND/src/api/client.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
| 1 |
import axios from 'axios';
|
| 2 |
|
| 3 |
const api = axios.create({
|
| 4 |
-
baseURL: import.meta.env.VITE_API_BASE_URL ||
|
| 5 |
});
|
| 6 |
|
| 7 |
// Interceptor for JWT
|
|
|
|
| 1 |
import axios from 'axios';
|
| 2 |
|
| 3 |
const api = axios.create({
|
| 4 |
+
baseURL: import.meta.env.VITE_API_BASE_URL || window.location.origin,
|
| 5 |
});
|
| 6 |
|
| 7 |
// Interceptor for JWT
|
RAG_FULL_APPLICATION_FRONTEND/src/components/FileUpload.jsx
CHANGED
|
@@ -14,7 +14,7 @@ const STRATEGIES = [
|
|
| 14 |
|
| 15 |
export default function FileUpload() {
|
| 16 |
const [file, setFile] = useState(null);
|
| 17 |
-
const { setIngesting, isIngesting } = usePipelineStore();
|
| 18 |
const [status, setStatus] = useState('idle'); // idle | loading | success | error
|
| 19 |
const [errorMsg, setErrorMsg] = useState('');
|
| 20 |
|
|
@@ -36,7 +36,9 @@ export default function FileUpload() {
|
|
| 36 |
formData.append('strategy', strategy);
|
| 37 |
|
| 38 |
try {
|
|
|
|
| 39 |
const { data } = await api.post('/ingest/upload', formData);
|
|
|
|
| 40 |
setStatus('success');
|
| 41 |
setTimeout(() => {
|
| 42 |
setStatus('idle');
|
|
|
|
| 14 |
|
| 15 |
export default function FileUpload() {
|
| 16 |
const [file, setFile] = useState(null);
|
| 17 |
+
const { setIngesting, isIngesting, setActiveJob, clearSteps } = usePipelineStore();
|
| 18 |
const [status, setStatus] = useState('idle'); // idle | loading | success | error
|
| 19 |
const [errorMsg, setErrorMsg] = useState('');
|
| 20 |
|
|
|
|
| 36 |
formData.append('strategy', strategy);
|
| 37 |
|
| 38 |
try {
|
| 39 |
+
clearSteps();
|
| 40 |
const { data } = await api.post('/ingest/upload', formData);
|
| 41 |
+
setActiveJob(data.job_id);
|
| 42 |
setStatus('success');
|
| 43 |
setTimeout(() => {
|
| 44 |
setStatus('idle');
|
RAG_FULL_APPLICATION_FRONTEND/src/store/pipelineStore.js
CHANGED
|
@@ -8,6 +8,7 @@ export const usePipelineStore = create((set) => ({
|
|
| 8 |
steps: [],
|
| 9 |
isQuerying: false,
|
| 10 |
isIngesting: false,
|
|
|
|
| 11 |
|
| 12 |
setDocuments: (documents) => set({ documents }),
|
| 13 |
setSelectedDoc: (selectedDoc) => set({ selectedDoc }),
|
|
@@ -17,4 +18,5 @@ export const usePipelineStore = create((set) => ({
|
|
| 17 |
clearSteps: () => set({ steps: [] }),
|
| 18 |
setQuerying: (isQuerying) => set({ isQuerying }),
|
| 19 |
setIngesting: (isIngesting) => set({ isIngesting }),
|
|
|
|
| 20 |
}));
|
|
|
|
| 8 |
steps: [],
|
| 9 |
isQuerying: false,
|
| 10 |
isIngesting: false,
|
| 11 |
+
activeJob: null,
|
| 12 |
|
| 13 |
setDocuments: (documents) => set({ documents }),
|
| 14 |
setSelectedDoc: (selectedDoc) => set({ selectedDoc }),
|
|
|
|
| 18 |
clearSteps: () => set({ steps: [] }),
|
| 19 |
setQuerying: (isQuerying) => set({ isQuerying }),
|
| 20 |
setIngesting: (isIngesting) => set({ isIngesting }),
|
| 21 |
+
setActiveJob: (activeJob) => set({ activeJob }),
|
| 22 |
}));
|
README.md
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
---
|
| 2 |
+
title: RAG Monolith
|
| 3 |
+
emoji: 🚀
|
| 4 |
+
colorFrom: blue
|
| 5 |
+
colorTo: indigo
|
| 6 |
+
sdk: docker
|
| 7 |
+
app_port: 7860
|
| 8 |
+
pinned: false
|
| 9 |
+
---
|
| 10 |
+
|
| 11 |
+
# RAG Multimodal Pipeline
|
| 12 |
+
|
| 13 |
+
This repository hosts a monolithic full-stack deployment of the RAG Multimodal Pipeline on Hugging Face Spaces.
|
| 14 |
+
|
| 15 |
+
## Setup & Secrets
|
| 16 |
+
|
| 17 |
+
Make sure to configure the following Secrets in your Space settings:
|
| 18 |
+
1. `SUPABASE_URL`
|
| 19 |
+
2. `SUPABASE_KEY`
|
| 20 |
+
3. `SUPABASE_DB_URL`
|
| 21 |
+
4. `REDIS_URL`
|
| 22 |
+
5. `JWT_SECRET_KEY`
|
| 23 |
+
6. `CORS_ORIGINS` (value can be `*` or the final Space URL)
|
patch_auth.py
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import re
|
| 2 |
+
|
| 3 |
+
with open("/Content/AI-PROJECTS/RAG_FULL_APPLICATION/RAG_FULL_APPLICATION_BACKEND/app/routers/auth.py", "r") as f:
|
| 4 |
+
content = f.read()
|
| 5 |
+
|
| 6 |
+
new_seed_admin = """@router.post("/seed_admin")
|
| 7 |
+
async def seed_admin():
|
| 8 |
+
import traceback
|
| 9 |
+
try:
|
| 10 |
+
hashed = get_password_hash("admin123")
|
| 11 |
+
supabase_service.client.table("users").delete().eq("username", "admin").execute()
|
| 12 |
+
supabase_service.client.table("users").insert({
|
| 13 |
+
"username": "admin",
|
| 14 |
+
"password_hash": hashed
|
| 15 |
+
}).execute()
|
| 16 |
+
logger.info("Admin user seeded successfully.")
|
| 17 |
+
return {"msg": "Admin user created/reset (admin / admin123)"}
|
| 18 |
+
except Exception as e:
|
| 19 |
+
logger.error(f"Seeding failed: {e}")
|
| 20 |
+
return {"error": str(e), "traceback": traceback.format_exc()}
|
| 21 |
+
"""
|
| 22 |
+
|
| 23 |
+
content = re.sub(r'@router\.post\("/seed_admin"\).*?(?=\n@|\Z)', new_seed_admin, content, flags=re.DOTALL)
|
| 24 |
+
|
| 25 |
+
with open("/Content/AI-PROJECTS/RAG_FULL_APPLICATION/RAG_FULL_APPLICATION_BACKEND/app/routers/auth.py", "w") as f:
|
| 26 |
+
f.write(content)
|