Update app.py
Browse files
app.py
CHANGED
|
@@ -1012,6 +1012,10 @@ class HuggingFaceChatBot:
|
|
| 1012 |
# FIXED MAIN APPLICATION WITH PROPER CHAT INPUT PLACEMENT
|
| 1013 |
# ===============================================================================
|
| 1014 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1015 |
def create_huggingface_app():
|
| 1016 |
"""Main Streamlit application optimized for Hugging Face Spaces"""
|
| 1017 |
|
|
@@ -1121,7 +1125,7 @@ def create_huggingface_app():
|
|
| 1121 |
])
|
| 1122 |
|
| 1123 |
# -------------------------------------------------------------------------
|
| 1124 |
-
# TAB 1: UPLOAD & PROCESS
|
| 1125 |
# -------------------------------------------------------------------------
|
| 1126 |
|
| 1127 |
with tab1:
|
|
@@ -1154,14 +1158,15 @@ def create_huggingface_app():
|
|
| 1154 |
</div>
|
| 1155 |
""", unsafe_allow_html=True)
|
| 1156 |
|
| 1157 |
-
# File upload interface
|
| 1158 |
st.markdown("### π Upload Your Invoices")
|
| 1159 |
|
| 1160 |
uploaded_files = st.file_uploader(
|
| 1161 |
"Choose invoice files (PDF, TXT supported)",
|
| 1162 |
type=['pdf', 'txt'],
|
| 1163 |
accept_multiple_files=True,
|
| 1164 |
-
help=f"Maximum file size: {HF_CONFIG['max_file_size_mb']}MB per file"
|
|
|
|
| 1165 |
)
|
| 1166 |
|
| 1167 |
if uploaded_files:
|
|
@@ -1172,7 +1177,7 @@ def create_huggingface_app():
|
|
| 1172 |
|
| 1173 |
st.info(f"π {len(uploaded_files)} files selected")
|
| 1174 |
|
| 1175 |
-
if st.button("π Process Files", type="primary", use_container_width=True):
|
| 1176 |
progress_bar = st.progress(0)
|
| 1177 |
status_container = st.container()
|
| 1178 |
results_container = st.container()
|
|
@@ -1192,7 +1197,7 @@ def create_huggingface_app():
|
|
| 1192 |
with results_container:
|
| 1193 |
if result.invoice_number:
|
| 1194 |
successful += 1
|
| 1195 |
-
with st.expander(f"β
{uploaded_file.name}", expanded=False):
|
| 1196 |
col1, col2 = st.columns(2)
|
| 1197 |
with col1:
|
| 1198 |
st.write(f"**Invoice #:** {result.invoice_number}")
|
|
@@ -1214,7 +1219,7 @@ def create_huggingface_app():
|
|
| 1214 |
st.balloons()
|
| 1215 |
|
| 1216 |
# -------------------------------------------------------------------------
|
| 1217 |
-
# TAB 2: AI CHAT - FIXED LAYOUT
|
| 1218 |
# -------------------------------------------------------------------------
|
| 1219 |
|
| 1220 |
with tab2:
|
|
@@ -1225,7 +1230,7 @@ def create_huggingface_app():
|
|
| 1225 |
st.markdown("### π¬ Chat History")
|
| 1226 |
chat_container = st.container()
|
| 1227 |
with chat_container:
|
| 1228 |
-
for message in st.session_state.chat_history:
|
| 1229 |
with st.chat_message(message["role"]):
|
| 1230 |
st.markdown(message["content"])
|
| 1231 |
|
|
@@ -1244,7 +1249,7 @@ def create_huggingface_app():
|
|
| 1244 |
"Find invoices with high amounts"
|
| 1245 |
]
|
| 1246 |
for i, query in enumerate(queries):
|
| 1247 |
-
if st.button(query, key=f"
|
| 1248 |
st.session_state.chat_history.append({"role": "user", "content": query, "timestamp": datetime.now()})
|
| 1249 |
response = st.session_state.hf_chatbot.query_database(query)
|
| 1250 |
st.session_state.chat_history.append({"role": "assistant", "content": response, "timestamp": datetime.now()})
|
|
@@ -1260,7 +1265,7 @@ def create_huggingface_app():
|
|
| 1260 |
"Find maintenance contracts"
|
| 1261 |
]
|
| 1262 |
for i, query in enumerate(semantic_queries):
|
| 1263 |
-
if st.button(query, key=f"
|
| 1264 |
st.session_state.chat_history.append({"role": "user", "content": query, "timestamp": datetime.now()})
|
| 1265 |
response = st.session_state.hf_chatbot.query_database(query)
|
| 1266 |
st.session_state.chat_history.append({"role": "assistant", "content": response, "timestamp": datetime.now()})
|
|
@@ -1277,11 +1282,11 @@ def create_huggingface_app():
|
|
| 1277 |
user_input = st.text_input(
|
| 1278 |
"Type your question here:",
|
| 1279 |
placeholder="e.g., 'show me total spending' or 'find technology purchases'",
|
| 1280 |
-
key="
|
| 1281 |
)
|
| 1282 |
|
| 1283 |
with col2:
|
| 1284 |
-
ask_button = st.button("π Ask", type="primary", use_container_width=True)
|
| 1285 |
|
| 1286 |
# Process the input
|
| 1287 |
if ask_button and user_input:
|
|
@@ -1307,12 +1312,12 @@ def create_huggingface_app():
|
|
| 1307 |
|
| 1308 |
# Clear chat button
|
| 1309 |
if st.session_state.chat_history:
|
| 1310 |
-
if st.button("ποΈ Clear Chat History", use_container_width=True):
|
| 1311 |
st.session_state.chat_history = []
|
| 1312 |
st.rerun()
|
| 1313 |
|
| 1314 |
# -------------------------------------------------------------------------
|
| 1315 |
-
# TAB 3: ANALYTICS
|
| 1316 |
# -------------------------------------------------------------------------
|
| 1317 |
|
| 1318 |
with tab3:
|
|
@@ -1360,7 +1365,7 @@ def create_huggingface_app():
|
|
| 1360 |
title="Invoice Amount Distribution",
|
| 1361 |
labels={'amount': 'Amount (βΉ)', 'count': 'Number of Invoices'}
|
| 1362 |
)
|
| 1363 |
-
st.plotly_chart(fig_hist, use_container_width=True)
|
| 1364 |
|
| 1365 |
# Top suppliers
|
| 1366 |
if df['supplier_name'].notna().any():
|
|
@@ -1373,7 +1378,7 @@ def create_huggingface_app():
|
|
| 1373 |
title="Top 10 Suppliers by Total Amount",
|
| 1374 |
labels={'x': 'Total Amount (βΉ)', 'y': 'Supplier'}
|
| 1375 |
)
|
| 1376 |
-
st.plotly_chart(fig_suppliers, use_container_width=True)
|
| 1377 |
|
| 1378 |
# Confidence analysis
|
| 1379 |
fig_confidence = px.histogram(
|
|
@@ -1382,13 +1387,13 @@ def create_huggingface_app():
|
|
| 1382 |
title="Extraction Confidence Distribution",
|
| 1383 |
labels={'confidence': 'Confidence Score', 'count': 'Number of Invoices'}
|
| 1384 |
)
|
| 1385 |
-
st.plotly_chart(fig_confidence, use_container_width=True)
|
| 1386 |
|
| 1387 |
except Exception as e:
|
| 1388 |
st.error(f"Analytics error: {e}")
|
| 1389 |
|
| 1390 |
# -------------------------------------------------------------------------
|
| 1391 |
-
# TAB 4: DATA EXPLORER
|
| 1392 |
# -------------------------------------------------------------------------
|
| 1393 |
|
| 1394 |
with tab4:
|
|
@@ -1419,19 +1424,19 @@ def create_huggingface_app():
|
|
| 1419 |
|
| 1420 |
df = pd.DataFrame(df_data)
|
| 1421 |
|
| 1422 |
-
# Filters
|
| 1423 |
col1, col2, col3 = st.columns(3)
|
| 1424 |
|
| 1425 |
with col1:
|
| 1426 |
suppliers = ['All'] + sorted(df['Supplier'].dropna().unique().tolist())
|
| 1427 |
-
selected_supplier = st.selectbox("Filter by Supplier", suppliers)
|
| 1428 |
|
| 1429 |
with col2:
|
| 1430 |
methods = ['All'] + sorted(df['Method'].dropna().unique().tolist())
|
| 1431 |
-
selected_method = st.selectbox("Filter by Method", methods)
|
| 1432 |
|
| 1433 |
with col3:
|
| 1434 |
-
min_amount = st.number_input("Min Amount", min_value=0.0, value=0.0)
|
| 1435 |
|
| 1436 |
# Apply filters
|
| 1437 |
filtered_df = df.copy()
|
|
@@ -1449,24 +1454,26 @@ def create_huggingface_app():
|
|
| 1449 |
column_config={
|
| 1450 |
"Amount": st.column_config.NumberColumn("Amount", format="βΉ%.2f"),
|
| 1451 |
"Confidence": st.column_config.ProgressColumn("Confidence", min_value=0, max_value=1)
|
| 1452 |
-
}
|
|
|
|
| 1453 |
)
|
| 1454 |
|
| 1455 |
-
# Export options
|
| 1456 |
col1, col2 = st.columns(2)
|
| 1457 |
|
| 1458 |
with col1:
|
| 1459 |
-
if st.button("π₯ Export CSV", use_container_width=True):
|
| 1460 |
csv_data = filtered_df.to_csv(index=False)
|
| 1461 |
st.download_button(
|
| 1462 |
"Download CSV",
|
| 1463 |
csv_data,
|
| 1464 |
f"invoices_{datetime.now().strftime('%Y%m%d_%H%M')}.csv",
|
| 1465 |
-
"text/csv"
|
|
|
|
| 1466 |
)
|
| 1467 |
|
| 1468 |
with col2:
|
| 1469 |
-
if st.button("π Export JSON", use_container_width=True):
|
| 1470 |
filtered_invoices = [inv for inv in invoices
|
| 1471 |
if inv.get('invoice_number') in filtered_df['Invoice Number'].values]
|
| 1472 |
|
|
@@ -1480,14 +1487,15 @@ def create_huggingface_app():
|
|
| 1480 |
"Download JSON",
|
| 1481 |
json.dumps(export_data, indent=2),
|
| 1482 |
f"invoices_{datetime.now().strftime('%Y%m%d_%H%M')}.json",
|
| 1483 |
-
"application/json"
|
|
|
|
| 1484 |
)
|
| 1485 |
|
| 1486 |
except Exception as e:
|
| 1487 |
st.error(f"Data explorer error: {e}")
|
| 1488 |
|
| 1489 |
# -------------------------------------------------------------------------
|
| 1490 |
-
# GLOBAL CHAT INPUT - OUTSIDE TABS
|
| 1491 |
# -------------------------------------------------------------------------
|
| 1492 |
|
| 1493 |
# Add some spacing
|
|
@@ -1496,8 +1504,8 @@ def create_huggingface_app():
|
|
| 1496 |
# Global chat input that works from any tab
|
| 1497 |
st.markdown("### π¬ Quick Chat (Available from any tab)")
|
| 1498 |
|
| 1499 |
-
# Use chat_input outside of tabs - this will work
|
| 1500 |
-
global_user_query = st.chat_input("Ask about your invoices from anywhere...")
|
| 1501 |
|
| 1502 |
if global_user_query:
|
| 1503 |
# Add user message
|
|
@@ -1537,6 +1545,27 @@ def create_huggingface_app():
|
|
| 1537 |
# MAIN APPLICATION ENTRY POINT
|
| 1538 |
# ===============================================================================
|
| 1539 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1540 |
def main():
|
| 1541 |
"""Main entry point for Hugging Face Spaces"""
|
| 1542 |
try:
|
|
|
|
| 1012 |
# FIXED MAIN APPLICATION WITH PROPER CHAT INPUT PLACEMENT
|
| 1013 |
# ===============================================================================
|
| 1014 |
|
| 1015 |
+
# ===============================================================================
|
| 1016 |
+
# FIXED APPLICATION WITH UNIQUE WIDGET KEYS
|
| 1017 |
+
# ===============================================================================
|
| 1018 |
+
|
| 1019 |
def create_huggingface_app():
|
| 1020 |
"""Main Streamlit application optimized for Hugging Face Spaces"""
|
| 1021 |
|
|
|
|
| 1125 |
])
|
| 1126 |
|
| 1127 |
# -------------------------------------------------------------------------
|
| 1128 |
+
# TAB 1: UPLOAD & PROCESS - FIXED WITH UNIQUE KEY
|
| 1129 |
# -------------------------------------------------------------------------
|
| 1130 |
|
| 1131 |
with tab1:
|
|
|
|
| 1158 |
</div>
|
| 1159 |
""", unsafe_allow_html=True)
|
| 1160 |
|
| 1161 |
+
# File upload interface with UNIQUE KEY
|
| 1162 |
st.markdown("### π Upload Your Invoices")
|
| 1163 |
|
| 1164 |
uploaded_files = st.file_uploader(
|
| 1165 |
"Choose invoice files (PDF, TXT supported)",
|
| 1166 |
type=['pdf', 'txt'],
|
| 1167 |
accept_multiple_files=True,
|
| 1168 |
+
help=f"Maximum file size: {HF_CONFIG['max_file_size_mb']}MB per file",
|
| 1169 |
+
key="main_file_uploader" # UNIQUE KEY ADDED
|
| 1170 |
)
|
| 1171 |
|
| 1172 |
if uploaded_files:
|
|
|
|
| 1177 |
|
| 1178 |
st.info(f"π {len(uploaded_files)} files selected")
|
| 1179 |
|
| 1180 |
+
if st.button("π Process Files", type="primary", use_container_width=True, key="process_files_btn"):
|
| 1181 |
progress_bar = st.progress(0)
|
| 1182 |
status_container = st.container()
|
| 1183 |
results_container = st.container()
|
|
|
|
| 1197 |
with results_container:
|
| 1198 |
if result.invoice_number:
|
| 1199 |
successful += 1
|
| 1200 |
+
with st.expander(f"β
{uploaded_file.name}", expanded=False, key=f"result_expander_{i}"):
|
| 1201 |
col1, col2 = st.columns(2)
|
| 1202 |
with col1:
|
| 1203 |
st.write(f"**Invoice #:** {result.invoice_number}")
|
|
|
|
| 1219 |
st.balloons()
|
| 1220 |
|
| 1221 |
# -------------------------------------------------------------------------
|
| 1222 |
+
# TAB 2: AI CHAT - FIXED LAYOUT WITH UNIQUE KEYS
|
| 1223 |
# -------------------------------------------------------------------------
|
| 1224 |
|
| 1225 |
with tab2:
|
|
|
|
| 1230 |
st.markdown("### π¬ Chat History")
|
| 1231 |
chat_container = st.container()
|
| 1232 |
with chat_container:
|
| 1233 |
+
for i, message in enumerate(st.session_state.chat_history):
|
| 1234 |
with st.chat_message(message["role"]):
|
| 1235 |
st.markdown(message["content"])
|
| 1236 |
|
|
|
|
| 1249 |
"Find invoices with high amounts"
|
| 1250 |
]
|
| 1251 |
for i, query in enumerate(queries):
|
| 1252 |
+
if st.button(query, key=f"basic_query_{i}", use_container_width=True):
|
| 1253 |
st.session_state.chat_history.append({"role": "user", "content": query, "timestamp": datetime.now()})
|
| 1254 |
response = st.session_state.hf_chatbot.query_database(query)
|
| 1255 |
st.session_state.chat_history.append({"role": "assistant", "content": response, "timestamp": datetime.now()})
|
|
|
|
| 1265 |
"Find maintenance contracts"
|
| 1266 |
]
|
| 1267 |
for i, query in enumerate(semantic_queries):
|
| 1268 |
+
if st.button(query, key=f"semantic_query_{i}", use_container_width=True):
|
| 1269 |
st.session_state.chat_history.append({"role": "user", "content": query, "timestamp": datetime.now()})
|
| 1270 |
response = st.session_state.hf_chatbot.query_database(query)
|
| 1271 |
st.session_state.chat_history.append({"role": "assistant", "content": response, "timestamp": datetime.now()})
|
|
|
|
| 1282 |
user_input = st.text_input(
|
| 1283 |
"Type your question here:",
|
| 1284 |
placeholder="e.g., 'show me total spending' or 'find technology purchases'",
|
| 1285 |
+
key="chat_text_input_tab" # UNIQUE KEY ADDED
|
| 1286 |
)
|
| 1287 |
|
| 1288 |
with col2:
|
| 1289 |
+
ask_button = st.button("π Ask", type="primary", use_container_width=True, key="ask_button_tab")
|
| 1290 |
|
| 1291 |
# Process the input
|
| 1292 |
if ask_button and user_input:
|
|
|
|
| 1312 |
|
| 1313 |
# Clear chat button
|
| 1314 |
if st.session_state.chat_history:
|
| 1315 |
+
if st.button("ποΈ Clear Chat History", use_container_width=True, key="clear_chat_btn"):
|
| 1316 |
st.session_state.chat_history = []
|
| 1317 |
st.rerun()
|
| 1318 |
|
| 1319 |
# -------------------------------------------------------------------------
|
| 1320 |
+
# TAB 3: ANALYTICS - WITH UNIQUE KEYS
|
| 1321 |
# -------------------------------------------------------------------------
|
| 1322 |
|
| 1323 |
with tab3:
|
|
|
|
| 1365 |
title="Invoice Amount Distribution",
|
| 1366 |
labels={'amount': 'Amount (βΉ)', 'count': 'Number of Invoices'}
|
| 1367 |
)
|
| 1368 |
+
st.plotly_chart(fig_hist, use_container_width=True, key="amount_histogram")
|
| 1369 |
|
| 1370 |
# Top suppliers
|
| 1371 |
if df['supplier_name'].notna().any():
|
|
|
|
| 1378 |
title="Top 10 Suppliers by Total Amount",
|
| 1379 |
labels={'x': 'Total Amount (βΉ)', 'y': 'Supplier'}
|
| 1380 |
)
|
| 1381 |
+
st.plotly_chart(fig_suppliers, use_container_width=True, key="suppliers_chart")
|
| 1382 |
|
| 1383 |
# Confidence analysis
|
| 1384 |
fig_confidence = px.histogram(
|
|
|
|
| 1387 |
title="Extraction Confidence Distribution",
|
| 1388 |
labels={'confidence': 'Confidence Score', 'count': 'Number of Invoices'}
|
| 1389 |
)
|
| 1390 |
+
st.plotly_chart(fig_confidence, use_container_width=True, key="confidence_chart")
|
| 1391 |
|
| 1392 |
except Exception as e:
|
| 1393 |
st.error(f"Analytics error: {e}")
|
| 1394 |
|
| 1395 |
# -------------------------------------------------------------------------
|
| 1396 |
+
# TAB 4: DATA EXPLORER - WITH UNIQUE KEYS
|
| 1397 |
# -------------------------------------------------------------------------
|
| 1398 |
|
| 1399 |
with tab4:
|
|
|
|
| 1424 |
|
| 1425 |
df = pd.DataFrame(df_data)
|
| 1426 |
|
| 1427 |
+
# Filters with unique keys
|
| 1428 |
col1, col2, col3 = st.columns(3)
|
| 1429 |
|
| 1430 |
with col1:
|
| 1431 |
suppliers = ['All'] + sorted(df['Supplier'].dropna().unique().tolist())
|
| 1432 |
+
selected_supplier = st.selectbox("Filter by Supplier", suppliers, key="supplier_filter")
|
| 1433 |
|
| 1434 |
with col2:
|
| 1435 |
methods = ['All'] + sorted(df['Method'].dropna().unique().tolist())
|
| 1436 |
+
selected_method = st.selectbox("Filter by Method", methods, key="method_filter")
|
| 1437 |
|
| 1438 |
with col3:
|
| 1439 |
+
min_amount = st.number_input("Min Amount", min_value=0.0, value=0.0, key="amount_filter")
|
| 1440 |
|
| 1441 |
# Apply filters
|
| 1442 |
filtered_df = df.copy()
|
|
|
|
| 1454 |
column_config={
|
| 1455 |
"Amount": st.column_config.NumberColumn("Amount", format="βΉ%.2f"),
|
| 1456 |
"Confidence": st.column_config.ProgressColumn("Confidence", min_value=0, max_value=1)
|
| 1457 |
+
},
|
| 1458 |
+
key="data_explorer_table" # UNIQUE KEY ADDED
|
| 1459 |
)
|
| 1460 |
|
| 1461 |
+
# Export options with unique keys
|
| 1462 |
col1, col2 = st.columns(2)
|
| 1463 |
|
| 1464 |
with col1:
|
| 1465 |
+
if st.button("π₯ Export CSV", use_container_width=True, key="export_csv_btn"):
|
| 1466 |
csv_data = filtered_df.to_csv(index=False)
|
| 1467 |
st.download_button(
|
| 1468 |
"Download CSV",
|
| 1469 |
csv_data,
|
| 1470 |
f"invoices_{datetime.now().strftime('%Y%m%d_%H%M')}.csv",
|
| 1471 |
+
"text/csv",
|
| 1472 |
+
key="download_csv_btn"
|
| 1473 |
)
|
| 1474 |
|
| 1475 |
with col2:
|
| 1476 |
+
if st.button("π Export JSON", use_container_width=True, key="export_json_btn"):
|
| 1477 |
filtered_invoices = [inv for inv in invoices
|
| 1478 |
if inv.get('invoice_number') in filtered_df['Invoice Number'].values]
|
| 1479 |
|
|
|
|
| 1487 |
"Download JSON",
|
| 1488 |
json.dumps(export_data, indent=2),
|
| 1489 |
f"invoices_{datetime.now().strftime('%Y%m%d_%H%M')}.json",
|
| 1490 |
+
"application/json",
|
| 1491 |
+
key="download_json_btn"
|
| 1492 |
)
|
| 1493 |
|
| 1494 |
except Exception as e:
|
| 1495 |
st.error(f"Data explorer error: {e}")
|
| 1496 |
|
| 1497 |
# -------------------------------------------------------------------------
|
| 1498 |
+
# GLOBAL CHAT INPUT - OUTSIDE TABS WITH UNIQUE KEY
|
| 1499 |
# -------------------------------------------------------------------------
|
| 1500 |
|
| 1501 |
# Add some spacing
|
|
|
|
| 1504 |
# Global chat input that works from any tab
|
| 1505 |
st.markdown("### π¬ Quick Chat (Available from any tab)")
|
| 1506 |
|
| 1507 |
+
# Use chat_input outside of tabs with unique key - this will work
|
| 1508 |
+
global_user_query = st.chat_input("Ask about your invoices from anywhere...", key="global_chat_input")
|
| 1509 |
|
| 1510 |
if global_user_query:
|
| 1511 |
# Add user message
|
|
|
|
| 1545 |
# MAIN APPLICATION ENTRY POINT
|
| 1546 |
# ===============================================================================
|
| 1547 |
|
| 1548 |
+
def main():
|
| 1549 |
+
"""Main entry point for Hugging Face Spaces"""
|
| 1550 |
+
try:
|
| 1551 |
+
# Display Hugging Face info if running on HF Spaces
|
| 1552 |
+
if IS_HF_SPACE:
|
| 1553 |
+
st.sidebar.info("π€ Running on Hugging Face Spaces")
|
| 1554 |
+
|
| 1555 |
+
# Create and run the app
|
| 1556 |
+
create_huggingface_app()
|
| 1557 |
+
|
| 1558 |
+
except Exception as e:
|
| 1559 |
+
st.error(f"Application error: {e}")
|
| 1560 |
+
st.info("Please refresh the page or contact support if the error persists.")
|
| 1561 |
+
|
| 1562 |
+
if __name__ == "__main__":
|
| 1563 |
+
main()
|
| 1564 |
+
|
| 1565 |
+
# ===============================================================================
|
| 1566 |
+
# MAIN APPLICATION ENTRY POINT
|
| 1567 |
+
# ===============================================================================
|
| 1568 |
+
|
| 1569 |
def main():
|
| 1570 |
"""Main entry point for Hugging Face Spaces"""
|
| 1571 |
try:
|