ktejeshnaidu commited on
Commit
a320ae4
Β·
verified Β·
1 Parent(s): 719bfa8

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +192 -131
app.py CHANGED
@@ -1,10 +1,15 @@
 
 
 
 
1
  import streamlit as st
2
  import requests
3
  import json
4
- import os
5
  import time
6
- from datetime import datetime
7
 
 
 
 
8
  st.set_page_config(
9
  page_title="DocuMind AI",
10
  page_icon="🧠",
@@ -12,170 +17,226 @@ st.set_page_config(
12
  initial_sidebar_state="expanded"
13
  )
14
 
 
 
 
 
 
 
15
  st.markdown("""
16
  <style>
17
- @import url('https://fonts.googleapis.com/css2?family=Syne:wght@400;500;600;700;800&family=DM+Sans:ital,opsz,wght@0,9..40,300;0,9..40,400;0,9..40,500;0,9..40,600;1,9..40,300&display=swap');
18
- :root {
19
- --bg-base:#0d0f14;--bg-card:#141720;--bg-sidebar:#111318;--bg-input:#1a1d27;
20
- --bg-user-msg:#1e2235;--bg-bot-msg:#181b26;--accent:#4ade80;--accent-dim:#22c55e22;
21
- --accent-muted:#4ade8044;--border:#ffffff0f;--border-hover:#ffffff1a;
22
- --text-primary:#f0f2f8;--text-secondary:#8892aa;--text-muted:#50586e;
23
- --radius-sm:8px;--radius-md:14px;--radius-lg:20px;
24
  }
25
- html,body,[data-testid="stAppViewContainer"]{font-family:'DM Sans',sans-serif;background:var(--bg-base)!important;color:var(--text-primary)!important}
26
- #MainMenu,footer,header,[data-testid="stToolbar"],[data-testid="stDecoration"],.stDeployButton{display:none!important}
27
- [data-testid="stSidebar"]{background:var(--bg-sidebar)!important;border-right:1px solid var(--border)!important;min-width:240px!important;max-width:240px!important}
28
- [data-testid="stSidebarContent"]{padding:0!important}
29
- .main .block-container{padding:0!important;max-width:100%!important}
30
- .stButton>button{background:var(--bg-input)!important;color:var(--text-primary)!important;border:1px solid var(--border-hover)!important;border-radius:var(--radius-sm)!important;padding:6px 14px!important;width:100%!important}
31
- .stButton>button:hover{background:var(--accent-dim)!important;color:var(--accent)!important}
32
- [data-testid="stChatInput"]{background:var(--bg-input)!important;border:1px solid var(--border-hover)!important;border-radius:var(--radius-lg)!important}
33
- [data-testid="stChatInput"] textarea{color:var(--text-primary)!important}
34
- </style>
35
- """, unsafe_allow_html=True)
36
 
37
- API_URL = "http://127.0.0.1:8000"
38
- API_TIMEOUT = 60
 
 
 
39
 
40
- if "messages" not in st.session_state:
41
- st.session_state.messages = []
42
- if "backend_ok" not in st.session_state:
43
- st.session_state.backend_ok = False
44
- if "backend_checked" not in st.session_state:
45
- st.session_state.backend_checked = False
46
- if "chat_sessions" not in st.session_state:
47
- st.session_state.chat_sessions = ["Q3 Financial Report","Product Roadmap 2...","Research Summary"]
48
- if "mode_summary" not in st.session_state:
49
- st.session_state.mode_summary = False
50
- if "mode_citations" not in st.session_state:
51
- st.session_state.mode_citations = True
52
- if "ingested_docs" not in st.session_state:
53
- st.session_state.ingested_docs = []
54
-
55
- def check_backend():
56
- try:
57
- r = requests.get(f"{API_URL}/docs", timeout=5)
58
- return r.status_code == 200
59
- except:
60
- return False
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
61
 
62
- def api_call(method, endpoint, **kwargs):
 
 
 
63
  try:
64
- kwargs.setdefault("timeout", API_TIMEOUT)
65
  url = f"{API_URL}{endpoint}"
66
- if method == "GET":
67
- return requests.get(url, **kwargs), None
68
- return requests.post(url, **kwargs), None
69
- except requests.exceptions.Timeout:
70
- return None, "⏱️ Request timed out"
71
- except requests.exceptions.ConnectionError:
72
- return None, "⚠️ Backend not responding"
73
- except Exception as e:
74
- return None, f"Error: {e}"
75
 
76
- def now_time():
77
- return datetime.now().strftime("%I:%M %p").lstrip("0")
 
 
78
 
79
- if not st.session_state.backend_checked:
80
- with st.spinner("Starting up..."):
81
- time.sleep(1)
82
- st.session_state.backend_ok = check_backend()
83
- st.session_state.backend_checked = True
84
 
85
- try:
86
- resp, _ = api_call("GET", "/sources")
87
- if resp and resp.status_code == 200:
88
- st.session_state.ingested_docs = resp.json().get("documents", [])
89
- except:
90
- pass
91
 
 
 
 
92
  with st.sidebar:
93
- st.markdown("""
94
- <div style="padding:22px 20px 16px;border-bottom:1px solid #ffffff0f;">
95
- <div style="display:flex;align-items:center;gap:10px;">
96
- <div style="width:32px;height:32px;background:linear-gradient(135deg,#4ade80,#22c55e);
97
- border-radius:8px;display:flex;align-items:center;justify-content:center;">🧠</div>
98
- <div>
99
- <div style="font-weight:700;">DocuMind AI</div>
100
- <div style="font-size:0.7rem;color:#50586e;">Digital Curator</div>
101
- </div>
102
- </div>
103
- </div>
104
- """, unsafe_allow_html=True)
105
-
106
- if st.button("οΌ‹ New Chat"):
107
  st.session_state.messages = []
108
- st.rerun()
109
 
110
- uploaded_file = st.file_uploader("Upload", type=["txt","pdf","docx"])
111
 
112
- if uploaded_file and st.button("Ingest Document"):
 
 
113
  files = {"file": (uploaded_file.name, uploaded_file.getvalue())}
114
- resp, err = api_call("POST", "/ingest", files=files)
 
115
  if err:
116
  st.error(err)
117
- elif resp and resp.status_code == 200:
118
- st.success("Ingested")
119
- st.session_state.ingested_docs.append(uploaded_file.name)
120
  else:
121
- st.error("Failed")
122
-
123
- for doc in st.session_state.ingested_docs[:5] or st.session_state.chat_sessions:
124
- st.markdown(f"- {doc}")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
125
 
126
- st.markdown("## CURRENT SESSION 🟒 ACTIVE")
 
 
 
127
 
 
 
 
128
  for msg in st.session_state.messages:
129
- with st.chat_message(msg["role"]):
130
- st.markdown(msg["content"])
131
-
132
- user_input = st.chat_input("Ask a question...")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
133
 
134
  if user_input:
135
- st.session_state.messages.append({"role":"user","content":user_input})
136
- with st.chat_message("user"):
137
- st.markdown(user_input)
 
 
138
 
139
- full_response = ""
140
- sources = []
 
 
 
 
 
141
 
142
- with st.chat_message("assistant"):
143
- placeholder = st.empty()
144
- resp, err = api_call("POST", "/query", json={"question": user_input}, stream=True)
145
 
146
  if err:
147
  full_response = err
148
- placeholder.markdown(err)
149
- elif resp:
150
  try:
151
- for line in resp.iter_lines(decode_unicode=True):
152
  if line:
153
- data = json.loads(line)
154
- if data.get("type") == "sources":
155
- sources = data.get("data", [])
156
- elif data.get("type") == "token":
157
  full_response += data.get("content", "")
158
- placeholder.markdown(full_response + "β–Œ")
159
- placeholder.markdown(full_response)
 
 
160
  except Exception as e:
161
- full_response = f"Error: {e}"
162
- placeholder.markdown(full_response)
163
 
 
164
  st.session_state.messages.append({
165
- "role":"assistant",
166
- "content":full_response,
167
- "sources":sources
168
  })
169
 
170
- c1,c2,c3,_=st.columns([1,1,1,5])
171
- with c1:
172
- if st.button("Summary Mode"):
173
- st.session_state.mode_summary=not st.session_state.mode_summary
174
- st.rerun()
175
- with c2:
176
- if st.button("Source Citations"):
177
- st.session_state.mode_citations=not st.session_state.mode_citations
178
- st.rerun()
179
- with c3:
180
- if st.button("Translate"):
181
- st.toast("Coming soon")
 
1
+ """
2
+ DocuMind - Modern UI + FastAPI Backend (Single File)
3
+ """
4
+
5
  import streamlit as st
6
  import requests
7
  import json
 
8
  import time
 
9
 
10
+ # =========================================================
11
+ # CONFIG
12
+ # =========================================================
13
  st.set_page_config(
14
  page_title="DocuMind AI",
15
  page_icon="🧠",
 
17
  initial_sidebar_state="expanded"
18
  )
19
 
20
+ API_URL = "http://127.0.0.1:8000"
21
+ API_TIMEOUT = 30
22
+
23
+ # =========================================================
24
+ # CUSTOM CSS (UI MAGIC)
25
+ # =========================================================
26
  st.markdown("""
27
  <style>
28
+
29
+ /* Background */
30
+ body {
31
+ background-color: #f7f9fb;
 
 
 
32
  }
 
 
 
 
 
 
 
 
 
 
 
33
 
34
+ /* Sidebar */
35
+ section[data-testid="stSidebar"] {
36
+ background-color: #ffffff;
37
+ border-right: 1px solid #e6e8eb;
38
+ }
39
 
40
+ /* Buttons */
41
+ .stButton>button {
42
+ border-radius: 12px;
43
+ background-color: #0f4c5c;
44
+ color: white;
45
+ border: none;
46
+ padding: 10px;
47
+ width: 100%;
48
+ }
49
+
50
+ /* Header */
51
+ .main-title {
52
+ text-align: center;
53
+ font-size: 34px;
54
+ font-weight: 600;
55
+ margin-top: 20px;
56
+ }
57
+
58
+ .sub-text {
59
+ text-align: center;
60
+ color: gray;
61
+ margin-bottom: 30px;
62
+ }
63
+
64
+ /* Chat bubbles */
65
+ .chat-user {
66
+ background-color: #0f4c5c;
67
+ color: white;
68
+ padding: 12px 16px;
69
+ border-radius: 18px;
70
+ margin: 10px 0;
71
+ max-width: 70%;
72
+ margin-left: auto;
73
+ }
74
+
75
+ .chat-assistant {
76
+ background-color: #f1f3f5;
77
+ color: black;
78
+ padding: 12px 16px;
79
+ border-radius: 18px;
80
+ margin: 10px 0;
81
+ max-width: 70%;
82
+ }
83
+
84
+ /* Input box */
85
+ [data-testid="stChatInput"] {
86
+ position: fixed;
87
+ bottom: 20px;
88
+ left: 300px;
89
+ right: 20px;
90
+ }
91
+
92
+ </style>
93
+ """, unsafe_allow_html=True)
94
 
95
+ # =========================================================
96
+ # API FUNCTIONS
97
+ # =========================================================
98
+ def safe_api_call(method, endpoint, **kwargs):
99
  try:
 
100
  url = f"{API_URL}{endpoint}"
101
+ kwargs.setdefault("timeout", API_TIMEOUT)
 
 
 
 
 
 
 
 
102
 
103
+ if method == "GET":
104
+ res = requests.get(url, **kwargs)
105
+ else:
106
+ res = requests.post(url, **kwargs)
107
 
108
+ return res, None
109
+ except Exception as e:
110
+ return None, str(e)
 
 
111
 
112
+ # =========================================================
113
+ # SESSION STATE
114
+ # =========================================================
115
+ if "messages" not in st.session_state:
116
+ st.session_state.messages = []
 
117
 
118
+ # =========================================================
119
+ # SIDEBAR
120
+ # =========================================================
121
  with st.sidebar:
122
+ st.markdown("## 🧠 DocuMind AI")
123
+ st.caption("Digital Curator")
124
+
125
+ if st.button("βž• New Chat"):
 
 
 
 
 
 
 
 
 
 
126
  st.session_state.messages = []
 
127
 
128
+ st.markdown("### πŸ“€ Upload")
129
 
130
+ uploaded_file = st.file_uploader("", type=["pdf", "txt", "docx"])
131
+
132
+ if uploaded_file and st.button("Upload Document"):
133
  files = {"file": (uploaded_file.name, uploaded_file.getvalue())}
134
+ res, err = safe_api_call("POST", "/ingest", files=files)
135
+
136
  if err:
137
  st.error(err)
138
+ elif res and res.status_code == 200:
139
+ st.success("Uploaded successfully!")
 
140
  else:
141
+ st.error("Upload failed")
142
+
143
+ st.markdown("---")
144
+ st.markdown("### πŸ“„ Recents")
145
+
146
+ res, err = safe_api_call("GET", "/sources")
147
+ if res and res.status_code == 200:
148
+ docs = res.json().get("documents", [])
149
+ for d in docs:
150
+ st.markdown(f"β€’ {d}")
151
+
152
+ # =========================================================
153
+ # MAIN HEADER
154
+ # =========================================================
155
+ st.markdown(
156
+ '<div class="main-title">How can I assist your research today?</div>',
157
+ unsafe_allow_html=True
158
+ )
159
 
160
+ st.markdown(
161
+ '<div class="sub-text">I can analyze documents, summarize findings, or answer questions.</div>',
162
+ unsafe_allow_html=True
163
+ )
164
 
165
+ # =========================================================
166
+ # CHAT DISPLAY
167
+ # =========================================================
168
  for msg in st.session_state.messages:
169
+ if msg["role"] == "user":
170
+ st.markdown(
171
+ f'<div class="chat-user">{msg["content"]}</div>',
172
+ unsafe_allow_html=True
173
+ )
174
+ else:
175
+ st.markdown(
176
+ f'<div class="chat-assistant">{msg["content"]}</div>',
177
+ unsafe_allow_html=True
178
+ )
179
+
180
+ if msg.get("sources"):
181
+ with st.expander("πŸ“š Sources"):
182
+ for src in msg["sources"]:
183
+ st.caption(src.get("source", "Unknown"))
184
+
185
+ # =========================================================
186
+ # CHAT INPUT
187
+ # =========================================================
188
+ user_input = st.chat_input("Ask a question about your documents...")
189
 
190
  if user_input:
191
+ # Show user message instantly
192
+ st.session_state.messages.append({
193
+ "role": "user",
194
+ "content": user_input
195
+ })
196
 
197
+ with st.spinner("Thinking..."):
198
+ res, err = safe_api_call(
199
+ "POST",
200
+ "/query",
201
+ json={"question": user_input},
202
+ stream=True
203
+ )
204
 
205
+ full_response = ""
206
+ sources = []
 
207
 
208
  if err:
209
  full_response = err
210
+
211
+ elif res:
212
  try:
213
+ for line in res.iter_lines():
214
  if line:
215
+ data = json.loads(line.decode("utf-8"))
216
+
217
+ if data.get("type") == "token":
 
218
  full_response += data.get("content", "")
219
+
220
+ elif data.get("type") == "sources":
221
+ sources = data.get("data", [])
222
+
223
  except Exception as e:
224
+ full_response = str(e)
 
225
 
226
+ # Save assistant response
227
  st.session_state.messages.append({
228
+ "role": "assistant",
229
+ "content": full_response,
230
+ "sources": sources
231
  })
232
 
233
+ st.rerun()
234
+
235
+ # =========================================================
236
+ # FOOTER
237
+ # =========================================================
238
+ st.markdown("---")
239
+ st.markdown(
240
+ "<div style='text-align:center; font-size:12px; color:gray;'>DocuMind AI β€’ Enterprise RAG</div>",
241
+ unsafe_allow_html=True
242
+ )