|
|
| import streamlit as st |
| import requests |
| import json |
| import uuid |
| from datetime import datetime |
| import os |
|
|
| BACKEND_URL = os.getenv("BACKEND_URL", "http://localhost:8001") |
| BACKEND_DB = "/app/chat_history.db" |
|
|
| |
| st.set_page_config( |
| page_title="AI Agent Chat", |
| page_icon="π€", |
| layout="wide" |
| ) |
|
|
| |
| st.markdown(""" |
| <style> |
| .main { |
| padding: 0rem 1rem; |
| } |
| .stChatMessage { |
| padding: 1rem; |
| border-radius: 0.5rem; |
| } |
| </style> |
| """, unsafe_allow_html=True) |
|
|
|
|
| |
| def get_all_sessions_from_api(): |
| """Get all sessions from backend API""" |
| try: |
| |
| response = requests.get(f"{BACKEND_URL}/sessions", timeout=5) |
| if response.status_code == 200: |
| return response.json().get("sessions", []) |
| return [] |
| except Exception as e: |
| st.error(f"Failed to fetch sessions: {e}") |
| return [] |
|
|
|
|
| |
| def get_session_preview_from_api(session_id: str): |
| """Get session preview from backend API""" |
| try: |
| response = requests.get( |
| f"{BACKEND_URL}/history/{session_id}", |
| params={"limit": 1}, |
| timeout=5 |
| ) |
| if response.status_code == 200: |
| data = response.json() |
| messages = data.get("messages", []) |
| if messages and messages[0]["role"] == "user": |
| preview = messages[0]["content"] |
| return preview[:50] + "..." if len(preview) > 50 else preview |
| return "Empty chat" |
| except Exception: |
| return "Unknown" |
|
|
|
|
| def check_backend_health(): |
| """Check if backend is running""" |
| try: |
| response = requests.get(f"{BACKEND_URL}/health", timeout=5) |
| return response.status_code == 200 |
| except Exception: |
| return False |
|
|
|
|
| def get_chat_history(session_id: str): |
| """Fetch chat history from backend""" |
| try: |
| response = requests.get( |
| f"{BACKEND_URL}/history/{session_id}", |
| timeout=5 |
| ) |
| if response.status_code == 200: |
| data = response.json() |
| return data.get("messages", []) |
| return [] |
| except Exception as e: |
| st.error(f"Failed to fetch history: {e}") |
| return [] |
|
|
|
|
| def clear_backend_history(session_id: str): |
| """Clear chat history from backend""" |
| try: |
| response = requests.delete( |
| f"{BACKEND_URL}/history/{session_id}", |
| timeout=5 |
| ) |
| return response.status_code == 200 |
| except Exception: |
| return False |
|
|
|
|
| def load_session(session_id: str): |
| """Load a specific session""" |
| st.session_state.session_id = session_id |
| st.session_state.messages = [] |
| |
| |
| history = get_chat_history(session_id) |
| if history: |
| for msg in history: |
| st.session_state.messages.append({ |
| "role": msg["role"], |
| "content": msg["content"] |
| }) |
| |
| st.rerun() |
|
|
|
|
| def create_new_session(): |
| """Create a new session""" |
| new_session_id = str(uuid.uuid4()) |
| st.session_state.session_id = new_session_id |
| st.session_state.messages = [] |
| st.rerun() |
|
|
|
|
| def send_message_streaming(message: str): |
| """Send message and stream response TOKEN BY TOKEN""" |
| try: |
| url = f"{BACKEND_URL}/chat/stream" |
| payload = { |
| "message": message, |
| "session_id": st.session_state.session_id |
| } |
| |
| response = requests.post( |
| url, |
| json=payload, |
| stream=True, |
| timeout=60 |
| ) |
| |
| if response.status_code != 200: |
| return f"Error: {response.status_code}" |
| |
| |
| tool_placeholder = st.empty() |
| message_placeholder = st.empty() |
| |
| full_response = "" |
| tool_calls = [] |
| tool_results = [] |
| current_tools_shown = False |
| |
| |
| for line in response.iter_lines(): |
| if line: |
| line = line.decode('utf-8') |
| if line.startswith('data: '): |
| try: |
| data = json.loads(line[6:]) |
| |
| if data.get("type") == "tool_call": |
| tools = data.get("tools", []) |
| tool_calls.extend(tools) |
| tool_placeholder.markdown(f"π§ **Using tools:** {', '.join(tools)}") |
| current_tools_shown = True |
| |
| elif data.get("type") == "tool_start": |
| tool_name = data.get("tool", "unknown") |
| tool_placeholder.markdown(f"βοΈ **Executing:** {tool_name}...") |
| |
| elif data.get("type") == "tool_result": |
| tool_name = data.get("tool", "unknown") |
| preview = data.get("preview", "") |
| tool_results.append(f"{tool_name}: {preview[:100]}") |
| tool_placeholder.markdown(f"β
**Completed:** {tool_name}") |
| |
| elif data.get("type") == "token": |
| |
| full_response = data.get("content", "") |
| message_placeholder.markdown(full_response + "β") |
| |
| elif data.get("type") == "content": |
| |
| full_response = data.get("content", "") |
| message_placeholder.markdown(full_response + "β") |
| |
| elif data.get("type") == "done": |
| |
| if full_response: |
| message_placeholder.markdown(full_response) |
| else: |
| message_placeholder.warning("No response generated. The tool may have encountered an issue.") |
| |
| if current_tools_shown: |
| tool_placeholder.empty() |
| break |
| |
| elif data.get("type") == "error": |
| st.error(f"Error: {data.get('error')}") |
| return None |
| |
| except json.JSONDecodeError: |
| continue |
| |
| return full_response |
| |
| except Exception as e: |
| st.error(f"Failed to send message: {e}") |
| return None |
|
|
|
|
| |
| if "session_id" not in st.session_state: |
| st.session_state.session_id = str(uuid.uuid4()) |
|
|
| if "messages" not in st.session_state: |
| st.session_state.messages = [] |
|
|
|
|
| |
| with st.sidebar: |
| st.title("π€ AI Agent") |
| |
| |
| is_healthy = check_backend_health() |
| |
| if not is_healthy: |
| st.error("β Backend not available") |
| st.info(f"Backend URL: {BACKEND_URL}") |
| st.warning("Check if backend Space is running") |
| else: |
| st.success("β
Connected to backend") |
| |
| st.divider() |
| |
| |
| if st.button("β New Chat", use_container_width=True, type="primary"): |
| create_new_session() |
| |
| st.divider() |
| |
| |
| st.subheader("π¬ Chat History") |
| |
| sessions = get_all_sessions_from_api() |
| |
| if sessions: |
| for session in sessions: |
| session_id = session.get("session_id") |
| preview = session.get("preview", "Unknown") |
| is_current = session_id == st.session_state.session_id |
| |
| col1, col2 = st.columns([5, 1]) |
| |
| with col1: |
| button_label = f"{'π' if is_current else 'π¬'} {preview}" |
| if st.button( |
| button_label, |
| key=f"load_{session_id}", |
| use_container_width=True, |
| disabled=is_current, |
| ): |
| load_session(session_id) |
| |
| with col2: |
| if len(sessions) > 1 or not is_current: |
| if st.button("ποΈ", key=f"delete_{session_id}", help="Delete"): |
| if clear_backend_history(session_id): |
| if is_current: |
| create_new_session() |
| else: |
| st.rerun() |
| else: |
| st.info("No chat history yet") |
| |
| st.divider() |
| |
| |
| with st.expander("βΉοΈ Current Session"): |
| st.text(f"ID: {st.session_state.session_id[:16]}...") |
| st.text(f"Messages: {len(st.session_state.messages)}") |
| st.text(f"Backend: {BACKEND_URL}") |
| |
| if st.button("ποΈ Clear Current Chat", use_container_width=True): |
| if clear_backend_history(st.session_state.session_id): |
| st.session_state.messages = [] |
| st.success("Chat cleared!") |
| st.rerun() |
| |
| st.divider() |
| |
| |
| with st.expander("π§ Features"): |
| st.markdown(""" |
| - π¬ **Streaming responses** |
| - π§ **Conversation memory** |
| - π **Web search** |
| - π€οΈ **Weather info** |
| - π± **Currency conversion** |
| - π **Wikipedia lookup** |
| - β° **World time** |
| """) |
|
|
|
|
| |
| st.title("π¬ Chat with AI Agent") |
|
|
| |
| for message in st.session_state.messages: |
| with st.chat_message(message["role"]): |
| st.markdown(message["content"]) |
|
|
| |
| if prompt := st.chat_input("Type your message here...", disabled=not is_healthy): |
| |
| st.session_state.messages.append({"role": "user", "content": prompt}) |
| with st.chat_message("user"): |
| st.markdown(prompt) |
| |
| |
| with st.chat_message("assistant"): |
| response = send_message_streaming(prompt) |
| |
| if response: |
| st.session_state.messages.append({ |
| "role": "assistant", |
| "content": response |
| }) |
|
|
| |
| st.divider() |
| st.caption(f"π Backend: {BACKEND_URL}") |