-
Notifications
You must be signed in to change notification settings - Fork 2
/
log_viewer.py
175 lines (146 loc) · 5.13 KB
/
log_viewer.py
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
import streamlit as st
import os
import json
import re
import time
from datetime import datetime
def load_json_file(file_path):
with open(file_path, "r") as file:
return json.load(file)
def color_json(json_str):
colors = {
"key": "#79c0ff",
"string": "#a5d6ff",
"number": "#ffa657",
"boolean": "#ff7b72",
"null": "#ff7b72",
"brace": "#c9d1d9",
}
def colorize(match):
token = match.group(0)
if ":" in token:
key = token.split(":")[0].strip('"')
return f'<span style="color: {colors["key"]}">{key}</span>:'
elif token.startswith('"'):
return f'<span style="color: {colors["string"]}">{token}</span>'
elif token in ("true", "false"):
return f'<span style="color: {colors["boolean"]}">{token}</span>'
elif token == "null":
return f'<span style="color: {colors["null"]}">{token}</span>'
elif token in "{}[]":
return f'<span style="color: {colors["brace"]}">{token}</span>'
else: # number
return f'<span style="color: {colors["number"]}">{token}</span>'
regex = r'("[^"]*")\s*:|"[^"]*"|-?\d+(?:\.\d+)?(?:[eE][+-]?\d+)?|true|false|null|[{}\[\]]'
return re.sub(regex, colorize, json_str)
def format_json(data):
def format_value(v):
if isinstance(v, str):
return html_escape(v).replace("\n", "<br>")
elif isinstance(v, (dict, list)):
return json.dumps(v, indent=2, ensure_ascii=False)
else:
return json.dumps(v)
def format_dict(d, indent=0):
result = []
for k, v in d.items():
if isinstance(v, dict):
result.append(f'{" " * indent}"{k}": {{')
result.append(format_dict(v, indent + 2))
result.append(f'{" " * indent}}}')
elif isinstance(v, list):
result.append(f'{" " * indent}"{k}": [')
for item in v:
if isinstance(item, dict):
result.append(f'{" " * (indent + 2)}{{')
result.append(format_dict(item, indent + 4))
result.append(f'{" " * (indent + 2)}}},')
else:
result.append(f'{" " * (indent + 2)}{format_value(item)},')
result.append(f'{" " * indent}]')
else:
result.append(f'{" " * indent}"{k}": {format_value(v)},')
return "<br>".join(result)
formatted_json = format_dict(data)
return color_json(f"<pre>{formatted_json}</pre>")
def html_escape(text):
return (
text.replace("&", "&")
.replace("<", "<")
.replace(">", ">")
.replace('"', """)
.replace("'", "'")
)
def get_log_files():
log_folder = "llm_logs"
files = [f for f in os.listdir(log_folder) if f.endswith(".json")]
return sorted(
files, key=lambda x: os.path.getmtime(os.path.join(log_folder, x)), reverse=True
)
st.set_page_config(layout="wide", page_title="LLM Log Viewer")
st.title("Lumos")
# Sidebar for options
st.sidebar.header("Options")
# Auto-refresh toggle
auto_refresh = st.sidebar.checkbox("Auto-refresh", value=True)
# Manual refresh button
if st.sidebar.button("Refresh Now"):
st.experimental_rerun()
# Get log files
log_files = get_log_files()
# File selection
st.sidebar.header("Select Log File")
for file in log_files:
file_path = os.path.join("llm_logs", file)
timestamp = datetime.fromtimestamp(os.path.getmtime(file_path)).strftime(
"%Y-%m-%d %H:%M:%S"
)
if st.sidebar.button(f"{timestamp} - {file}", key=file):
st.session_state.selected_file = file
# Main content area
if "selected_file" in st.session_state and st.session_state.selected_file in log_files:
file_path = os.path.join("llm_logs", st.session_state.selected_file)
data = load_json_file(file_path)
# Display request and response
col1, col2 = st.columns(2)
with col1:
st.subheader("Request")
request_html = format_json(data["request"])
st.markdown(
f'<div class="json-content">{request_html}</div>', unsafe_allow_html=True
)
with col2:
st.subheader("Response")
response_html = format_json(data["response"])
st.markdown(
f'<div class="json-content">{response_html}</div>', unsafe_allow_html=True
)
# Add custom CSS for better formatting
st.markdown(
"""
<style>
.json-content {
background-color: #0d1117;
color: #c9d1d9;
padding: 10px;
border-radius: 5px;
font-family: 'Courier New', Courier, monospace;
font-size: 0.7em;
white-space: pre-wrap;
word-wrap: break-word;
max-height: 80vh;
overflow-y: auto;
}
.json-content pre {
margin: 0;
}
</style>
""",
unsafe_allow_html=True,
)
else:
st.write("No file selected. Please choose a file from the sidebar.")
# Auto-refresh logic
if auto_refresh:
time.sleep(5) # Wait for 5 seconds
st.experimental_rerun()