jostlebot Claude Opus 4.5 commited on
Commit
eedbb82
·
1 Parent(s): b79f970

Redesign Session Log with full conversations and clinical highlights

Browse files

- Session selector dropdown to pick saved sessions
- Full conversation display (complete back-and-forth, not preview)
- Clinical Highlights panel with 'Generate' button per session
- Highlights identify key moments, quote specific language, note clinical observations
- Shows prompt sculpting observations (what's working, what tensions emerged)
- Cross-session reports (Session Report, A/B Comparison) remain available
- Cleaner two-column layout: conversation left, highlights right

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

Files changed (2) hide show
  1. __pycache__/app.cpython-313.pyc +0 -0
  2. app.py +176 -27
__pycache__/app.cpython-313.pyc CHANGED
Binary files a/__pycache__/app.cpython-313.pyc and b/__pycache__/app.cpython-313.pyc differ
 
app.py CHANGED
@@ -89,27 +89,128 @@ def save_to_session_log(sessions, system_prompt, history, persona, label):
89
 
90
 
91
  def format_session_display(sessions):
92
- """Format sessions for display."""
93
  if not sessions:
94
- return "No sessions saved yet. Use 'Save to Session Log' in Test & Analyze tab."
 
95
 
96
- display = ""
97
- for i, s in enumerate(sessions):
98
- display += f"### {i+1}. {s['label']}\n"
99
- display += f"*{s['timestamp']} | Persona: {s['persona']} | {s['exchange_count']} exchanges*\n\n"
100
- display += f"**Prompt excerpt:** {s['system_prompt'][:200]}...\n\n"
101
 
102
- # Show first exchange as preview
103
- if s['conversation']:
104
- user_msg, bot_msg = s['conversation'][0]
105
- display += f"**First exchange:**\n> User: {user_msg[:100]}...\n\n"
106
- display += f"> Bot: {bot_msg[:150]}...\n\n"
 
107
 
108
- display += "---\n\n"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
109
 
110
  return display
111
 
112
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
113
  def generate_session_report(api_key_input, sessions):
114
  """Generate clinical summary across all saved sessions."""
115
  key_to_use = api_key_input.strip() if api_key_input else ""
@@ -776,32 +877,55 @@ with gr.Blocks(title="PromptWork", theme=gr.themes.Soft()) as app:
776
  # TAB 3: Session Log
777
  with gr.Tab("Session Log"):
778
  gr.Markdown("### Saved Test Sessions")
779
- gr.Markdown("*Conversations saved from Test & Analyze appear here. Label sessions for A/B comparison.*")
780
 
781
  with gr.Row():
782
- with gr.Column(scale=2):
783
- session_display = gr.Markdown("No sessions saved yet. Use 'Save to Session Log' in Test & Analyze tab.")
 
 
 
 
 
 
 
784
 
 
785
  with gr.Column(scale=1):
786
- gr.Markdown("### Generate Reports")
 
 
 
 
787
 
788
- gr.Markdown("**Session Report** - Clinical summary across all saved sessions")
789
- generate_report_btn = gr.Button("Generate Session Report", variant="primary")
 
 
 
790
 
791
- gr.Markdown("---")
792
 
793
- gr.Markdown("**A/B Comparison** - Compare sessions labeled 'A' vs 'B'")
794
- gr.Markdown("*Tip: Label sessions as 'Prompt A', 'Prompt B' (or just 'A', 'B') when saving*")
795
- generate_ab_btn = gr.Button("Generate A/B Comparison", variant="primary")
 
796
 
797
- gr.Markdown("---")
 
 
 
 
 
 
798
 
 
799
  clear_sessions_btn = gr.Button("Clear All Sessions", variant="stop")
800
  clear_status = gr.Textbox(label="", interactive=False, show_label=False)
801
 
802
  gr.Markdown("---")
803
  gr.Markdown("### Report Output")
804
- report_output = gr.Markdown("*Reports will appear here after generation*")
805
 
806
  # TAB 4: Compare Responses (manual paste)
807
  with gr.Tab("Compare Responses"):
@@ -858,6 +982,10 @@ with gr.Blocks(title="PromptWork", theme=gr.themes.Soft()) as app:
858
  clear_btn.click(clear_chat, [], [chatbot])
859
 
860
  # Session log events
 
 
 
 
861
  save_session_btn.click(
862
  save_to_session_log,
863
  [session_state, prompt_input, chatbot, persona_dropdown, session_label],
@@ -866,6 +994,24 @@ with gr.Blocks(title="PromptWork", theme=gr.themes.Soft()) as app:
866
  format_session_display,
867
  [session_state],
868
  [session_display]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
869
  )
870
 
871
  generate_report_btn.click(
@@ -880,10 +1026,13 @@ with gr.Blocks(title="PromptWork", theme=gr.themes.Soft()) as app:
880
  [report_output]
881
  )
882
 
 
 
 
883
  clear_sessions_btn.click(
884
- clear_sessions,
885
  [],
886
- [session_state, clear_status]
887
  ).then(
888
  format_session_display,
889
  [session_state],
 
89
 
90
 
91
  def format_session_display(sessions):
92
+ """Format sessions for dropdown display."""
93
  if not sessions:
94
+ return "No sessions saved yet."
95
+ return f"{len(sessions)} session(s) saved. Select one below to view."
96
 
 
 
 
 
 
97
 
98
+ def get_session_choices(sessions):
99
+ """Get dropdown choices for sessions."""
100
+ if not sessions:
101
+ return []
102
+ return [(f"{s['label']} ({s['timestamp']}, {s['persona']})", i) for i, s in enumerate(sessions)]
103
+
104
 
105
+ def format_full_conversation(sessions, selected_index):
106
+ """Format the full conversation for a selected session."""
107
+ if not sessions or selected_index is None:
108
+ return "Select a session to view the full conversation."
109
+
110
+ try:
111
+ idx = int(selected_index)
112
+ s = sessions[idx]
113
+ except (ValueError, IndexError, TypeError):
114
+ return "Select a session to view."
115
+
116
+ display = f"## {s['label']}\n"
117
+ display += f"*{s['timestamp']} | Persona: {s['persona']}*\n\n"
118
+ display += f"---\n\n"
119
+ display += f"**System Prompt:**\n```\n{s['full_prompt']}\n```\n\n"
120
+ display += f"---\n\n"
121
+ display += "### Conversation\n\n"
122
+
123
+ for i, (user_msg, bot_msg) in enumerate(s['conversation']):
124
+ display += f"**USER:**\n{user_msg}\n\n"
125
+ display += f"**BOT:**\n{bot_msg}\n\n"
126
+ if i < len(s['conversation']) - 1:
127
+ display += "---\n\n"
128
 
129
  return display
130
 
131
 
132
+ def generate_session_highlights(api_key_input, sessions, selected_index):
133
+ """Generate clinical highlights for a specific session."""
134
+ key_to_use = api_key_input.strip() if api_key_input else ""
135
+ if not key_to_use:
136
+ key_to_use, _ = get_api_key_from_env()
137
+
138
+ if not key_to_use:
139
+ return "API key required."
140
+
141
+ if not sessions or selected_index is None:
142
+ return "Select a session first."
143
+
144
+ try:
145
+ idx = int(selected_index)
146
+ s = sessions[idx]
147
+ except (ValueError, IndexError, TypeError):
148
+ return "Select a session first."
149
+
150
+ # Format conversation
151
+ conv_text = ""
152
+ for user_msg, bot_msg in s['conversation']:
153
+ conv_text += f"USER: {user_msg}\n\nBOT: {bot_msg}\n\n---\n\n"
154
+
155
+ highlights_prompt = f"""You are a clinical consultant reviewing this AI chatbot conversation. Generate concise clinical highlights—observations a trauma-informed prompt engineer would find valuable.
156
+
157
+ SYSTEM PROMPT:
158
+ {s['full_prompt']}
159
+
160
+ CONVERSATION:
161
+ {conv_text}
162
+
163
+ ---
164
+
165
+ Generate clinical highlights in this format:
166
+
167
+ ## Key Moments
168
+
169
+ For each notable exchange, quote the specific language and note what's happening clinically:
170
+
171
+ **Moment 1: [Brief title]**
172
+ > [Quote the key phrase]
173
+
174
+ *Clinical note:* [1-2 sentences on what this reveals about the prompt's effect—good or concerning]
175
+
176
+ **Moment 2: [Brief title]**
177
+ > [Quote]
178
+
179
+ *Clinical note:* [Observation]
180
+
181
+ (Continue for 3-5 key moments)
182
+
183
+ ---
184
+
185
+ ## Prompt Sculpting Observations
186
+
187
+ What is the system prompt successfully doing here?
188
+ - [Observation with quote]
189
+ - [Observation with quote]
190
+
191
+ What tensions or risks emerged?
192
+ - [Observation with quote]
193
+
194
+ ## One-Line Summary
195
+
196
+ [Single sentence capturing the psychodynamic signature of this exchange]
197
+
198
+ ---
199
+
200
+ Be specific. Quote actual phrases. Focus on the nuances a clinician would notice—boundary maintenance, first-person language choices, bridging to human support, capacity-building vs dependency, the displaced listener."""
201
+
202
+ try:
203
+ client = anthropic.Anthropic(api_key=key_to_use)
204
+ response = client.messages.create(
205
+ model="claude-sonnet-4-20250514",
206
+ max_tokens=1500,
207
+ messages=[{"role": "user", "content": highlights_prompt}]
208
+ )
209
+ return response.content[0].text
210
+ except Exception as e:
211
+ return f"Error: {str(e)}"
212
+
213
+
214
  def generate_session_report(api_key_input, sessions):
215
  """Generate clinical summary across all saved sessions."""
216
  key_to_use = api_key_input.strip() if api_key_input else ""
 
877
  # TAB 3: Session Log
878
  with gr.Tab("Session Log"):
879
  gr.Markdown("### Saved Test Sessions")
880
+ gr.Markdown("*View full conversations with clinical highlights. Label sessions 'A'/'B' for comparison.*")
881
 
882
  with gr.Row():
883
+ session_display = gr.Markdown("No sessions saved yet.")
884
+ session_selector = gr.Dropdown(
885
+ label="Select Session",
886
+ choices=[],
887
+ value=None,
888
+ interactive=True,
889
+ scale=2
890
+ )
891
+ generate_highlights_btn = gr.Button("Generate Clinical Highlights", variant="primary", scale=1)
892
 
893
+ with gr.Row():
894
  with gr.Column(scale=1):
895
+ gr.Markdown("### Full Conversation")
896
+ conversation_display = gr.Markdown(
897
+ "Select a session above to view the full conversation.",
898
+ elem_id="conversation-display"
899
+ )
900
 
901
+ with gr.Column(scale=1):
902
+ gr.Markdown("### Clinical Highlights")
903
+ highlights_display = gr.Markdown(
904
+ "*Click 'Generate Clinical Highlights' to analyze the selected conversation.*\n\nHighlights will identify key moments, quote specific language, and note what's happening clinically—boundary maintenance, first-person choices, bridging to human support, capacity-building vs dependency."
905
+ )
906
 
907
+ gr.Markdown("---")
908
 
909
+ with gr.Row():
910
+ with gr.Column(scale=1):
911
+ gr.Markdown("### Cross-Session Reports")
912
+ gr.Markdown("*Synthesize patterns across all saved sessions*")
913
 
914
+ with gr.Column(scale=1):
915
+ generate_report_btn = gr.Button("Generate Session Report", variant="secondary")
916
+ gr.Markdown("*Clinical summary across all sessions*")
917
+
918
+ with gr.Column(scale=1):
919
+ generate_ab_btn = gr.Button("Generate A/B Comparison", variant="secondary")
920
+ gr.Markdown("*Compare sessions labeled A vs B*")
921
 
922
+ with gr.Column(scale=1):
923
  clear_sessions_btn = gr.Button("Clear All Sessions", variant="stop")
924
  clear_status = gr.Textbox(label="", interactive=False, show_label=False)
925
 
926
  gr.Markdown("---")
927
  gr.Markdown("### Report Output")
928
+ report_output = gr.Markdown("*Cross-session reports will appear here*")
929
 
930
  # TAB 4: Compare Responses (manual paste)
931
  with gr.Tab("Compare Responses"):
 
982
  clear_btn.click(clear_chat, [], [chatbot])
983
 
984
  # Session log events
985
+ def update_session_dropdown(sessions):
986
+ choices = get_session_choices(sessions)
987
+ return gr.Dropdown(choices=choices, value=choices[-1][1] if choices else None)
988
+
989
  save_session_btn.click(
990
  save_to_session_log,
991
  [session_state, prompt_input, chatbot, persona_dropdown, session_label],
 
994
  format_session_display,
995
  [session_state],
996
  [session_display]
997
+ ).then(
998
+ update_session_dropdown,
999
+ [session_state],
1000
+ [session_selector]
1001
+ )
1002
+
1003
+ # When session is selected, show full conversation
1004
+ session_selector.change(
1005
+ format_full_conversation,
1006
+ [session_state, session_selector],
1007
+ [conversation_display]
1008
+ )
1009
+
1010
+ # Generate highlights for selected session
1011
+ generate_highlights_btn.click(
1012
+ generate_session_highlights,
1013
+ [api_key, session_state, session_selector],
1014
+ [highlights_display]
1015
  )
1016
 
1017
  generate_report_btn.click(
 
1026
  [report_output]
1027
  )
1028
 
1029
+ def clear_and_reset():
1030
+ return [], "Sessions cleared.", gr.Dropdown(choices=[], value=None), "Select a session to view.", "*Click 'Generate Clinical Highlights' to analyze.*"
1031
+
1032
  clear_sessions_btn.click(
1033
+ clear_and_reset,
1034
  [],
1035
+ [session_state, clear_status, session_selector, conversation_display, highlights_display]
1036
  ).then(
1037
  format_session_display,
1038
  [session_state],