Sahana31 commited on
Commit
b996c35
Β·
verified Β·
1 Parent(s): f2b7ec6

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +249 -90
app.py CHANGED
@@ -1,114 +1,273 @@
 
1
  import gradio as gr
2
  import requests
 
3
  import time
 
4
 
5
- API_BASE = "https://sahana31-rag-backend.hf.space"
6
-
7
 
8
- class BackendClient:
9
- def __init__(self, base):
10
- self.base = base.rstrip("/")
11
 
12
- def health(self):
13
- r = requests.get(f"{self.base}/health", timeout=10)
14
- r.raise_for_status()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
15
 
16
- def upload(self, file_obj):
17
- r = requests.post(
18
- f"{self.base}/files/upload",
19
- files={"file": (file_obj.name, file_obj, "application/pdf")},
20
- timeout=120
21
- )
22
- r.raise_for_status()
23
- return r.json()
24
-
25
- def status(self, file_id):
26
- r = requests.get(f"{self.base}/files/{file_id}", timeout=20)
27
- r.raise_for_status()
28
- return r.json()
29
-
30
- def chat(self, file_ids, messages):
31
- r = requests.post(
32
- f"{self.base}/chat-with-document",
33
- json={"file_ids": file_ids, "messages": messages},
34
- timeout=180
35
- )
36
- r.raise_for_status()
37
- return r.json()
38
 
 
 
 
 
 
39
 
40
- client = BackendClient(API_BASE)
 
 
 
 
 
 
 
 
 
 
 
 
 
41
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
42
 
43
- def upload_files(files, store):
 
 
44
  if not files:
45
- return "No files", [], store
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
46
 
47
- client.health()
 
 
 
 
 
 
 
 
 
 
 
 
 
48
 
49
- status = []
50
- choices = []
 
 
 
51
 
52
- for f in files:
53
- res = client.upload(f)
54
- fid = res["file_id"]
55
- name = res["filename"]
 
 
 
 
 
 
56
 
57
- for _ in range(60):
58
- s = client.status(fid)
59
- if s["status"] == "completed":
60
- store[fid] = name
61
- choices.append((name, fid))
62
- status.append(f"βœ… {name}")
63
- break
64
- time.sleep(2)
65
-
66
- return "\n".join(status), choices, store
67
-
68
-
69
- def chat_fn(msg, history, selected, chat_state):
70
- if not selected:
71
- history.append((msg, "Select a document first"))
72
  return history, ""
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
73
 
74
- chat_state.append({"role": "user", "content": msg})
75
- history.append((msg, "Thinking..."))
76
-
77
- res = client.chat(selected, chat_state)
78
- answer = res["answer"]
79
-
80
- chat_state.append({"role": "assistant", "content": answer})
81
- history[-1] = (msg, answer)
82
-
83
- return history, ""
84
-
85
-
86
- with gr.Blocks() as demo:
87
- gr.Markdown("# πŸ“„ RAG Document Chat")
88
-
89
- files_state = gr.State({})
90
- chat_state = gr.State([])
91
 
 
 
 
 
 
92
  with gr.Row():
93
- files = gr.File(file_types=[".pdf"], file_count="multiple")
94
- upload_btn = gr.Button("Upload")
95
- status = gr.Markdown()
96
- selector = gr.CheckboxGroup(label="Documents")
97
-
98
- bot = gr.Chatbot()
99
- msg = gr.Textbox()
100
- send = gr.Button("Send")
101
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
102
  upload_btn.click(
103
- upload_files,
104
- inputs=[files, files_state],
105
- outputs=[status, selector, files_state],
106
  )
107
-
108
- send.click(
109
- chat_fn,
110
- inputs=[msg, bot, selector, chat_state],
111
- outputs=[bot, msg],
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
112
  )
113
 
114
- demo.queue()
 
 
 
1
+
2
  import gradio as gr
3
  import requests
4
+ from typing import List, Dict
5
  import time
6
+ import os
7
 
8
+ # Disable Gradio analytics for HF Spaces
9
+ os.environ["GRADIO_ANALYTICS_ENABLED"] = "False"
10
 
11
+ # === CONFIG ===
12
+ API_BASE = "https://sahana31-rag-backend.hf.space"
 
13
 
14
+ # === CLIENT ===
15
+ class DwaniClient:
16
+ def __init__(self, base_url=API_BASE):
17
+ self.base_url = base_url.rstrip('/')
18
+
19
+ def upload_file(self, file) -> dict:
20
+ """Upload a file (HF temp file compatible)"""
21
+ files = {
22
+ "file": (file.name.split("/")[-1], open(file.name, "rb"), "application/pdf")
23
+ }
24
+ resp = requests.post(f"{self.base_url}/files/upload", files=files, timeout=300)
25
+ resp.raise_for_status()
26
+ return resp.json()
27
+
28
+ def get_file_status(self, file_id: str) -> dict:
29
+ resp = requests.get(f"{self.base_url}/files/{file_id}", timeout=60)
30
+ resp.raise_for_status()
31
+ return resp.json()
32
+
33
+ def list_files(self) -> List[dict]:
34
+ resp = requests.get(f"{self.base_url}/files/", timeout=60)
35
+ resp.raise_for_status()
36
+ return resp.json()
37
+
38
+ def chat(self, file_ids: List[str], messages: List[Dict]) -> dict:
39
+ payload = {"file_ids": file_ids, "messages": messages}
40
+ resp = requests.post(f"{self.base_url}/chat-with-document", json=payload, timeout=300)
41
+ resp.raise_for_status()
42
+ return resp.json()
43
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
44
 
45
+ # === GLOBALS ===
46
+ client = DwaniClient()
47
+ uploaded_files = {}
48
+ chat_history: List[Dict] = []
49
+ selected_files = []
50
 
51
+ # === HELPERS ===
52
+ def poll_file_status(file_id: str, max_wait=60):
53
+ """Wait for file processing"""
54
+ for _ in range(max_wait):
55
+ try:
56
+ status = client.get_file_status(file_id)
57
+ if status['status'] == 'completed':
58
+ return status, True
59
+ if status['status'] == 'failed':
60
+ return status, False
61
+ time.sleep(2)
62
+ except:
63
+ time.sleep(2)
64
+ return {'status': 'timeout'}, False
65
 
66
+ def create_file_list():
67
+ """Display all files with status"""
68
+ if not uploaded_files:
69
+ return "No files uploaded"
70
+
71
+ lines = ["**πŸ“ Your Files:**"]
72
+ for info in uploaded_files.values():
73
+ emoji = {
74
+ 'completed': 'βœ…',
75
+ 'processing': 'πŸ”„',
76
+ 'pending': '⏳',
77
+ 'failed': '❌'
78
+ }.get(info['status'], '❓')
79
+ lines.append(f"{emoji} {info['filename']} ({info['status']})")
80
+ return "\n".join(lines)
81
 
82
+ # === UI FUNCTIONS ===
83
+ def upload_multiple(files):
84
+ """Handle multiple PDF uploads"""
85
  if not files:
86
+ return "No files selected", gr.update(choices=[]), "No files uploaded"
87
+
88
+ global uploaded_files
89
+ status_msgs = []
90
+
91
+ for file in files:
92
+ try:
93
+ # Upload each file
94
+ result = client.upload_file(file)
95
+ file_id = result['file_id']
96
+ filename = result['filename']
97
+
98
+ uploaded_files[file_id] = {
99
+ 'filename': filename,
100
+ 'status': 'pending',
101
+ 'file_id': file_id
102
+ }
103
+
104
+ # Poll for completion
105
+ status, success = poll_file_status(file_id)
106
+
107
+ if success:
108
+ uploaded_files[file_id]['status'] = 'completed'
109
+ status_msgs.append(f"βœ… {filename} - READY")
110
+ else:
111
+ uploaded_files[file_id]['status'] = 'failed'
112
+ status_msgs.append(f"❌ {filename} - FAILED")
113
+
114
+ except Exception as e:
115
+ status_msgs.append(f"❌ {file.name} - ERROR: {str(e)}")
116
+
117
+ # Update choices for only completed files
118
+ choices = [(info['filename'], info['file_id']) for info in uploaded_files.values()
119
+ if info['status'] == 'completed']
120
+
121
+ return "\n".join(status_msgs), gr.update(choices=choices), create_file_list()
122
 
123
+ def refresh_files():
124
+ """Refresh file list from backend"""
125
+ try:
126
+ files = client.list_files()
127
+ global uploaded_files
128
+ uploaded_files.clear()
129
+
130
+ for f in files:
131
+ uploaded_files[f['file_id']] = f
132
+
133
+ choices = [(f['filename'], f['file_id']) for f in files if f['status'] == 'completed']
134
+ return create_file_list(), gr.update(choices=choices)
135
+ except:
136
+ return "Refresh failed", gr.update()
137
 
138
+ def update_selected_files(files):
139
+ """Update selected files"""
140
+ global selected_files
141
+ selected_files = files or []
142
+ return len(selected_files)
143
 
144
+ def format_chat_response(result):
145
+ """Format response with sources"""
146
+ answer = result['answer']
147
+ if result.get('sources'):
148
+ sources = "\n\n**πŸ“š Sources:**\n"
149
+ for i, src in enumerate(result['sources'][:5], 1):
150
+ sources += f"{i}. **{src['filename']}** (Page {src['page']})\n"
151
+ sources += f" > {src['excerpt'][:120]}...\n\n"
152
+ return answer + sources
153
+ return answer
154
 
155
+ def send_message(message, history):
156
+ """Send chat message"""
157
+ global chat_history, selected_files
158
+
159
+ if not message.strip():
 
 
 
 
 
 
 
 
 
 
160
  return history, ""
161
+
162
+ if not selected_files:
163
+ return history, "⚠️ Please select files first!"
164
+
165
+ user_message = {"role": "user", "content": message}
166
+ assistant_message = {"role": "assistant", "content": "Thinking..."}
167
+ new_history = history + [user_message, assistant_message]
168
+
169
+ try:
170
+ api_messages = chat_history + [user_message]
171
+ result = client.chat(selected_files, api_messages)
172
+
173
+ chat_history.append(user_message)
174
+ chat_history.append({"role": "assistant", "content": result['answer']})
175
+
176
+ full_response = format_chat_response(result)
177
+ final_history = history + [user_message, {"role": "assistant", "content": full_response}]
178
+ return final_history, ""
179
+
180
+ except Exception as e:
181
+ error_response = {"role": "assistant", "content": f"❌ Error: {str(e)}"}
182
+ return new_history[:-1] + [error_response], f"Error: {str(e)}"
183
 
184
+ def clear_chat():
185
+ global chat_history
186
+ chat_history = []
187
+ return []
 
 
 
 
 
 
 
 
 
 
 
 
 
188
 
189
+ # === GRADIO UI ===
190
+ with gr.Blocks(title="Dwani.ai", theme=gr.themes.Soft()) as demo:
191
+ gr.Markdown("# πŸ“š Dwani.ai - Document Chat")
192
+ gr.Markdown("**Upload multiple PDFs β†’ Chat with page-accurate citations**")
193
+
194
  with gr.Row():
195
+ with gr.Column(scale=1):
196
+ gr.Markdown("## πŸ“€ Upload Multiple PDFs")
197
+ file_input = gr.File(
198
+ label="Select PDFs (Ctrl+Click for multiple)",
199
+ file_types=[".pdf"],
200
+ file_count="multiple"
201
+ )
202
+ upload_btn = gr.Button("πŸš€ Upload & Process All", variant="primary")
203
+ status_output = gr.Markdown("Ready to upload...")
204
+ refresh_btn = gr.Button("πŸ”„ Refresh List")
205
+ files_display = gr.Markdown("No files uploaded")
206
+
207
+ with gr.Column(scale=2):
208
+ gr.Markdown("## πŸ“‹ File Manager")
209
+ file_checkboxes = gr.CheckboxGroup(
210
+ label="Select documents to chat with:",
211
+ choices=[],
212
+ value=[],
213
+ info="Only completed files appear here"
214
+ )
215
+ file_count = gr.Number(label="Files selected", value=0, interactive=False)
216
+
217
+ with gr.Row():
218
+ gr.Markdown("## πŸ’¬ Chat with Documents")
219
+ chatbot = gr.Chatbot(
220
+ label="Ask questions about your documents",
221
+ height=500,
222
+ avatar_images=("user.png", "assistant.png")
223
+ )
224
+
225
+ with gr.Row():
226
+ msg_input = gr.Textbox(
227
+ label="Your question",
228
+ placeholder="e.g., What are the payment terms? When does the contract expire?",
229
+ scale=4
230
+ )
231
+ send_btn = gr.Button("Send", variant="primary", scale=1)
232
+
233
+ with gr.Row():
234
+ clear_btn = gr.Button("πŸ—‘οΈ New Chat", variant="secondary")
235
+
236
+ # === EVENTS ===
237
  upload_btn.click(
238
+ upload_multiple,
239
+ inputs=file_input,
240
+ outputs=[status_output, file_checkboxes, files_display]
241
  )
242
+
243
+ refresh_btn.click(
244
+ refresh_files,
245
+ outputs=[files_display, file_checkboxes]
246
+ )
247
+
248
+ file_checkboxes.change(
249
+ update_selected_files,
250
+ inputs=file_checkboxes,
251
+ outputs=file_count
252
+ )
253
+
254
+ send_btn.click(
255
+ send_message,
256
+ inputs=[msg_input, chatbot],
257
+ outputs=[chatbot, msg_input]
258
+ )
259
+
260
+ msg_input.submit(
261
+ send_message,
262
+ inputs=[msg_input, chatbot],
263
+ outputs=[chatbot, msg_input]
264
+ )
265
+
266
+ clear_btn.click(
267
+ clear_chat,
268
+ outputs=chatbot
269
  )
270
 
271
+ # === LAUNCH FOR HF SPACES ===
272
+ if __name__ == "__main__":
273
+ demo.launch(show_error=True)