GodRad commited on
Commit
f044ce9
·
verified ·
1 Parent(s): 2053305

Delete webapp

Browse files
Files changed (3) hide show
  1. webapp/Dockerfile +0 -27
  2. webapp/app.py +0 -250
  3. webapp/requirements.txt +0 -4
webapp/Dockerfile DELETED
@@ -1,27 +0,0 @@
1
- FROM python:3.11-slim
2
-
3
- # Set environment variables
4
- ENV PYTHONUNBUFFERED=1 \
5
- PYTHONDONTWRITEBYTECODE=1 \
6
- PORT=7860
7
-
8
- # Create user and set permissions (HF expectation)
9
- RUN useradd -m -u 1000 user
10
- USER user
11
- ENV HOME=/home/user \
12
- PATH=/home/user/.local/bin:$PATH
13
-
14
- WORKDIR $HOME/app
15
-
16
- # Copy requirements and install
17
- COPY --chown=user requirements.txt .
18
- RUN pip install --no-cache-dir -r requirements.txt
19
-
20
- # Copy app code
21
- COPY --chown=user . .
22
-
23
- # Expose port
24
- EXPOSE 7860
25
-
26
- # Run entrypoint
27
- ENTRYPOINT ["streamlit", "run", "app.py", "--server.port=7860", "--server.address=0.0.0.0"]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
webapp/app.py DELETED
@@ -1,250 +0,0 @@
1
- import streamlit as st
2
- import requests
3
- import json
4
- import pandas as pd
5
- from datetime import datetime
6
-
7
- # Page Configuration
8
- st.set_page_config(
9
- page_title="RAG SaaS Playground",
10
- page_icon="🤖",
11
- layout="wide",
12
- initial_sidebar_state="expanded",
13
- )
14
-
15
- # Custom CSS for Premium Look
16
- st.markdown("""
17
- <style>
18
- .main {
19
- background-color: #0e1117;
20
- color: #ffffff;
21
- }
22
- .stButton>button {
23
- width: 100%;
24
- border-radius: 5px;
25
- height: 3em;
26
- background-color: #1f6feb;
27
- color: white;
28
- font-weight: bold;
29
- }
30
- .stTextInput>div>div>input {
31
- background-color: #161b22;
32
- color: white;
33
- border: 1px solid #30363d;
34
- }
35
- .sidebar .sidebar-content {
36
- background-color: #161b22;
37
- }
38
- .css-1d391kg {
39
- background-color: #161b22;
40
- }
41
- .stChatMessage {
42
- background-color: #161b22;
43
- border-radius: 10px;
44
- padding: 10px;
45
- margin-bottom: 10px;
46
- border: 1px solid #30363d;
47
- }
48
- </style>
49
- """, unsafe_allow_html=True)
50
-
51
- # Session State Initialization
52
- if "api_url" not in st.session_state:
53
- st.session_state.api_url = "http://localhost:7860/v1"
54
- if "tenant_id" not in st.session_state:
55
- st.session_state.tenant_id = "default"
56
- if "messages" not in st.session_state:
57
- st.session_state.messages = []
58
-
59
- # Helper Functions
60
- def api_request(method, endpoint, data=None, headers=None):
61
- url = f"{st.session_state.api_url}{endpoint}"
62
- if headers is None:
63
- headers = {}
64
- headers["X-Tenant-ID"] = st.session_state.tenant_id
65
-
66
- try:
67
- if method == "GET":
68
- response = requests.get(url, headers=headers)
69
- elif method == "POST":
70
- response = requests.post(url, json=data, headers=headers)
71
- response.raise_for_status()
72
- return response.json()
73
- except Exception as e:
74
- st.error(f"API Error: {str(e)}")
75
- return None
76
-
77
- # Sidebar - Configuration
78
- with st.sidebar:
79
- st.title("⚙️ RAG SaaS Config")
80
-
81
- with st.expander("🔗 API Connection", expanded=True):
82
- st.session_state.api_url = st.text_input("Backend API URL", st.session_state.api_url)
83
- if st.button("Check Connectivity"):
84
- res = requests.get(st.session_state.api_url.replace("/v1", "/health"))
85
- if res.status_code == 200:
86
- st.success("Backend Connected!")
87
- else:
88
- st.error("Failed to connect.")
89
-
90
- st.divider()
91
-
92
- st.title("🏢 Tenant Management")
93
-
94
- # List Tenants
95
- tenants = api_request("GET", "/tenants/")
96
- if tenants:
97
- tenant_names = [t["name"] for t in tenants]
98
- tenant_ids = [t["id"] for t in tenants]
99
-
100
- selected_index = 0
101
- if st.session_state.tenant_id in tenant_ids:
102
- selected_index = tenant_ids.index(st.session_state.tenant_id)
103
-
104
- selected_tenant_name = st.selectbox("Select Active Tenant", tenant_names, index=selected_index)
105
- st.session_state.tenant_id = tenant_ids[tenant_names.index(selected_tenant_name)]
106
-
107
- # Create Tenant
108
- with st.expander("➕ Create New Tenant"):
109
- new_tenant_id = st.text_input("Tenant ID (Slug)")
110
- new_tenant_name = st.text_input("Tenant Name")
111
- if st.button("Create"):
112
- if new_tenant_id and new_tenant_name:
113
- res = api_request("POST", "/tenants/", data={"id": new_tenant_id, "name": new_tenant_name})
114
- if res:
115
- st.success(f"Tenant '{new_tenant_name}' created!")
116
- st.rerun()
117
- else:
118
- st.warning("Please fill all fields.")
119
-
120
- st.divider()
121
- st.info(f"Active Tenant: **{st.session_state.tenant_id}**")
122
-
123
- # Main Content
124
- st.title("🚀 RAG SaaS Multi-tenant Playground")
125
-
126
- tab_chat, tab_ingest, tab_playground = st.tabs(["💬 Chat", "📥 Ingestion", "📖 How it Works"])
127
-
128
- # --- TAB: CHAT ---
129
- with tab_chat:
130
- st.subheader("Interactive Q&A")
131
-
132
- # Display chat messages from history on app rerun
133
- for message in st.session_state.messages:
134
- with st.chat_message(message["role"]):
135
- st.markdown(message["content"])
136
- if "citations" in message and message["citations"]:
137
- with st.expander("References"):
138
- for i, cit in enumerate(message["citations"]):
139
- st.markdown(f"**[{i}]** {cit.get('text_snippet')}")
140
-
141
- # React to user input
142
- if prompt := st.chat_input("Ask something about your documents..."):
143
- # Display user message in chat message container
144
- st.chat_message("user").markdown(prompt)
145
- st.session_state.messages.append({"role": "user", "content": prompt})
146
-
147
- with st.chat_message("assistant"):
148
- with st.spinner("Thinking..."):
149
- response = api_request("POST", "/ask/", data={"query": prompt})
150
- if response:
151
- st.markdown(response["answer"])
152
- if response["citations"]:
153
- with st.expander("References"):
154
- for i, cit in enumerate(response["citations"]):
155
- st.markdown(f"**[{i}]** {cit.get('text_snippet')}")
156
-
157
- st.session_state.messages.append({
158
- "role": "assistant",
159
- "content": response["answer"],
160
- "citations": response["citations"]
161
- })
162
-
163
- if st.button("Clear Chat History", key="clear_chat"):
164
- st.session_state.messages = []
165
- st.rerun()
166
-
167
- # --- TAB: INGESTION ---
168
- with tab_ingest:
169
- st.subheader("Knowledge Management")
170
-
171
- st.write("Upload documents to your tenant's vector space.")
172
-
173
- col1, col2 = st.columns([2, 1])
174
-
175
- with col1:
176
- uploaded_file = st.file_uploader("Choose a file (PDF, Text)", type=["pdf", "txt", "docx"])
177
- source_name = st.text_input("Source Name", value=uploaded_file.name if uploaded_file else "")
178
-
179
- if st.button("Process & Ingest"):
180
- if uploaded_file and source_name:
181
- with st.spinner("Uploading and processing..."):
182
- # 1. Create Source
183
- # In a real app, you'd upload the file to storage and get a URI
184
- # For playground, we'll mock the URI as the filename
185
- source_uid = f"src_{datetime.now().strftime('%Y%m%d%H%M%S')}"
186
- source_data = {
187
- "id": source_uid,
188
- "tenant_id": st.session_state.tenant_id,
189
- "type": "file",
190
- "name": source_name,
191
- "uri": f"uploads/{uploaded_file.name}",
192
- "metadata": {"filename": uploaded_file.name}
193
- }
194
-
195
- # Create source record
196
- created_source = api_request("POST", "/sources/", data=source_data)
197
-
198
- if created_source:
199
- # 2. Trigger Ingestion
200
- ingest_res = api_request("POST", f"/ingest/{source_uid}")
201
- if ingest_res:
202
- st.success(f"Successfully ingested '{source_name}'!")
203
- st.balloons()
204
- else:
205
- st.warning("Please upload a file and provide a name.")
206
-
207
- with col2:
208
- st.write("📋 **Existing Sources**")
209
- sources = api_request("GET", "/sources/")
210
- if sources:
211
- df = pd.DataFrame([
212
- {"Name": s["name"], "Type": s["type"], "Created": s["created_at"][:10]}
213
- for s in sources
214
- ])
215
- st.dataframe(df, use_container_width=True)
216
- else:
217
- st.info("No sources found for this tenant.")
218
-
219
- # --- TAB: PLAYGROUND ---
220
- with tab_playground:
221
- st.header("🛠️ How the Multi-tenant RAG SaaS Works")
222
-
223
- st.markdown("""
224
- ### 1. Multi-Tenancy (Data Isolation)
225
- Every request to the RAG backend includes an `X-Tenant-ID` header. This ensures that:
226
- - **Separate Vector Index**: Documents from Tenant A are never retrieved for Tenant B.
227
- - **Isolated Databases**: Metadata and audit trails are tagged per tenant.
228
-
229
- ### 2. The Ingestion Pipeline
230
- When you upload a document:
231
- 1. **Extraction**: The system parses the file (PDF, TXT) into raw text.
232
- 2. **Chunking**: Text is split into semantic chunks with overlapping context.
233
- 3. **Embedding**: Each chunk is converted into a vector (numerical representation) using models like CLIP or OpenAI.
234
- 4. **Vector Storage**: Vectors are stored in a database (ChromaDB/Qdrant) alongside metadata.
235
-
236
- ### 3. The Retrieval-Augmented Generation (RAG) Loop
237
- When you ask a question:
238
- 1. **Query Rewriting**: The AI rephrases your query to better match document content.
239
- 2. **Semantic Search**: The system finds the top 5 most relevant chunks for your specific tenant.
240
- 3. **Contextual Answer**: The AI generates a response using only the retrieved chunks as evidence.
241
- 4. **Citations**: Claims are linked back to the specific source chunks.
242
-
243
- ### 4. Play with it!
244
- Try creating two different tenants (e.g., *Marketing* and *Engineering*). Upload different documents to each, and see how the AI answers differently based on which tenant you select in the sidebar!
245
- """)
246
-
247
- st.image("https://miro.medium.com/v2/resize:fit:1400/1*C5e6DkCHiBfKTo296QY4kg.png", caption="RAG Architecture Diagram")
248
-
249
- st.markdown("---")
250
- st.markdown("<div style='text-align: center;'>Built for AI Institute Africa • 2026</div>", unsafe_allow_html=True)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
webapp/requirements.txt DELETED
@@ -1,4 +0,0 @@
1
- streamlit
2
- requests
3
- python-dotenv
4
- pandas