Spaces:
Running
on
Zero
Running
on
Zero
File size: 11,441 Bytes
6ea41ec |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 |
"""Chat interface UI component and logic."""
import gradio as gr
import logging
from src.core.logging_config import get_logger
from src.rag import rag_chat_service
from src.services.data_clearing_service import data_clearing_service
logger = get_logger(__name__)
def handle_chat_message(message, history):
"""Handle a new chat message with streaming response."""
if not message or not message.strip():
return "", history, gr.update()
try:
# Add user message to history
history = history or []
history.append({"role": "user", "content": message})
# Add assistant message placeholder
history.append({"role": "assistant", "content": ""})
# Get response from RAG service
response_text = ""
for chunk in rag_chat_service.chat_stream(message):
response_text += chunk
# Update the last message in history with the current response
history[-1]["content"] = response_text
# Update status in real-time during streaming
updated_status = get_chat_status()
yield "", history, updated_status
logger.info(f"Chat response completed for message: {message[:50]}...")
# Final status update after message completion
final_status = get_chat_status()
yield "", history, final_status
except Exception as e:
error_msg = f"Error generating response: {str(e)}"
logger.error(error_msg)
if history and len(history) > 0:
history[-1]["content"] = f"β {error_msg}"
else:
history = [
{"role": "user", "content": message},
{"role": "assistant", "content": f"β {error_msg}"}
]
# Update status even on error
error_status = get_chat_status()
yield "", history, error_status
def start_new_chat_session():
"""Start a new chat session."""
try:
session_id = rag_chat_service.start_new_session()
logger.info(f"Started new chat session: {session_id}")
return [], f"β
New chat session started: {session_id}"
except Exception as e:
error_msg = f"Error starting new session: {str(e)}"
logger.error(error_msg)
return [], f"β {error_msg}"
def handle_clear_all_data():
"""Handle clearing all RAG data (vector store + chat history)."""
try:
# Clear all data using the data clearing service
success, message, stats = data_clearing_service.clear_all_data()
if success:
# Reset chat session after clearing data
session_id = rag_chat_service.start_new_session()
# Get updated status
updated_status = get_chat_status()
# Create success message with stats
if stats.get("total_cleared_documents", 0) > 0 or stats.get("total_cleared_files", 0) > 0:
clear_msg = f"β
{message}"
session_msg = f"π Started new session: {session_id}"
combined_msg = f'{clear_msg}<br/><div class="session-info">{session_msg}</div>'
else:
combined_msg = f'βΉοΈ {message}<br/><div class="session-info">π Started new session: {session_id}</div>'
logger.info(f"Data cleared successfully: {message}")
return [], combined_msg, updated_status
else:
error_msg = f"β {message}"
logger.error(f"Data clearing failed: {message}")
# Still get updated status even on error
updated_status = get_chat_status()
return None, f'<div class="session-info">{error_msg}</div>', updated_status
except Exception as e:
error_msg = f"Error clearing data: {str(e)}"
logger.error(error_msg)
# Get current status
current_status = get_chat_status()
return None, f'<div class="session-info">β {error_msg}</div>', current_status
def get_chat_status():
"""Get current chat system status."""
try:
# Check ingestion status
from src.rag import document_ingestion_service
from src.services.data_clearing_service import data_clearing_service
ingestion_status = document_ingestion_service.get_ingestion_status()
# Check usage stats
usage_stats = rag_chat_service.get_usage_stats()
# Get data status for additional context
data_status = data_clearing_service.get_data_status()
# Get environment info
import os
env_type = "Hugging Face Space" if os.getenv("SPACE_ID") else "Local Development"
# Modern status card design with better styling
status_html = f"""
<div class="status-card">
<div class="status-header">
<h3>π¬ Chat System Status</h3>
<div class="status-indicator {'status-ready' if ingestion_status.get('system_ready', False) else 'status-not-ready'}">
{'π’ READY' if ingestion_status.get('system_ready', False) else 'π΄ NOT READY'}
</div>
</div>
<div class="status-grid">
<div class="status-item">
<div class="status-label">Vector Store Docs</div>
<div class="status-value">{data_status.get('vector_store', {}).get('document_count', 0)}</div>
</div>
<div class="status-item">
<div class="status-label">Chat History Files</div>
<div class="status-value">{data_status.get('chat_history', {}).get('file_count', 0)}</div>
</div>
<div class="status-item">
<div class="status-label">Session Usage</div>
<div class="status-value">{usage_stats.get('session_messages', 0)}/{usage_stats.get('session_limit', 50)}</div>
</div>
<div class="status-item">
<div class="status-label">Environment</div>
<div class="status-value">{'HF Space' if data_status.get('environment') == 'hf_space' else 'Local'}</div>
</div>
</div>
<div class="status-services">
<div class="service-status {'service-ready' if ingestion_status.get('embedding_model_available', False) else 'service-error'}">
<span class="service-icon">π§ </span>
<span>Embedding Model</span>
<span class="service-indicator">{'β
' if ingestion_status.get('embedding_model_available', False) else 'β'}</span>
</div>
<div class="service-status {'service-ready' if ingestion_status.get('vector_store_available', False) else 'service-error'}">
<span class="service-icon">ποΈ</span>
<span>Vector Store</span>
<span class="service-indicator">{'β
' if ingestion_status.get('vector_store_available', False) else 'β'}</span>
</div>
</div>
</div>
"""
return status_html
except Exception as e:
error_msg = f"Error getting chat status: {str(e)}"
logger.error(error_msg)
return f"""
<div class="status-card status-error">
<div class="status-header">
<h3>β System Error</h3>
</div>
<p class="error-message">{error_msg}</p>
</div>
"""
def create_chat_interface_tab():
"""Create the chat interface tab UI."""
with gr.TabItem("π¬ Chat with Documents"):
with gr.Column(elem_classes=["chat-tab-container"]):
# Header
gr.HTML("""
<div class="chat-header">
<h2>π¬ Chat with your converted documents</h2>
<p>Ask questions about your documents using advanced RAG technology</p>
</div>
""")
# Status monitoring
status_display = gr.HTML(value=get_chat_status())
# Control buttons
with gr.Row(elem_classes=["control-buttons"]):
refresh_btn = gr.Button("π Refresh Status", elem_classes=["control-btn", "btn-refresh"])
new_session_btn = gr.Button("π New Session", elem_classes=["control-btn", "btn-new-session"])
clear_data_btn = gr.Button("ποΈ Clear All Data", elem_classes=["control-btn", "btn-clear-data"], variant="stop")
# Chat interface
with gr.Column(elem_classes=["chat-main-container"]):
chatbot = gr.Chatbot(
elem_classes=["chat-container"],
height=500,
show_label=False,
show_share_button=False,
bubble_full_width=False,
type="messages",
placeholder="Start a conversation by asking questions about your documents..."
)
with gr.Row(elem_classes=["input-row"]):
msg_input = gr.Textbox(
placeholder="Ask questions about your documents...",
show_label=False,
scale=5,
lines=1,
max_lines=3,
elem_classes=["message-input"]
)
send_btn = gr.Button("Submit", elem_classes=["send-button"], scale=0)
# Session info display
session_info = gr.HTML(
value='<div class="session-info">No active session - Click "New Session" to start</div>'
)
# Event handlers for chat
def clear_input():
return ""
# Send message when button clicked or Enter pressed
msg_input.submit(
fn=handle_chat_message,
inputs=[msg_input, chatbot],
outputs=[msg_input, chatbot, status_display]
)
send_btn.click(
fn=handle_chat_message,
inputs=[msg_input, chatbot],
outputs=[msg_input, chatbot, status_display]
)
# Control button handlers
refresh_btn.click(
fn=get_chat_status,
inputs=[],
outputs=[status_display]
)
# New session handler with improved feedback
def enhanced_new_session():
history, info = start_new_chat_session()
session_html = f'<div class="session-info">{info}</div>'
updated_status = get_chat_status()
return history, session_html, updated_status
new_session_btn.click(
fn=enhanced_new_session,
inputs=[],
outputs=[chatbot, session_info, status_display]
)
clear_data_btn.click(
handle_clear_all_data,
outputs=[chatbot, session_info, status_display]
) |