|
|
import json |
|
|
from datetime import datetime |
|
|
|
|
|
import streamlit as st |
|
|
|
|
|
|
|
|
st.set_page_config(page_title="Conversation Viewer", layout="wide") |
|
|
|
|
|
@st.cache_data |
|
|
def load_conversations(uploaded_file): |
|
|
"""Load conversations from uploaded JSON file""" |
|
|
return json.load(uploaded_file) |
|
|
|
|
|
def format_timestamp(timestamp): |
|
|
"""Convert timestamp to readable format""" |
|
|
try: |
|
|
if timestamp > 1e12: |
|
|
timestamp = timestamp / 1000 |
|
|
return datetime.fromtimestamp(timestamp).strftime('%Y-%m-%d %H:%M:%S') |
|
|
except Exception: |
|
|
return "N/A" |
|
|
|
|
|
def search_in_conversation(conv, search_term): |
|
|
"""Check if search term exists in conversation""" |
|
|
if not search_term: |
|
|
return True |
|
|
|
|
|
search_term = search_term.lower() |
|
|
|
|
|
|
|
|
if search_term in conv['conv']['name'].lower(): |
|
|
return True |
|
|
|
|
|
|
|
|
for msg in conv['messages']: |
|
|
if 'content' in msg and search_term in msg['content'].lower(): |
|
|
return True |
|
|
|
|
|
return False |
|
|
|
|
|
def display_message(msg): |
|
|
"""Display a single message""" |
|
|
if msg['role'] == 'user': |
|
|
with st.chat_message("user"): |
|
|
st.markdown(msg['content']) |
|
|
elif msg['role'] == 'assistant': |
|
|
with st.chat_message("assistant"): |
|
|
|
|
|
content = msg['content'] |
|
|
|
|
|
|
|
|
if '<think>' in content and '</think>' in content: |
|
|
start = content.find('<think>') |
|
|
end = content.find('</think>') + len('</think>') |
|
|
|
|
|
|
|
|
thinking_content = content[start + len('<think>'):content.find('</think>')] |
|
|
response_content = content[end:].strip() |
|
|
|
|
|
|
|
|
with st.expander("π§ Internal Thinking", expanded=False): |
|
|
st.code(thinking_content.strip(), language="text", wrap_lines=True) |
|
|
|
|
|
|
|
|
if response_content: |
|
|
st.markdown(response_content) |
|
|
else: |
|
|
|
|
|
st.markdown(content) |
|
|
|
|
|
def main(): |
|
|
st.title("π¬ Conversation Viewer") |
|
|
st.write("XSRF:", st.get_option("server.enableXsrfProtection")) |
|
|
|
|
|
|
|
|
json_file = st.file_uploader("Upload JSON file", type="json") |
|
|
|
|
|
if json_file is None: |
|
|
st.info("π Please upload a JSON file containing your conversations to get started.") |
|
|
return |
|
|
|
|
|
try: |
|
|
conversations = load_conversations(json_file) |
|
|
|
|
|
|
|
|
with st.sidebar: |
|
|
st.header("Search & Filter") |
|
|
search_term = st.text_input("π Search conversations", "") |
|
|
|
|
|
st.divider() |
|
|
|
|
|
|
|
|
filtered_convs = [ |
|
|
conv for conv in conversations |
|
|
if search_in_conversation(conv, search_term) |
|
|
] |
|
|
|
|
|
st.write(f"**{len(filtered_convs)}** conversations found") |
|
|
|
|
|
|
|
|
sort_by = st.selectbox( |
|
|
"Sort by", |
|
|
["Most Recent", "Oldest First", "Alphabetical"] |
|
|
) |
|
|
|
|
|
if sort_by == "Most Recent": |
|
|
filtered_convs.sort(key=lambda x: x['conv'].get('lastModified', 0), reverse=True) |
|
|
elif sort_by == "Oldest First": |
|
|
filtered_convs.sort(key=lambda x: x['conv'].get('lastModified', 0)) |
|
|
else: |
|
|
filtered_convs.sort(key=lambda x: x['conv']['name']) |
|
|
|
|
|
st.divider() |
|
|
|
|
|
|
|
|
st.subheader("Conversations") |
|
|
selected_conv_id = st.radio( |
|
|
"Select a conversation", |
|
|
options=[conv['conv']['id'] for conv in filtered_convs], |
|
|
format_func=lambda x: next( |
|
|
(conv['conv']['name'][:50] + "..." if len(conv['conv']['name']) > 50 |
|
|
else conv['conv']['name']) |
|
|
for conv in filtered_convs if conv['conv']['id'] == x |
|
|
), |
|
|
label_visibility="collapsed" |
|
|
) |
|
|
|
|
|
|
|
|
if filtered_convs: |
|
|
|
|
|
selected_conv = next( |
|
|
(conv for conv in filtered_convs if conv['conv']['id'] == selected_conv_id), |
|
|
filtered_convs[0] |
|
|
) |
|
|
|
|
|
|
|
|
|
|
|
st.write(selected_conv['conv']['name']) |
|
|
|
|
|
col1, col2, col3 = st.columns(3) |
|
|
with col1: |
|
|
st.metric("Messages", len(selected_conv['messages']) - 1) |
|
|
with col2: |
|
|
st.metric("Last Modified", format_timestamp(selected_conv['conv']['lastModified'])) |
|
|
with col3: |
|
|
st.metric("Conv ID", selected_conv['conv']['id'][:8] + "...") |
|
|
|
|
|
st.divider() |
|
|
|
|
|
|
|
|
messages = [msg for msg in selected_conv['messages'] if msg['role'] != 'root'] |
|
|
|
|
|
for msg in messages: |
|
|
if msg['role'] in ['user', 'assistant']: |
|
|
display_message(msg) |
|
|
else: |
|
|
st.info("No conversations found matching your search.") |
|
|
|
|
|
except json.JSONDecodeError: |
|
|
st.error("β Invalid JSON file format. Please upload a valid JSON file.") |
|
|
except Exception as e: |
|
|
st.error(f"β Error loading conversations: {str(e)}") |
|
|
|
|
|
if __name__ == "__main__": |
|
|
main() |
|
|
|
|
|
|