Spaces:
Runtime error
Runtime error
Justadudeinspace
commited on
Add app demo of BLUX-cA
Browse files
app.py
CHANGED
|
@@ -20,7 +20,7 @@ Run:
|
|
| 20 |
"""
|
| 21 |
|
| 22 |
from __future__ import annotations
|
| 23 |
-
import json, re, time, uuid, logging
|
| 24 |
from dataclasses import dataclass, asdict
|
| 25 |
from pathlib import Path
|
| 26 |
from typing import Dict, List, Tuple, Optional, Any
|
|
@@ -272,13 +272,31 @@ class SessionManager:
|
|
| 272 |
def __init__(self):
|
| 273 |
self.session_id = str(uuid.uuid4())
|
| 274 |
self.start_time = time.time()
|
|
|
|
|
|
|
| 275 |
|
| 276 |
def get_session_info(self) -> Dict[str, Any]:
|
| 277 |
return {
|
| 278 |
"session_id": self.session_id,
|
| 279 |
"start_time": self.start_time,
|
| 280 |
-
"duration": time.time() - self.start_time
|
|
|
|
| 281 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 282 |
|
| 283 |
session_manager = SessionManager()
|
| 284 |
|
|
@@ -307,7 +325,7 @@ def ca_chat(
|
|
| 307 |
message: str,
|
| 308 |
enable_redaction: bool,
|
| 309 |
gentle_tone: bool,
|
| 310 |
-
|
| 311 |
) -> Tuple[List[Dict[str, str]], Dict[str, Any], Optional[str]]:
|
| 312 |
"""
|
| 313 |
Enhanced chat handler with proper Gradio messages format and error handling.
|
|
@@ -365,17 +383,30 @@ def ca_chat(
|
|
| 365 |
audit_event = make_audit_event(clean_message, found_redactions, compass_result, reply)
|
| 366 |
audit_dict = asdict(audit_event)
|
| 367 |
|
| 368 |
-
|
|
|
|
|
|
|
| 369 |
try:
|
| 370 |
-
|
| 371 |
-
|
| 372 |
-
with
|
| 373 |
f.write(json.dumps(audit_dict, ensure_ascii=False) + "\n")
|
| 374 |
-
|
|
|
|
| 375 |
except Exception as e:
|
| 376 |
-
logger.error(f"Failed to write audit log: {e}")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 377 |
|
| 378 |
-
return updated_history, audit_dict,
|
| 379 |
|
| 380 |
except Exception as e:
|
| 381 |
logger.error(f"Error in ca_chat: {e}")
|
|
@@ -440,6 +471,11 @@ def build_ui():
|
|
| 440 |
label="🌱 Gentle tone mode",
|
| 441 |
info="Softer, more supportive language"
|
| 442 |
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 443 |
|
| 444 |
with gr.Row():
|
| 445 |
send_btn = gr.Button("Send", variant="primary", size="lg")
|
|
@@ -465,41 +501,43 @@ def build_ui():
|
|
| 465 |
value={}
|
| 466 |
)
|
| 467 |
|
| 468 |
-
|
| 469 |
-
value="~/.outer_void/audit/blux_ca_demo.jsonl",
|
| 470 |
-
label="Audit Log Path",
|
| 471 |
-
info="JSONL file for append-only logging"
|
| 472 |
-
)
|
| 473 |
-
|
| 474 |
download = gr.File(
|
| 475 |
-
label="
|
| 476 |
-
visible=False
|
|
|
|
|
|
|
| 477 |
)
|
| 478 |
|
| 479 |
# Event handlers
|
| 480 |
-
def on_send(history, message, redact_enabled, gentle_enabled,
|
| 481 |
if not message or not message.strip():
|
| 482 |
gr.Warning("Please enter a message before sending.")
|
| 483 |
return history, {}, None, session_manager.get_session_info()
|
| 484 |
|
| 485 |
new_history, audit_data, download_path = ca_chat(
|
| 486 |
-
history or [], message, redact_enabled, gentle_enabled,
|
| 487 |
)
|
| 488 |
|
| 489 |
session_info = session_manager.get_session_info()
|
| 490 |
-
download_component = download_path if download_path else None
|
| 491 |
|
| 492 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 493 |
|
| 494 |
def on_clear():
|
| 495 |
-
|
| 496 |
-
session_manager.
|
| 497 |
-
|
|
|
|
|
|
|
| 498 |
|
| 499 |
# Connect components
|
| 500 |
send_btn.click(
|
| 501 |
fn=on_send,
|
| 502 |
-
inputs=[chat, msg, redact_toggle, gentle_tone,
|
| 503 |
outputs=[chat, audit_json, download, session_info]
|
| 504 |
).then(
|
| 505 |
lambda: "", # Clear message input
|
|
@@ -508,7 +546,7 @@ def build_ui():
|
|
| 508 |
|
| 509 |
msg.submit(
|
| 510 |
fn=on_send,
|
| 511 |
-
inputs=[chat, msg, redact_toggle, gentle_tone,
|
| 512 |
outputs=[chat, audit_json, download, session_info]
|
| 513 |
).then(
|
| 514 |
lambda: "", # Clear message input
|
|
@@ -532,6 +570,11 @@ def build_ui():
|
|
| 532 |
**Safety First**: Automatic crisis detection with immediate resource guidance
|
| 533 |
|
| 534 |
**Privacy**: Optional redaction of personal information before processing
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 535 |
""")
|
| 536 |
|
| 537 |
return demo
|
|
@@ -548,13 +591,23 @@ if __name__ == "__main__":
|
|
| 548 |
logger.error("Gradio not installed. Run: pip install gradio>=4.44.0")
|
| 549 |
exit(1)
|
| 550 |
|
| 551 |
-
|
| 552 |
-
|
| 553 |
-
|
| 554 |
-
|
| 555 |
-
|
| 556 |
-
|
| 557 |
-
|
| 558 |
-
|
| 559 |
-
|
| 560 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 20 |
"""
|
| 21 |
|
| 22 |
from __future__ import annotations
|
| 23 |
+
import json, re, time, uuid, logging, tempfile, shutil
|
| 24 |
from dataclasses import dataclass, asdict
|
| 25 |
from pathlib import Path
|
| 26 |
from typing import Dict, List, Tuple, Optional, Any
|
|
|
|
| 272 |
def __init__(self):
|
| 273 |
self.session_id = str(uuid.uuid4())
|
| 274 |
self.start_time = time.time()
|
| 275 |
+
self.temp_dir = Path(tempfile.mkdtemp(prefix="blux_ca_"))
|
| 276 |
+
logger.info(f"Session temp directory: {self.temp_dir}")
|
| 277 |
|
| 278 |
def get_session_info(self) -> Dict[str, Any]:
|
| 279 |
return {
|
| 280 |
"session_id": self.session_id,
|
| 281 |
"start_time": self.start_time,
|
| 282 |
+
"duration": round(time.time() - self.start_time, 2),
|
| 283 |
+
"temp_dir": str(self.temp_dir)
|
| 284 |
}
|
| 285 |
+
|
| 286 |
+
def create_temp_audit_file(self) -> Path:
|
| 287 |
+
"""Create a temporary audit file that Gradio can safely serve."""
|
| 288 |
+
audit_file = self.temp_dir / f"blux_ca_audit_{self.session_id}.jsonl"
|
| 289 |
+
audit_file.touch()
|
| 290 |
+
return audit_file
|
| 291 |
+
|
| 292 |
+
def cleanup(self):
|
| 293 |
+
"""Clean up temporary files."""
|
| 294 |
+
try:
|
| 295 |
+
if self.temp_dir.exists():
|
| 296 |
+
shutil.rmtree(self.temp_dir)
|
| 297 |
+
logger.info(f"Cleaned up temp directory: {self.temp_dir}")
|
| 298 |
+
except Exception as e:
|
| 299 |
+
logger.warning(f"Failed to clean up temp directory: {e}")
|
| 300 |
|
| 301 |
session_manager = SessionManager()
|
| 302 |
|
|
|
|
| 325 |
message: str,
|
| 326 |
enable_redaction: bool,
|
| 327 |
gentle_tone: bool,
|
| 328 |
+
use_temp_logging: bool # New parameter to control logging location
|
| 329 |
) -> Tuple[List[Dict[str, str]], Dict[str, Any], Optional[str]]:
|
| 330 |
"""
|
| 331 |
Enhanced chat handler with proper Gradio messages format and error handling.
|
|
|
|
| 383 |
audit_event = make_audit_event(clean_message, found_redactions, compass_result, reply)
|
| 384 |
audit_dict = asdict(audit_event)
|
| 385 |
|
| 386 |
+
download_path = None
|
| 387 |
+
|
| 388 |
+
if use_temp_logging:
|
| 389 |
try:
|
| 390 |
+
# Use temporary directory that Gradio can access
|
| 391 |
+
temp_audit_file = session_manager.create_temp_audit_file()
|
| 392 |
+
with temp_audit_file.open("a", encoding="utf-8") as f:
|
| 393 |
f.write(json.dumps(audit_dict, ensure_ascii=False) + "\n")
|
| 394 |
+
download_path = str(temp_audit_file)
|
| 395 |
+
logger.info(f"Audit event written to temporary file: {temp_audit_file}")
|
| 396 |
except Exception as e:
|
| 397 |
+
logger.error(f"Failed to write temporary audit log: {e}")
|
| 398 |
+
else:
|
| 399 |
+
# Original logging to custom path (without download capability)
|
| 400 |
+
try:
|
| 401 |
+
log_path = Path("~/.outer_void/audit/blux_ca_demo.jsonl").expanduser()
|
| 402 |
+
log_path.parent.mkdir(parents=True, exist_ok=True)
|
| 403 |
+
with log_path.open("a", encoding="utf-8") as f:
|
| 404 |
+
f.write(json.dumps(audit_dict, ensure_ascii=False) + "\n")
|
| 405 |
+
logger.info(f"Audit event written to persistent log: {log_path}")
|
| 406 |
+
except Exception as e:
|
| 407 |
+
logger.error(f"Failed to write persistent audit log: {e}")
|
| 408 |
|
| 409 |
+
return updated_history, audit_dict, download_path
|
| 410 |
|
| 411 |
except Exception as e:
|
| 412 |
logger.error(f"Error in ca_chat: {e}")
|
|
|
|
| 471 |
label="🌱 Gentle tone mode",
|
| 472 |
info="Softer, more supportive language"
|
| 473 |
)
|
| 474 |
+
temp_logging = gr.Checkbox(
|
| 475 |
+
True,
|
| 476 |
+
label="📥 Enable file downloads",
|
| 477 |
+
info="Allows downloading audit logs (uses temp files)"
|
| 478 |
+
)
|
| 479 |
|
| 480 |
with gr.Row():
|
| 481 |
send_btn = gr.Button("Send", variant="primary", size="lg")
|
|
|
|
| 501 |
value={}
|
| 502 |
)
|
| 503 |
|
| 504 |
+
gr.Markdown("### 💾 Download Audit Log")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 505 |
download = gr.File(
|
| 506 |
+
label="Session Audit Log",
|
| 507 |
+
visible=False,
|
| 508 |
+
file_types=[".jsonl"],
|
| 509 |
+
type="filepath"
|
| 510 |
)
|
| 511 |
|
| 512 |
# Event handlers
|
| 513 |
+
def on_send(history, message, redact_enabled, gentle_enabled, temp_logging_enabled):
|
| 514 |
if not message or not message.strip():
|
| 515 |
gr.Warning("Please enter a message before sending.")
|
| 516 |
return history, {}, None, session_manager.get_session_info()
|
| 517 |
|
| 518 |
new_history, audit_data, download_path = ca_chat(
|
| 519 |
+
history or [], message, redact_enabled, gentle_enabled, temp_logging_enabled
|
| 520 |
)
|
| 521 |
|
| 522 |
session_info = session_manager.get_session_info()
|
|
|
|
| 523 |
|
| 524 |
+
# Show download button only if we have a downloadable file
|
| 525 |
+
download_visible = download_path is not None
|
| 526 |
+
download_value = download_path if download_path else None
|
| 527 |
+
|
| 528 |
+
return new_history, audit_data, gr.update(visible=download_visible, value=download_value), session_info
|
| 529 |
|
| 530 |
def on_clear():
|
| 531 |
+
# Clean up old session and start new one
|
| 532 |
+
session_manager.cleanup()
|
| 533 |
+
session_manager.__init__() # Reinitialize for new session
|
| 534 |
+
|
| 535 |
+
return [], {}, gr.update(visible=False, value=None), session_manager.get_session_info()
|
| 536 |
|
| 537 |
# Connect components
|
| 538 |
send_btn.click(
|
| 539 |
fn=on_send,
|
| 540 |
+
inputs=[chat, msg, redact_toggle, gentle_tone, temp_logging],
|
| 541 |
outputs=[chat, audit_json, download, session_info]
|
| 542 |
).then(
|
| 543 |
lambda: "", # Clear message input
|
|
|
|
| 546 |
|
| 547 |
msg.submit(
|
| 548 |
fn=on_send,
|
| 549 |
+
inputs=[chat, msg, redact_toggle, gentle_tone, temp_logging],
|
| 550 |
outputs=[chat, audit_json, download, session_info]
|
| 551 |
).then(
|
| 552 |
lambda: "", # Clear message input
|
|
|
|
| 570 |
**Safety First**: Automatic crisis detection with immediate resource guidance
|
| 571 |
|
| 572 |
**Privacy**: Optional redaction of personal information before processing
|
| 573 |
+
|
| 574 |
+
### 📁 File Downloads
|
| 575 |
+
|
| 576 |
+
Enable "Download Audit Logs" to get a JSONL file containing all conversation events.
|
| 577 |
+
Files are stored in a temporary directory and cleaned up when you clear the history.
|
| 578 |
""")
|
| 579 |
|
| 580 |
return demo
|
|
|
|
| 591 |
logger.error("Gradio not installed. Run: pip install gradio>=4.44.0")
|
| 592 |
exit(1)
|
| 593 |
|
| 594 |
+
try:
|
| 595 |
+
# Launch the application with allowed paths for file downloads
|
| 596 |
+
demo = build_ui()
|
| 597 |
+
demo.launch(
|
| 598 |
+
server_name="0.0.0.0",
|
| 599 |
+
server_port=7860,
|
| 600 |
+
share=False,
|
| 601 |
+
show_error=True,
|
| 602 |
+
debug=False,
|
| 603 |
+
ssr_mode=False,
|
| 604 |
+
# Allow temp directory access for file downloads
|
| 605 |
+
allowed_paths=[session_manager.temp_dir]
|
| 606 |
+
)
|
| 607 |
+
except KeyboardInterrupt:
|
| 608 |
+
logger.info("Received interrupt signal - shutting down gracefully...")
|
| 609 |
+
except Exception as e:
|
| 610 |
+
logger.error(f"Failed to launch application: {e}")
|
| 611 |
+
finally:
|
| 612 |
+
# Clean up on exit
|
| 613 |
+
session_manager.cleanup()
|