Skip to content

Commit 669ac09

Browse files
authored
Saves Chat, Added "Read More" and "Delete This Response" options and more
Saves Chat, Added "Read More" and "Delete This Response" options. Last response deletes whole conversation. Adjusted buttons size. Removed thinking block <think> </think> from output.
1 parent b7bd1a1 commit 669ac09

File tree

1 file changed

+126
-30
lines changed

1 file changed

+126
-30
lines changed

Horizontal View - app.py

Lines changed: 126 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@
22
import requests
33
import time
44
import concurrent.futures
5+
import json
6+
import os
7+
import re
58

69
st.set_page_config(page_title="LLM Comparison", layout="wide")
710

@@ -15,6 +18,12 @@
1518
line-height: 1 !important;
1619
margin-top: 28px !important;
1720
}
21+
22+
div.stButton button[data-testid*="stButton-primary"] {
23+
font-size: 14px !important;
24+
height: 35px !important;
25+
}
26+
1827
div[data-testid="stSelectbox"] > div {
1928
margin-right: 0px !important;
2029
}
@@ -38,6 +47,44 @@ def get_models():
3847
st.warning("No models found. Ensure Ollama is running and has models pulled.")
3948
st.stop()
4049

50+
HISTORY_FILE = "chat_history.json"
51+
52+
def load_chat_history():
53+
if os.path.exists(HISTORY_FILE):
54+
try:
55+
with open(HISTORY_FILE, "r", encoding="utf-8") as f:
56+
return json.load(f)
57+
except json.JSONDecodeError:
58+
st.warning("Error decoding chat history file. Starting with empty history.")
59+
return []
60+
except Exception as e:
61+
st.error(f"Could not load chat history: {e}")
62+
return []
63+
return []
64+
65+
def save_chat_history(history):
66+
try:
67+
with open(HISTORY_FILE, "w", encoding="utf-8") as f:
68+
json.dump(history, f, indent=4)
69+
except Exception as e:
70+
st.error(f"Could not save chat history: {e}")
71+
72+
if "chat_history" not in st.session_state:
73+
st.session_state.chat_history = load_chat_history()
74+
75+
def delete_model_response(conversation_index, model_response_idx_in_entry):
76+
actual_conversation_index = len(st.session_state.chat_history) - 1 - conversation_index
77+
78+
if (0 <= actual_conversation_index < len(st.session_state.chat_history) and
79+
0 <= model_response_idx_in_entry < len(st.session_state.chat_history[actual_conversation_index]["responses"])):
80+
81+
st.session_state.chat_history[actual_conversation_index]["responses"].pop(model_response_idx_in_entry)
82+
83+
if not st.session_state.chat_history[actual_conversation_index]["responses"]:
84+
st.session_state.chat_history.pop(actual_conversation_index)
85+
86+
save_chat_history(st.session_state.chat_history)
87+
4188
prompt = st.text_area("Prompt", "")
4289

4390
if "model_count" not in st.session_state:
@@ -53,9 +100,8 @@ def remove_model(index):
53100
for i in range(st.session_state.model_count):
54101
col1, col2 = st.columns([0.97, 0.02])
55102
with col1:
56-
# Ensure the list is long enough
57103
if i >= len(st.session_state.selected_models):
58-
st.session_state.selected_models.append("")
104+
st.session_state.selected_models.append("")
59105

60106
st.session_state.selected_models[i] = st.selectbox(
61107
f"Model {i+1}",
@@ -68,17 +114,16 @@ def remove_model(index):
68114

69115
selected_models_filtered = [model for model in st.session_state.selected_models if model]
70116

71-
_, _, spacer, col_add, col_run = st.columns([0.5, 0.2, 0.1, 0.1, 0.1])
117+
_, col_add, col_run = st.columns([0.7, 0.15, 0.15])
72118
with col_add:
73-
if st.button("Add new model"):
119+
if st.button("Add New Model"):
74120
st.session_state.model_count += 1
75121
st.session_state.selected_models.append("")
76122
st.rerun()
77123
with col_run:
78124
run_clicked = st.button("Run Models", type="primary")
79125

80126
def query_ollama_model(model_name, prompt_text):
81-
"""Function to query a single Ollama model."""
82127
try:
83128
start_time = time.time()
84129
res = requests.post(
@@ -92,15 +137,18 @@ def query_ollama_model(model_name, prompt_text):
92137

93138
duration = round(end_time - start_time, 2)
94139
content = response_data.get("response", "")
95-
eval_count = response_data.get("eval_count", len(content.split()))
140+
141+
cleaned_content = re.sub(r"<think>.*?</think>", "", content, flags=re.DOTALL)
142+
143+
eval_count = response_data.get("eval_count", len(cleaned_content.split()))
96144
eval_rate = response_data.get("eval_rate", round(eval_count / duration, 2) if duration > 0 else 0)
97145

98146
return {
99147
"model": model_name,
100148
"duration": duration,
101149
"eval_count": eval_count,
102150
"eval_rate": eval_rate,
103-
"response": content
151+
"response": cleaned_content
104152
}
105153
except Exception as e:
106154
return {
@@ -114,14 +162,9 @@ def query_ollama_model(model_name, prompt_text):
114162
if run_clicked and prompt and selected_models_filtered:
115163
responses = []
116164

117-
# Create placeholders for immediate feedback
118-
response_placeholders = {model: st.empty() for model in selected_models_filtered}
119-
120165
with concurrent.futures.ThreadPoolExecutor(max_workers=len(selected_models_filtered)) as executor:
121-
# Submit tasks to the thread pool
122166
future_to_model = {executor.submit(query_ollama_model, model, prompt): model for model in selected_models_filtered}
123167

124-
# Iterate as futures complete
125168
for future in concurrent.futures.as_completed(future_to_model):
126169
model_name = future_to_model[future]
127170
try:
@@ -136,28 +179,81 @@ def query_ollama_model(model_name, prompt_text):
136179
"response": f"Error: {exc}"
137180
})
138181

139-
# Sort responses by the order of selected models for consistent display
140182
ordered_responses = []
141183
for model in selected_models_filtered:
142184
for res in responses:
143185
if res["model"] == model:
144186
ordered_responses.append(res)
145187
break
188+
189+
st.session_state.chat_history.append({"prompt": prompt, "responses": ordered_responses})
190+
save_chat_history(st.session_state.chat_history)
191+
192+
st.markdown("---")
193+
st.subheader("Previous Interactions")
194+
195+
def get_truncated_text(text, word_limit=50):
196+
words = text.split()
197+
if len(words) > word_limit:
198+
return ' '.join(words[:word_limit]) + "..."
199+
return text
146200

147-
cols = st.columns(len(ordered_responses))
148-
for i, res in enumerate(ordered_responses):
149-
with cols[i]:
150-
st.markdown(
151-
f"### <span style='color:#3366cc'>{res['model']}</span>" if i % 2 == 0 else f"### <span style='color:#cc0000'>{res['model']}</span>",
152-
unsafe_allow_html=True
153-
)
154-
st.markdown(
155-
f"""
156-
<div style="background-color:#e6f0ff; padding:10px; border-radius:8px; margin-bottom:10px;">
157-
<b>Duration</b>: <span style="color:#3366cc;">{res['duration']} secs</span> &nbsp;
158-
<b>Eval count</b>: <span style="color:green;">{res['eval_count']} tokens</span> &nbsp;
159-
<b>Eval rate</b>: <span style="color:green;">{res['eval_rate']} tokens/s</span>
160-
</div>
161-
""", unsafe_allow_html=True
162-
)
163-
st.write(res["response"])
201+
if st.session_state.chat_history:
202+
for entry_idx, entry in enumerate(reversed(st.session_state.chat_history)):
203+
st.markdown(f"**Prompt:** {entry['prompt']}")
204+
205+
cols = st.columns(len(entry['responses']))
206+
207+
for i, res in enumerate(entry['responses']):
208+
with cols[i]:
209+
st.markdown(
210+
f"### <span style='color:#3366cc'>{res['model']}</span>" if i % 2 == 0 else f"### <span style='color:#cc0000'>{res['model']}</span>",
211+
unsafe_allow_html=True
212+
)
213+
st.markdown(
214+
f"""
215+
<div style="background-color:#e6f0ff; padding:10px; border-radius:8px; margin-bottom:10px;">
216+
<b>Duration</b>: <span style="color:#3366cc;">{res['duration']} secs</span> &nbsp;
217+
<b>Eval count</b>: <span style="color:green;">{res['eval_count']} tokens</span> &nbsp;
218+
<b>Eval rate</b>: <span style="color:green;">{res['eval_rate']} tokens/s</span>
219+
</div>
220+
""", unsafe_allow_html=True
221+
)
222+
223+
full_response_text = res["response"]
224+
words = full_response_text.split()
225+
content_is_longer_than_50_words = len(words) > 50
226+
227+
read_more_toggle_key = f"read_more_entry_{entry_idx}_model_{i}"
228+
229+
if read_more_toggle_key not in st.session_state:
230+
st.session_state[read_more_toggle_key] = False
231+
232+
if content_is_longer_than_50_words and not st.session_state[read_more_toggle_key]:
233+
st.write(get_truncated_text(full_response_text, word_limit=50))
234+
else:
235+
st.write(full_response_text)
236+
237+
button_cols = st.columns([0.5, 0.5])
238+
239+
with button_cols[0]:
240+
if content_is_longer_than_50_words:
241+
if not st.session_state[read_more_toggle_key]:
242+
if st.button("Read More", key=f"btn_read_{read_more_toggle_key}"):
243+
st.session_state[read_more_toggle_key] = True
244+
st.rerun()
245+
else:
246+
if st.button("Show Less", key=f"btn_less_{read_more_toggle_key}"):
247+
st.session_state[read_more_toggle_key] = False
248+
st.rerun()
249+
250+
with button_cols[1]:
251+
st.button(
252+
"Delete This Response",
253+
key=f"delete_response_{entry_idx}_{i}",
254+
on_click=delete_model_response,
255+
args=(entry_idx, i)
256+
)
257+
st.markdown("---")
258+
else:
259+
st.info("No previous interactions found. Run models to start saving history!")

0 commit comments

Comments
 (0)