ranjithkumar111 commited on
Commit
879da49
Β·
verified Β·
1 Parent(s): 734308d

Upload 4 files

Browse files
Files changed (4) hide show
  1. Dockerfile +32 -0
  2. README.md +25 -11
  3. requirements.txt +53 -0
  4. streamlit_app.py +241 -0
Dockerfile ADDED
@@ -0,0 +1,32 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # File : Dockerfile
2
+ # Purpose : Combined FastAPI + Streamlit in one container for HF Spaces
3
+
4
+ FROM python:3.11-slim
5
+
6
+ WORKDIR /app
7
+
8
+ # System dependencies
9
+ RUN apt-get update && apt-get install -y \
10
+ build-essential \
11
+ libpq-dev \
12
+ curl \
13
+ && rm -rf /var/lib/apt/lists/*
14
+
15
+ # Install Python dependencies
16
+ COPY requirements.txt .
17
+ RUN pip install --no-cache-dir --upgrade pip && \
18
+ pip install --no-cache-dir -r requirements.txt && \
19
+ pip install --no-cache-dir streamlit
20
+
21
+ # Copy application code
22
+ COPY app/ ./app/
23
+ COPY streamlit_app.py .
24
+
25
+ # Hugging Face Spaces runs as non-root user 1000
26
+ RUN useradd -m -u 1000 appuser && chown -R appuser:appuser /app
27
+ USER appuser
28
+
29
+ # Expose port 7860 (HF Spaces requirement)
30
+ EXPOSE 7860
31
+
32
+ CMD ["/bin/bash", "-c", "uvicorn app.main:app --host 0.0.0.0 --port 8000 & sleep 5 && streamlit run streamlit_app.py --server.port 7860 --server.address 0.0.0.0"]
README.md CHANGED
@@ -1,11 +1,25 @@
1
- ---
2
- title: Banking Rag Ui
3
- emoji: ⚑
4
- colorFrom: gray
5
- colorTo: indigo
6
- sdk: docker
7
- pinned: false
8
- license: apache-2.0
9
- ---
10
-
11
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ title: Banking RAG Platform
3
+ emoji: 🏦
4
+ colorFrom: blue
5
+ colorTo: green
6
+ sdk: docker
7
+ pinned: false
8
+ ---
9
+
10
+ # Banking RAG Platform
11
+
12
+ Enterprise RAG system built on LangChain + Groq for RBI and Finance documents.
13
+
14
+ ## Stack
15
+
16
+ | Layer | Technology |
17
+ |---|---|
18
+ | Backend | FastAPI |
19
+ | AI Framework | LangChain + Groq (llama-3.3-70b-versatile) |
20
+ | Observability | LangSmith |
21
+ | Vector DB | ChromaDB |
22
+ | Cache | Upstash Redis |
23
+ | Database | Neon PostgreSQL |
24
+ | Embeddings | BAAI/bge-small-en |
25
+ | Deployment | Hugging Face Spaces (Docker) |
requirements.txt ADDED
@@ -0,0 +1,53 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # File : requirements.txt
2
+ # Purpose : All pinned dependencies for Banking RAG Platform
3
+
4
+ # --- Web Framework ---
5
+ fastapi==0.111.0
6
+ uvicorn[standard]==0.29.0
7
+ python-multipart==0.0.9
8
+
9
+ # --- Auth ---
10
+ python-jose[cryptography]==3.3.0
11
+ passlib[bcrypt]==1.7.4
12
+
13
+ # --- Database ---
14
+ sqlalchemy==2.0.30
15
+ asyncpg==0.29.0
16
+ psycopg2-binary==2.9.9
17
+ alembic==1.13.1
18
+
19
+ # --- Vector Store ---
20
+ chromadb==0.5.3
21
+
22
+ # --- LangChain + Groq ---
23
+ langchain==0.2.5
24
+ langchain-groq==0.1.6
25
+ langchain-community==0.2.5
26
+ langchain-huggingface==0.0.3
27
+ langsmith==0.1.77
28
+
29
+ # --- Embeddings ---
30
+ sentence-transformers==3.0.1
31
+ torch==2.3.0
32
+ transformers==4.41.2
33
+
34
+ # --- Document Parsing ---
35
+ pypdf==4.2.0
36
+ python-docx==1.1.2
37
+ pdfplumber==0.11.0
38
+
39
+ # --- Retrieval ---
40
+ rank-bm25==0.2.2
41
+
42
+ # --- Caching ---
43
+ redis==5.0.4
44
+ hiredis==2.3.2
45
+
46
+ # --- Async + Utils ---
47
+ httpx==0.27.0
48
+ python-dotenv==1.0.1
49
+ pydantic==2.7.1
50
+ pydantic-settings==2.3.0
51
+ aiofiles==23.2.1
52
+ tenacity==8.3.0
53
+ numpy==1.26.4
streamlit_app.py ADDED
@@ -0,0 +1,241 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # File : streamlit_app.py
2
+ # Purpose : Streamlit frontend for Banking RAG Platform
3
+
4
+ import streamlit as st
5
+ import requests
6
+ from pathlib import Path
7
+
8
+ # ─── Config ───────────────────────────────────────────────
9
+ API_URL = "http://localhost:8000"
10
+
11
+ st.set_page_config(
12
+ page_title="Banking RAG Platform",
13
+ page_icon="🏦",
14
+ layout="wide",
15
+ initial_sidebar_state="expanded",
16
+ )
17
+
18
+ # ─── Session State ────────────────────────────────────────
19
+ if "token" not in st.session_state:
20
+ st.session_state.token = None
21
+ if "username" not in st.session_state:
22
+ st.session_state.username = None
23
+ if "chat_history" not in st.session_state:
24
+ st.session_state.chat_history = []
25
+
26
+ # ─── Helpers ──────────────────────────────────────────────
27
+ def auth_headers():
28
+ return {"Authorization": f"Bearer {st.session_state.token}"}
29
+
30
+
31
+ def api_post(endpoint, payload=None, files=None, auth=False):
32
+ headers = auth_headers() if auth else {}
33
+ try:
34
+ if files:
35
+ r = requests.post(f"{API_URL}{endpoint}", headers=headers, files=files, timeout=120)
36
+ else:
37
+ r = requests.post(f"{API_URL}{endpoint}", headers=headers, json=payload, timeout=120)
38
+ return r
39
+ except requests.exceptions.ConnectionError:
40
+ st.error("Cannot connect to backend.")
41
+ return None
42
+ except requests.exceptions.Timeout:
43
+ st.error("Request timed out. Please try again.")
44
+ return None
45
+
46
+
47
+ def api_get(endpoint, auth=False):
48
+ headers = auth_headers() if auth else {}
49
+ try:
50
+ r = requests.get(f"{API_URL}{endpoint}", headers=headers, timeout=30)
51
+ return r
52
+ except requests.exceptions.ConnectionError:
53
+ st.error("Cannot connect to backend.")
54
+ return None
55
+
56
+
57
+ # ─── Sidebar ──────────────────────────────────────────────
58
+ with st.sidebar:
59
+ st.title("🏦 Banking RAG")
60
+ st.markdown("---")
61
+
62
+ if st.session_state.token:
63
+ st.success(f"Logged in as **{st.session_state.username}**")
64
+ if st.button("Logout", use_container_width=True):
65
+ st.session_state.token = None
66
+ st.session_state.username = None
67
+ st.session_state.chat_history = []
68
+ st.rerun()
69
+ st.markdown("---")
70
+ page = st.radio(
71
+ "Navigate",
72
+ ["πŸ’¬ Ask Documents", "πŸ“ My Documents", "πŸ“€ Upload"],
73
+ label_visibility="collapsed",
74
+ )
75
+ else:
76
+ page = "πŸ” Login"
77
+
78
+ st.markdown("---")
79
+ st.caption("Built with LangChain + Groq + ChromaDB")
80
+ st.caption(f"Backend: {API_URL}")
81
+
82
+
83
+ # ─── Login / Register Page ────────────────────────────────
84
+ if not st.session_state.token:
85
+ st.title("🏦 Banking RAG Platform")
86
+ st.markdown("Enterprise AI assistant for RBI and Finance documents.")
87
+ st.markdown("---")
88
+
89
+ tab1, tab2 = st.tabs(["Login", "Register"])
90
+
91
+ with tab1:
92
+ st.subheader("Login")
93
+ username = st.text_input("Username", key="login_user")
94
+ password = st.text_input("Password", type="password", key="login_pass")
95
+ if st.button("Login", use_container_width=True, type="primary"):
96
+ if username and password:
97
+ r = api_post("/auth/login", {"username": username, "password": password})
98
+ if r and r.status_code == 200:
99
+ data = r.json()
100
+ st.session_state.token = data["access_token"]
101
+ st.session_state.username = username
102
+ st.success("Login successful!")
103
+ st.rerun()
104
+ elif r:
105
+ st.error(r.json().get("detail", "Login failed"))
106
+ else:
107
+ st.warning("Enter username and password")
108
+
109
+ with tab2:
110
+ st.subheader("Register")
111
+ reg_email = st.text_input("Email", key="reg_email")
112
+ reg_username = st.text_input("Username", key="reg_user")
113
+ reg_password = st.text_input("Password", type="password", key="reg_pass")
114
+ if st.button("Register", use_container_width=True, type="primary"):
115
+ if reg_email and reg_username and reg_password:
116
+ r = api_post("/auth/register", {
117
+ "email": reg_email,
118
+ "username": reg_username,
119
+ "password": reg_password,
120
+ })
121
+ if r and r.status_code == 201:
122
+ st.success("Account created! Please login.")
123
+ elif r:
124
+ st.error(r.json().get("detail", "Registration failed"))
125
+ else:
126
+ st.warning("Fill all fields")
127
+
128
+
129
+ # ─── Ask Documents Page ───────────────────────────────────
130
+ elif page == "πŸ’¬ Ask Documents":
131
+ st.title("πŸ’¬ Ask Your Documents")
132
+ st.markdown("Ask questions about your uploaded RBI and Finance documents.")
133
+ st.markdown("---")
134
+
135
+ # Chat history
136
+ for msg in st.session_state.chat_history:
137
+ with st.chat_message(msg["role"]):
138
+ st.markdown(msg["content"])
139
+ if msg["role"] == "assistant" and "citations" in msg:
140
+ if msg["citations"]:
141
+ with st.expander("πŸ“„ Sources"):
142
+ for i, cite in enumerate(msg["citations"], 1):
143
+ st.markdown(f"**[{i}]** Page {cite.get('page_number', 'N/A')} β€” {cite.get('content_preview', '')}")
144
+
145
+ # Query input
146
+ query = st.chat_input("Ask something about your documents...")
147
+ if query:
148
+ st.session_state.chat_history.append({"role": "user", "content": query})
149
+ with st.chat_message("user"):
150
+ st.markdown(query)
151
+
152
+ with st.chat_message("assistant"):
153
+ with st.spinner("Searching documents..."):
154
+ r = api_post("/rag/query", {"query": query}, auth=True)
155
+ if r and r.status_code == 200:
156
+ data = r.json()
157
+ answer = data.get("answer", "No answer found.")
158
+ citations = data.get("citations", [])
159
+ from_cache = data.get("from_cache", False)
160
+
161
+ st.markdown(answer)
162
+
163
+ if from_cache:
164
+ st.caption("⚑ From cache")
165
+
166
+ if citations:
167
+ with st.expander("πŸ“„ Sources"):
168
+ for i, cite in enumerate(citations, 1):
169
+ st.markdown(f"**[{i}]** Page {cite.get('page_number', 'N/A')} β€” {cite.get('content_preview', '')}")
170
+
171
+ st.session_state.chat_history.append({
172
+ "role": "assistant",
173
+ "content": answer,
174
+ "citations": citations,
175
+ })
176
+ elif r:
177
+ err = r.json().get("detail", "Query failed")
178
+ st.error(err)
179
+ st.session_state.chat_history.append({
180
+ "role": "assistant",
181
+ "content": f"Error: {err}",
182
+ "citations": [],
183
+ })
184
+
185
+ if st.session_state.chat_history:
186
+ if st.button("Clear Chat"):
187
+ st.session_state.chat_history = []
188
+ st.rerun()
189
+
190
+
191
+ # ─── Upload Page ──────────────────────────────────────────
192
+ elif page == "πŸ“€ Upload":
193
+ st.title("πŸ“€ Upload Documents")
194
+ st.markdown("Upload PDF, DOCX, or TXT files to query with AI.")
195
+ st.markdown("---")
196
+
197
+ uploaded_file = st.file_uploader(
198
+ "Choose a file",
199
+ type=["pdf", "docx", "txt"],
200
+ help="Max 50MB per file",
201
+ )
202
+
203
+ if uploaded_file:
204
+ st.info(f"Selected: **{uploaded_file.name}** ({round(uploaded_file.size / 1024, 1)} KB)")
205
+ if st.button("Upload & Process", type="primary", use_container_width=True):
206
+ with st.spinner("Uploading and processing document... this may take a minute."):
207
+ files = {"file": (uploaded_file.name, uploaded_file.getvalue(), uploaded_file.type)}
208
+ r = api_post("/documents/upload", files=files, auth=True)
209
+ if r and r.status_code == 201:
210
+ data = r.json()
211
+ st.success("Document uploaded successfully!")
212
+ st.json({
213
+ "filename": data["filename"],
214
+ "total_chunks": data["total_chunks"],
215
+ "status": data["status"],
216
+ })
217
+ elif r:
218
+ st.error(r.json().get("detail", "Upload failed"))
219
+
220
+
221
+ # ─── My Documents Page ────────────────────────────────────
222
+ elif page == "πŸ“ My Documents":
223
+ st.title("πŸ“ My Documents")
224
+ st.markdown("---")
225
+
226
+ r = api_get("/documents/list", auth=True)
227
+ if r and r.status_code == 200:
228
+ docs = r.json()
229
+ if not docs:
230
+ st.info("No documents uploaded yet. Go to Upload to add documents.")
231
+ else:
232
+ st.success(f"{len(docs)} document(s) found")
233
+ for doc in docs:
234
+ with st.expander(f"πŸ“„ {doc['filename']}"):
235
+ col1, col2, col3 = st.columns(3)
236
+ col1.metric("Chunks", doc["total_chunks"])
237
+ col2.metric("Type", doc["file_type"].upper())
238
+ col3.metric("Status", doc["status"].capitalize())
239
+ st.caption(f"Uploaded: {doc['created_at']}")
240
+ elif r:
241
+ st.error("Failed to load documents")