Ryan commited on
Commit
08f222a
·
1 Parent(s): 39cf944
.idea/workspace.xml CHANGED
@@ -5,12 +5,7 @@
5
  </component>
6
  <component name="ChangeListManager">
7
  <list default="true" id="8e67814c-7f04-433c-ab7a-2b65a1106d4c" name="Changes" comment="">
8
- <change afterPath="$PROJECT_DIR$/processors/ngram_analysis.py" afterDir="false" />
9
- <change afterPath="$PROJECT_DIR$/visualization/ngram_visualizer.py" afterDir="false" />
10
  <change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" />
11
- <change beforePath="$PROJECT_DIR$/app.py" beforeDir="false" afterPath="$PROJECT_DIR$/app.py" afterDir="false" />
12
- <change beforePath="$PROJECT_DIR$/ui/analysis_screen.py" beforeDir="false" afterPath="$PROJECT_DIR$/ui/analysis_screen.py" afterDir="false" />
13
- <change beforePath="$PROJECT_DIR$/visualization/bow_visualizer.py" beforeDir="false" afterPath="$PROJECT_DIR$/visualization/bow_visualizer.py" afterDir="false" />
14
  </list>
15
  <option name="SHOW_DIALOG" value="false" />
16
  <option name="HIGHLIGHT_CONFLICTS" value="true" />
@@ -60,7 +55,7 @@
60
  <option name="presentableId" value="Default" />
61
  <updated>1745170754325</updated>
62
  <workItem from="1745170755404" duration="245000" />
63
- <workItem from="1745172030020" duration="7284000" />
64
  </task>
65
  <servers />
66
  </component>
 
5
  </component>
6
  <component name="ChangeListManager">
7
  <list default="true" id="8e67814c-7f04-433c-ab7a-2b65a1106d4c" name="Changes" comment="">
 
 
8
  <change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" />
 
 
 
9
  </list>
10
  <option name="SHOW_DIALOG" value="false" />
11
  <option name="HIGHLIGHT_CONFLICTS" value="true" />
 
55
  <option name="presentableId" value="Default" />
56
  <updated>1745170754325</updated>
57
  <workItem from="1745170755404" duration="245000" />
58
+ <workItem from="1745172030020" duration="9539000" />
59
  </task>
60
  <servers />
61
  </component>
_archive/app copy.py ADDED
@@ -0,0 +1,355 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+ from ui.dataset_input import create_dataset_input, load_example_dataset
3
+ from ui.analysis_screen import create_analysis_screen, process_analysis_request
4
+ from visualization.bow_visualizer import process_and_visualize_analysis
5
+ import nltk
6
+ import os
7
+ import json
8
+
9
+ # Download necessary NLTK resources function remains unchanged
10
+ def download_nltk_resources():
11
+ """Download required NLTK resources if not already downloaded"""
12
+ try:
13
+ # Create nltk_data directory in the user's home directory if it doesn't exist
14
+ nltk_data_path = os.path.expanduser("~/nltk_data")
15
+ os.makedirs(nltk_data_path, exist_ok=True)
16
+
17
+ # Add this path to NLTK's data path
18
+ nltk.data.path.append(nltk_data_path)
19
+
20
+ # Download required resources
21
+ resources = ['punkt', 'wordnet', 'stopwords', 'punkt_tab']
22
+ for resource in resources:
23
+ try:
24
+ # Different resources can be in different directories in NLTK
25
+ locations = [
26
+ f'tokenizers/{resource}',
27
+ f'corpora/{resource}',
28
+ f'taggers/{resource}',
29
+ f'{resource}'
30
+ ]
31
+
32
+ found = False
33
+ for location in locations:
34
+ try:
35
+ nltk.data.find(location)
36
+ print(f"Resource {resource} already downloaded")
37
+ found = True
38
+ break
39
+ except LookupError:
40
+ continue
41
+
42
+ if not found:
43
+ print(f"Downloading {resource}...")
44
+ nltk.download(resource, quiet=True)
45
+ except Exception as e:
46
+ print(f"Error with resource {resource}: {e}")
47
+
48
+ print("NLTK resources check completed")
49
+ except Exception as e:
50
+ print(f"Error downloading NLTK resources: {e}")
51
+
52
+ def create_app():
53
+ """
54
+ Create a streamlined Gradio app for dataset input and Bag of Words analysis.
55
+
56
+ Returns:
57
+ gr.Blocks: The Gradio application
58
+ """
59
+ with gr.Blocks(title="LLM Response Comparator") as app:
60
+ # Application state to share data between tabs
61
+ dataset_state = gr.State({})
62
+ analysis_results_state = gr.State({})
63
+
64
+ # Dataset Input Tab
65
+ with gr.Tab("Dataset Input"):
66
+ dataset_inputs, example_dropdown, load_example_btn, create_btn, prompt, response1, model1, response2, model2 = create_dataset_input()
67
+
68
+ # Add status indicator to show when dataset is created
69
+ dataset_status = gr.Markdown("*No dataset loaded*")
70
+
71
+ # Load example dataset
72
+ load_example_btn.click(
73
+ fn=load_example_dataset,
74
+ inputs=[example_dropdown],
75
+ outputs=[prompt, response1, model1, response2, model2] # Update all field values
76
+ )
77
+
78
+ # Save dataset to state and update status
79
+ def create_dataset(p, r1, m1, r2, m2):
80
+ if not p or not r1 or not r2:
81
+ return {}, "❌ **Error:** Please fill in at least the prompt and both responses"
82
+
83
+ dataset = {
84
+ "entries": [
85
+ {"prompt": p, "response": r1, "model": m1 or "Model 1"},
86
+ {"prompt": p, "response": r2, "model": m2 or "Model 2"}
87
+ ]
88
+ }
89
+ return dataset, "✅ **Dataset created successfully!** You can now go to the Analysis tab"
90
+
91
+ create_btn.click(
92
+ fn=create_dataset,
93
+ inputs=[prompt, response1, model1, response2, model2],
94
+ outputs=[dataset_state, dataset_status]
95
+ )
96
+
97
+ # Analysis Tab
98
+ with gr.Tab("Analysis"):
99
+ # Use create_analysis_screen to get UI components including visualization container
100
+ analysis_options, analysis_params, run_analysis_btn, analysis_output, bow_top_slider = create_analysis_screen()
101
+
102
+ # Pre-create visualization components (initially hidden)
103
+ visualization_area_visible = gr.Checkbox(value=False, visible=False, label="Visualization Visible")
104
+ analysis_title = gr.Markdown("## Analysis Results", visible=False)
105
+ prompt_title = gr.Markdown(visible=False)
106
+ models_compared = gr.Markdown(visible=False)
107
+
108
+ # Container for model 1 words
109
+ model1_title = gr.Markdown(visible=False)
110
+ model1_words = gr.Markdown(visible=False)
111
+
112
+ # Container for model 2 words
113
+ model2_title = gr.Markdown(visible=False)
114
+ model2_words = gr.Markdown(visible=False)
115
+
116
+ # Similarity metrics
117
+ similarity_metrics_title = gr.Markdown("### Similarity Metrics", visible=False)
118
+ similarity_metrics = gr.Markdown(visible=False)
119
+
120
+ # Status or error message area
121
+ status_message_visible = gr.Checkbox(value=False, visible=False, label="Status Message Visible")
122
+ status_message = gr.Markdown(visible=False)
123
+
124
+ # Define a helper function to extract parameter values and run the analysis
125
+ def run_analysis(dataset, selected_analyses, bow_top, ngram_n, ngram_top):
126
+ try:
127
+ if not dataset or "entries" not in dataset or not dataset["entries"]:
128
+ return (
129
+ {}, # analysis_results_state
130
+ False, # analysis_output visibility
131
+ False, # visualization_area_visible
132
+ gr.update(visible=False), # analysis_title
133
+ gr.update(visible=False), # prompt_title
134
+ gr.update(visible=False), # models_compared
135
+ gr.update(visible=False), # model1_title
136
+ gr.update(visible=False), # model1_words
137
+ gr.update(visible=False), # model2_title
138
+ gr.update(visible=False), # model2_words
139
+ gr.update(visible=False), # similarity_metrics_title
140
+ gr.update(visible=False), # similarity_metrics
141
+ True, # status_message_visible
142
+ gr.update(visible=True, value="❌ **Error:** No dataset loaded. Please create or load a dataset first.") # status_message
143
+ )
144
+
145
+ parameters = {
146
+ "bow_top": bow_top,
147
+ "ngram_n": ngram_n,
148
+ "ngram_top": ngram_top
149
+ }
150
+ print("Running analysis with parameters:", parameters)
151
+
152
+ # Process the analysis request
153
+ analysis_results, _ = process_analysis_request(dataset, selected_analyses, parameters)
154
+
155
+ # If there's an error or no results
156
+ if not analysis_results or "analyses" not in analysis_results or not analysis_results["analyses"]:
157
+ return (
158
+ analysis_results,
159
+ False,
160
+ False,
161
+ gr.update(visible=False),
162
+ gr.update(visible=False),
163
+ gr.update(visible=False),
164
+ gr.update(visible=False),
165
+ gr.update(visible=False),
166
+ gr.update(visible=False),
167
+ gr.update(visible=False),
168
+ gr.update(visible=False),
169
+ gr.update(visible=False),
170
+ True,
171
+ gr.update(visible=True, value="❌ **No results found.** Try different analysis options.")
172
+ )
173
+
174
+ # Extract information to display in components
175
+ prompt = list(analysis_results["analyses"].keys())[0]
176
+ analyses = analysis_results["analyses"][prompt]
177
+
178
+ if "bag_of_words" not in analyses:
179
+ return (
180
+ analysis_results,
181
+ False,
182
+ False,
183
+ gr.update(visible=False),
184
+ gr.update(visible=False),
185
+ gr.update(visible=False),
186
+ gr.update(visible=False),
187
+ gr.update(visible=False),
188
+ gr.update(visible=False),
189
+ gr.update(visible=False),
190
+ gr.update(visible=False),
191
+ gr.update(visible=False),
192
+ True,
193
+ gr.update(visible=True, value="❌ **No Bag of Words analysis found.** Make sure to select it in the options.")
194
+ )
195
+
196
+ bow_results = analyses["bag_of_words"]
197
+ models = bow_results.get("models", [])
198
+
199
+ if len(models) < 2:
200
+ return (
201
+ analysis_results,
202
+ False,
203
+ False,
204
+ gr.update(visible=False),
205
+ gr.update(visible=False),
206
+ gr.update(visible=False),
207
+ gr.update(visible=False),
208
+ gr.update(visible=False),
209
+ gr.update(visible=False),
210
+ gr.update(visible=False),
211
+ gr.update(visible=False),
212
+ gr.update(visible=False),
213
+ True,
214
+ gr.update(visible=True, value="❌ **Not enough models to compare.** Please ensure you have two model responses.")
215
+ )
216
+
217
+ # Extract and format information for display
218
+ model1_name = models[0]
219
+ model2_name = models[1]
220
+
221
+ # Format important words for each model
222
+ important_words = bow_results.get("important_words", {})
223
+ model1_words_text = "No important words found"
224
+ model2_words_text = "No important words found"
225
+
226
+ if model1_name in important_words:
227
+ word_list = [f"**{item['word']}** ({item['count']})" for item in important_words[model1_name][:10]]
228
+ model1_words_text = ", ".join(word_list)
229
+
230
+ if model2_name in important_words:
231
+ word_list = [f"**{item['word']}** ({item['count']})" for item in important_words[model2_name][:10]]
232
+ model2_words_text = ", ".join(word_list)
233
+
234
+ # Format similarity metrics
235
+ similarity_text = "No similarity metrics found"
236
+ comparisons = bow_results.get("comparisons", {})
237
+ comparison_key = f"{model1_name} vs {model2_name}"
238
+
239
+ if comparison_key in comparisons:
240
+ metrics = comparisons[comparison_key]
241
+ cosine = metrics.get("cosine_similarity", 0)
242
+ jaccard = metrics.get("jaccard_similarity", 0)
243
+ semantic = metrics.get("semantic_similarity", 0) # Add semantic similarity
244
+ common_words = metrics.get("common_word_count", 0)
245
+
246
+ similarity_text = f"""
247
+ - **Cosine Similarity**: {cosine:.2f} (higher means more similar word frequency patterns)
248
+ - **Jaccard Similarity**: {jaccard:.2f} (higher means more word overlap)
249
+ - **Semantic Similarity**: {semantic:.2f} (higher means more similar meaning)
250
+ - **Common Words**: {common_words} words appear in both responses
251
+ """
252
+
253
+ # Return all updated component values
254
+ return (
255
+ analysis_results, # analysis_results_state
256
+ False, # analysis_output visibility
257
+ True, # visualization_area_visible
258
+ gr.update(visible=True), # analysis_title
259
+ gr.update(visible=True, value=f"## Analysis of Prompt: \"{prompt[:100]}...\""), # prompt_title
260
+ gr.update(visible=True, value=f"### Comparing responses from {model1_name} and {model2_name}"), # models_compared
261
+ gr.update(visible=True, value=f"#### Top Words Used by {model1_name}"), # model1_title
262
+ gr.update(visible=True, value=model1_words_text), # model1_words
263
+ gr.update(visible=True, value=f"#### Top Words Used by {model2_name}"), # model2_title
264
+ gr.update(visible=True, value=model2_words_text), # model2_words
265
+ gr.update(visible=True), # similarity_metrics_title
266
+ gr.update(visible=True, value=similarity_text), # similarity_metrics
267
+ False, # status_message_visible
268
+ gr.update(visible=False) # status_message
269
+ )
270
+
271
+ except Exception as e:
272
+ import traceback
273
+ error_msg = f"Error in analysis: {str(e)}\n{traceback.format_exc()}"
274
+ print(error_msg)
275
+
276
+ return (
277
+ {"error": error_msg}, # analysis_results_state
278
+ True, # analysis_output visibility (show raw JSON for debugging)
279
+ False, # visualization_area_visible
280
+ gr.update(visible=False),
281
+ gr.update(visible=False),
282
+ gr.update(visible=False),
283
+ gr.update(visible=False),
284
+ gr.update(visible=False),
285
+ gr.update(visible=False),
286
+ gr.update(visible=False),
287
+ gr.update(visible=False),
288
+ gr.update(visible=False),
289
+ True, # status_message_visible
290
+ gr.update(visible=True, value=f"❌ **Error during analysis:**\n\n```\n{str(e)}\n```") # status_message
291
+ )
292
+
293
+ # Function to update visibility based on checkbox state
294
+ def update_visibility(viz_visible, status_visible):
295
+ return [
296
+ gr.update(visible=viz_visible), # analysis_title
297
+ gr.update(visible=viz_visible), # prompt_title
298
+ gr.update(visible=viz_visible), # models_compared
299
+ gr.update(visible=viz_visible), # model1_title
300
+ gr.update(visible=viz_visible), # model1_words
301
+ gr.update(visible=viz_visible), # model2_title
302
+ gr.update(visible=viz_visible), # model2_words
303
+ gr.update(visible=viz_visible), # similarity_metrics_title
304
+ gr.update(visible=viz_visible), # similarity_metrics
305
+ gr.update(visible=status_visible) # status_message
306
+ ]
307
+
308
+ # Connect visibility checkboxes to update function
309
+ visualization_area_visible.change(
310
+ fn=update_visibility,
311
+ inputs=[visualization_area_visible, status_message_visible],
312
+ outputs=[
313
+ analysis_title,
314
+ prompt_title,
315
+ models_compared,
316
+ model1_title,
317
+ model1_words,
318
+ model2_title,
319
+ model2_words,
320
+ similarity_metrics_title,
321
+ similarity_metrics,
322
+ status_message
323
+ ]
324
+ )
325
+
326
+ # Run analysis with proper parameters
327
+ run_analysis_btn.click(
328
+ fn=run_analysis,
329
+ inputs=[dataset_state, analysis_options, bow_top_slider, ngram_n, ngram_top],
330
+ outputs=[
331
+ analysis_results_state,
332
+ analysis_output,
333
+ visualization_area_visible,
334
+ analysis_title,
335
+ prompt_title,
336
+ models_compared,
337
+ model1_title,
338
+ model1_words,
339
+ model2_title,
340
+ model2_words,
341
+ similarity_metrics_title,
342
+ similarity_metrics,
343
+ status_message_visible,
344
+ status_message
345
+ ]
346
+ )
347
+
348
+ return app
349
+
350
+ if __name__ == "__main__":
351
+ # Download required NLTK resources before launching the app
352
+ download_nltk_resources()
353
+
354
+ app = create_app()
355
+ app.launch()
app.py CHANGED
@@ -175,28 +175,137 @@ def create_app():
175
  prompt = list(analysis_results["analyses"].keys())[0]
176
  analyses = analysis_results["analyses"][prompt]
177
 
178
- if "bag_of_words" not in analyses:
179
- return (
180
- analysis_results,
181
- False,
182
- False,
183
- gr.update(visible=False),
184
- gr.update(visible=False),
185
- gr.update(visible=False),
186
- gr.update(visible=False),
187
- gr.update(visible=False),
188
- gr.update(visible=False),
189
- gr.update(visible=False),
190
- gr.update(visible=False),
191
- gr.update(visible=False),
192
- True,
193
- gr.update(visible=True, value="❌ **No Bag of Words analysis found.** Make sure to select it in the options.")
194
- )
 
 
 
195
 
196
- bow_results = analyses["bag_of_words"]
197
- models = bow_results.get("models", [])
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
198
 
199
- if len(models) < 2:
 
200
  return (
201
  analysis_results,
202
  False,
@@ -211,59 +320,23 @@ def create_app():
211
  gr.update(visible=False),
212
  gr.update(visible=False),
213
  True,
214
- gr.update(visible=True, value="❌ **Not enough models to compare.** Please ensure you have two model responses.")
215
  )
216
-
217
- # Extract and format information for display
218
- model1_name = models[0]
219
- model2_name = models[1]
220
-
221
- # Format important words for each model
222
- important_words = bow_results.get("important_words", {})
223
- model1_words_text = "No important words found"
224
- model2_words_text = "No important words found"
225
-
226
- if model1_name in important_words:
227
- word_list = [f"**{item['word']}** ({item['count']})" for item in important_words[model1_name][:10]]
228
- model1_words_text = ", ".join(word_list)
229
-
230
- if model2_name in important_words:
231
- word_list = [f"**{item['word']}** ({item['count']})" for item in important_words[model2_name][:10]]
232
- model2_words_text = ", ".join(word_list)
233
-
234
- # Format similarity metrics
235
- similarity_text = "No similarity metrics found"
236
- comparisons = bow_results.get("comparisons", {})
237
- comparison_key = f"{model1_name} vs {model2_name}"
238
 
239
- if comparison_key in comparisons:
240
- metrics = comparisons[comparison_key]
241
- cosine = metrics.get("cosine_similarity", 0)
242
- jaccard = metrics.get("jaccard_similarity", 0)
243
- semantic = metrics.get("semantic_similarity", 0) # Add semantic similarity
244
- common_words = metrics.get("common_word_count", 0)
245
-
246
- similarity_text = f"""
247
- - **Cosine Similarity**: {cosine:.2f} (higher means more similar word frequency patterns)
248
- - **Jaccard Similarity**: {jaccard:.2f} (higher means more word overlap)
249
- - **Semantic Similarity**: {semantic:.2f} (higher means more similar meaning)
250
- - **Common Words**: {common_words} words appear in both responses
251
- """
252
-
253
  # Return all updated component values
254
  return (
255
  analysis_results, # analysis_results_state
256
  False, # analysis_output visibility
257
  True, # visualization_area_visible
258
  gr.update(visible=True), # analysis_title
259
- gr.update(visible=True, value=f"## Analysis of Prompt: \"{prompt[:100]}...\""), # prompt_title
260
- gr.update(visible=True, value=f"### Comparing responses from {model1_name} and {model2_name}"), # models_compared
261
- gr.update(visible=True, value=f"#### Top Words Used by {model1_name}"), # model1_title
262
- gr.update(visible=True, value=model1_words_text), # model1_words
263
- gr.update(visible=True, value=f"#### Top Words Used by {model2_name}"), # model2_title
264
- gr.update(visible=True, value=model2_words_text), # model2_words
265
- gr.update(visible=True), # similarity_metrics_title
266
- gr.update(visible=True, value=similarity_text), # similarity_metrics
267
  False, # status_message_visible
268
  gr.update(visible=False) # status_message
269
  )
@@ -326,7 +399,7 @@ def create_app():
326
  # Run analysis with proper parameters
327
  run_analysis_btn.click(
328
  fn=run_analysis,
329
- inputs=[dataset_state, analysis_options, bow_top_slider, ngram_n, ngram_top],
330
  outputs=[
331
  analysis_results_state,
332
  analysis_output,
 
175
  prompt = list(analysis_results["analyses"].keys())[0]
176
  analyses = analysis_results["analyses"][prompt]
177
 
178
+ # Initialize visualization components visibilities and contents
179
+ visualization_area_visible = False
180
+ prompt_title_visible = False
181
+ prompt_title_value = ""
182
+ models_compared_visible = False
183
+ models_compared_value = ""
184
+
185
+ model1_title_visible = False
186
+ model1_title_value = ""
187
+ model1_words_visible = False
188
+ model1_words_value = ""
189
+
190
+ model2_title_visible = False
191
+ model2_title_value = ""
192
+ model2_words_visible = False
193
+ model2_words_value = ""
194
+
195
+ similarity_title_visible = False
196
+ similarity_metrics_visible = False
197
+ similarity_metrics_value = ""
198
 
199
+ # Check for Bag of Words analysis
200
+ if "bag_of_words" in analyses:
201
+ visualization_area_visible = True
202
+ bow_results = analyses["bag_of_words"]
203
+ models = bow_results.get("models", [])
204
+
205
+ if len(models) >= 2:
206
+ prompt_title_visible = True
207
+ prompt_title_value = f"## Analysis of Prompt: \"{prompt[:100]}...\""
208
+
209
+ models_compared_visible = True
210
+ models_compared_value = f"### Comparing responses from {models[0]} and {models[1]}"
211
+
212
+ # Extract and format information for display
213
+ model1_name = models[0]
214
+ model2_name = models[1]
215
+
216
+ # Format important words for each model
217
+ important_words = bow_results.get("important_words", {})
218
+
219
+ if model1_name in important_words:
220
+ model1_title_visible = True
221
+ model1_title_value = f"#### Top Words Used by {model1_name}"
222
+
223
+ word_list = [f"**{item['word']}** ({item['count']})" for item in important_words[model1_name][:10]]
224
+ model1_words_visible = True
225
+ model1_words_value = ", ".join(word_list)
226
+
227
+ if model2_name in important_words:
228
+ model2_title_visible = True
229
+ model2_title_value = f"#### Top Words Used by {model2_name}"
230
+
231
+ word_list = [f"**{item['word']}** ({item['count']})" for item in important_words[model2_name][:10]]
232
+ model2_words_visible = True
233
+ model2_words_value = ", ".join(word_list)
234
+
235
+ # Format similarity metrics
236
+ comparisons = bow_results.get("comparisons", {})
237
+ comparison_key = f"{model1_name} vs {model2_name}"
238
+
239
+ if comparison_key in comparisons:
240
+ metrics = comparisons[comparison_key]
241
+ cosine = metrics.get("cosine_similarity", 0)
242
+ jaccard = metrics.get("jaccard_similarity", 0)
243
+ semantic = metrics.get("semantic_similarity", 0)
244
+ common_words = metrics.get("common_word_count", 0)
245
+
246
+ similarity_title_visible = True
247
+ similarity_metrics_visible = True
248
+ similarity_metrics_value = f"""
249
+ - **Cosine Similarity**: {cosine:.2f} (higher means more similar word frequency patterns)
250
+ - **Jaccard Similarity**: {jaccard:.2f} (higher means more word overlap)
251
+ - **Semantic Similarity**: {semantic:.2f} (higher means more similar meaning)
252
+ - **Common Words**: {common_words} words appear in both responses
253
+ """
254
+
255
+ # Check for N-gram analysis (if not found, we'll fallback to BOW)
256
+ if "ngram_analysis" in analyses and not visualization_area_visible:
257
+ visualization_area_visible = True
258
+ ngram_results = analyses["ngram_analysis"]
259
+ models = ngram_results.get("models", [])
260
+ ngram_size = ngram_results.get("ngram_size", 2)
261
+ size_name = "Unigrams" if ngram_size == 1 else f"{ngram_size}-grams"
262
+
263
+ if len(models) >= 2:
264
+ prompt_title_visible = True
265
+ prompt_title_value = f"## Analysis of Prompt: \"{prompt[:100]}...\""
266
+
267
+ models_compared_visible = True
268
+ models_compared_value = f"### {size_name} Analysis: Comparing responses from {models[0]} and {models[1]}"
269
+
270
+ # Extract and format information for display
271
+ model1_name = models[0]
272
+ model2_name = models[1]
273
+
274
+ # Format important n-grams for each model
275
+ important_ngrams = ngram_results.get("important_ngrams", {})
276
+
277
+ if model1_name in important_ngrams:
278
+ model1_title_visible = True
279
+ model1_title_value = f"#### Top {size_name} Used by {model1_name}"
280
+
281
+ ngram_list = [f"**{item['ngram']}** ({item['count']})" for item in important_ngrams[model1_name][:10]]
282
+ model1_words_visible = True
283
+ model1_words_value = ", ".join(ngram_list)
284
+
285
+ if model2_name in important_ngrams:
286
+ model2_title_visible = True
287
+ model2_title_value = f"#### Top {size_name} Used by {model2_name}"
288
+
289
+ ngram_list = [f"**{item['ngram']}** ({item['count']})" for item in important_ngrams[model2_name][:10]]
290
+ model2_words_visible = True
291
+ model2_words_value = ", ".join(ngram_list)
292
+
293
+ # Format similarity metrics if available
294
+ if "comparisons" in ngram_results:
295
+ comparison_key = f"{model1_name} vs {model2_name}"
296
+
297
+ if comparison_key in ngram_results["comparisons"]:
298
+ metrics = ngram_results["comparisons"][comparison_key]
299
+ common_count = metrics.get("common_ngram_count", 0)
300
+
301
+ similarity_title_visible = True
302
+ similarity_metrics_visible = True
303
+ similarity_metrics_value = f"""
304
+ - **Common {size_name}**: {common_count} {size_name.lower()} appear in both responses
305
+ """
306
 
307
+ # If we don't have visualization data from any analysis
308
+ if not visualization_area_visible:
309
  return (
310
  analysis_results,
311
  False,
 
320
  gr.update(visible=False),
321
  gr.update(visible=False),
322
  True,
323
+ gr.update(visible=True, value="❌ **No visualization data found.** Make sure to select at least one analysis type.")
324
  )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
325
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
326
  # Return all updated component values
327
  return (
328
  analysis_results, # analysis_results_state
329
  False, # analysis_output visibility
330
  True, # visualization_area_visible
331
  gr.update(visible=True), # analysis_title
332
+ gr.update(visible=prompt_title_visible, value=prompt_title_value), # prompt_title
333
+ gr.update(visible=models_compared_visible, value=models_compared_value), # models_compared
334
+ gr.update(visible=model1_title_visible, value=model1_title_value), # model1_title
335
+ gr.update(visible=model1_words_visible, value=model1_words_value), # model1_words
336
+ gr.update(visible=model2_title_visible, value=model2_title_value), # model2_title
337
+ gr.update(visible=model2_words_visible, value=model2_words_value), # model2_words
338
+ gr.update(visible=similarity_title_visible), # similarity_metrics_title
339
+ gr.update(visible=similarity_metrics_visible, value=similarity_metrics_value), # similarity_metrics
340
  False, # status_message_visible
341
  gr.update(visible=False) # status_message
342
  )
 
399
  # Run analysis with proper parameters
400
  run_analysis_btn.click(
401
  fn=run_analysis,
402
+ inputs=[dataset_state, analysis_options, bow_top_slider],
403
  outputs=[
404
  analysis_results_state,
405
  analysis_output,
processors/ngram_analysis.py CHANGED
@@ -36,74 +36,93 @@ def compare_ngrams(texts, model_names, n=2, top_n=25):
36
  if not texts or len(texts) < 1:
37
  return result
38
 
39
- # Create n-gram representations using CountVectorizer
40
- vectorizer = CountVectorizer(
41
- ngram_range=(n, n), # Use the specified n-gram size
42
- max_features=1000,
43
- stop_words='english'
44
- )
45
-
46
- X = vectorizer.fit_transform(texts)
47
-
48
- # Get feature names (n-grams)
49
- feature_names = vectorizer.get_feature_names_out()
50
-
51
- # Create n-gram count matrix
52
- ngram_counts = {}
53
- for i, model in enumerate(model_names):
54
- counts = X[i].toarray()[0]
55
- ngram_counts[model] = {}
56
-
57
- # Store n-gram frequencies for this model
58
- for j, ngram in enumerate(feature_names):
59
- if counts[j] > 0: # Only store n-grams that appear
60
- ngram_counts[model][ngram] = int(counts[j])
61
-
62
- # Add to n-gram count matrix
63
- if ngram not in result["ngram_count_matrix"]:
64
- result["ngram_count_matrix"][ngram] = {}
65
- result["ngram_count_matrix"][ngram][model] = int(counts[j])
66
-
67
- # Find important n-grams for each model
68
- for model, ngram_freq in ngram_counts.items():
69
- # Sort by frequency
70
- sorted_ngrams = sorted(ngram_freq.items(), key=lambda x: x[1], reverse=True)
71
-
72
- # Store top N n-grams
73
- result["important_ngrams"][model] = [
74
- {"ngram": ngram, "count": count}
75
- for ngram, count in sorted_ngrams[:top_n]
76
- ]
77
-
78
- # Calculate differential n-grams (n-grams with biggest frequency difference between models)
79
- if len(model_names) >= 2:
80
- model1, model2 = model_names[0], model_names[1]
81
-
82
- # Calculate differences
83
- diff_scores = {}
84
- for ngram in result["ngram_count_matrix"]:
85
- count1 = result["ngram_count_matrix"][ngram].get(model1, 0)
86
- count2 = result["ngram_count_matrix"][ngram].get(model2, 0)
87
-
88
- # Absolute difference
89
- diff_scores[ngram] = abs(count1 - count2)
90
-
91
- # Sort by difference
92
- sorted_diffs = sorted(diff_scores.items(), key=lambda x: x[1], reverse=True)
93
- result["differential_ngrams"] = [ngram for ngram, _ in sorted_diffs[:top_n]]
94
-
95
- # Calculate overlap statistics
96
- model1_ngrams = set(ngram_counts.get(model1, {}).keys())
97
- model2_ngrams = set(ngram_counts.get(model2, {}).keys())
98
- common_ngrams = model1_ngrams.intersection(model2_ngrams)
99
-
100
- # Initialize comparisons if needed
101
- if "comparisons" not in result:
102
- result["comparisons"] = {}
103
-
104
- comparison_key = f"{model1} vs {model2}"
105
- result["comparisons"][comparison_key] = {
106
- "common_ngram_count": len(common_ngrams)
107
- }
 
 
 
 
 
 
 
 
 
108
 
109
- return result
 
 
 
 
 
 
 
 
 
 
 
36
  if not texts or len(texts) < 1:
37
  return result
38
 
39
+ # Convert n to integer if it's a string
40
+ if isinstance(n, str):
41
+ n = int(n)
42
+
43
+ # Convert top_n to integer if necessary
44
+ if isinstance(top_n, str):
45
+ top_n = int(top_n)
46
+
47
+ try:
48
+ # Create n-gram representations using CountVectorizer
49
+ vectorizer = CountVectorizer(
50
+ ngram_range=(n, n), # Use the specified n-gram size
51
+ max_features=1000,
52
+ stop_words='english'
53
+ )
54
+
55
+ X = vectorizer.fit_transform(texts)
56
+
57
+ # Get feature names (n-grams)
58
+ feature_names = vectorizer.get_feature_names_out()
59
+
60
+ # Create n-gram count matrix
61
+ ngram_counts = {}
62
+ for i, model in enumerate(model_names):
63
+ counts = X[i].toarray()[0]
64
+ ngram_counts[model] = {}
65
+
66
+ # Store n-gram frequencies for this model
67
+ for j, ngram in enumerate(feature_names):
68
+ if counts[j] > 0: # Only store n-grams that appear
69
+ ngram_counts[model][ngram] = int(counts[j])
70
+
71
+ # Add to n-gram count matrix
72
+ if ngram not in result["ngram_count_matrix"]:
73
+ result["ngram_count_matrix"][ngram] = {}
74
+ result["ngram_count_matrix"][ngram][model] = int(counts[j])
75
+
76
+ # Find important n-grams for each model
77
+ for model, ngram_freq in ngram_counts.items():
78
+ # Sort by frequency
79
+ sorted_ngrams = sorted(ngram_freq.items(), key=lambda x: x[1], reverse=True)
80
+
81
+ # Store top N n-grams
82
+ result["important_ngrams"][model] = [
83
+ {"ngram": ngram, "count": count}
84
+ for ngram, count in sorted_ngrams[:top_n]
85
+ ]
86
+
87
+ # Calculate differential n-grams (n-grams with biggest frequency difference between models)
88
+ if len(model_names) >= 2:
89
+ model1, model2 = model_names[0], model_names[1]
90
+
91
+ # Calculate differences
92
+ diff_scores = {}
93
+ for ngram in result["ngram_count_matrix"]:
94
+ count1 = result["ngram_count_matrix"][ngram].get(model1, 0)
95
+ count2 = result["ngram_count_matrix"][ngram].get(model2, 0)
96
+
97
+ # Absolute difference
98
+ diff_scores[ngram] = abs(count1 - count2)
99
+
100
+ # Sort by difference
101
+ sorted_diffs = sorted(diff_scores.items(), key=lambda x: x[1], reverse=True)
102
+ result["differential_ngrams"] = [ngram for ngram, _ in sorted_diffs[:top_n]]
103
+
104
+ # Calculate overlap statistics
105
+ model1_ngrams = set(ngram_counts.get(model1, {}).keys())
106
+ model2_ngrams = set(ngram_counts.get(model2, {}).keys())
107
+ common_ngrams = model1_ngrams.intersection(model2_ngrams)
108
+
109
+ # Initialize comparisons if needed
110
+ if "comparisons" not in result:
111
+ result["comparisons"] = {}
112
+
113
+ comparison_key = f"{model1} vs {model2}"
114
+ result["comparisons"][comparison_key] = {
115
+ "common_ngram_count": len(common_ngrams)
116
+ }
117
 
118
+ return result
119
+ except Exception as e:
120
+ import traceback
121
+ error_msg = f"N-gram analysis error: {str(e)}\n{traceback.format_exc()}"
122
+ print(error_msg)
123
+ # Return basic structure with error
124
+ return {
125
+ "models": model_names,
126
+ "ngram_size": n,
127
+ "error": str(e)
128
+ }
ui/analysis_screen.py CHANGED
@@ -5,19 +5,18 @@ from visualization.bow_visualizer import process_and_visualize_analysis
5
  # Import analysis modules
6
  # Uncomment these when implemented
7
  # from processors.topic_modeling import extract_topics, compare_topics
8
- # from processors.ngram_analysis import compare_ngrams
9
  # from processors.bias_detection import compare_bias
10
  from processors.bow_analysis import compare_bow
11
  # from processors.metrics import calculate_similarity
12
  # from processors.diff_highlighter import highlight_differences
13
- from processors.ngram_analysis import compare_ngrams
14
 
15
  def create_analysis_screen():
16
  """
17
  Create the analysis options screen
18
 
19
  Returns:
20
- tuple: (analysis_options, analysis_params, run_analysis_btn, analysis_output, bow_top_slider)
21
  """
22
  with gr.Column() as analysis_screen:
23
  gr.Markdown("## Analysis Options")
@@ -48,7 +47,19 @@ def create_analysis_screen():
48
  elem_id="bow_top_slider"
49
  )
50
 
51
- # Parameters for each analysis type (these will be hidden/shown based on selections)
 
 
 
 
 
 
 
 
 
 
 
 
52
  with gr.Group() as analysis_params:
53
  # Topic modeling parameters
54
  with gr.Group(visible=False) as topic_params:
@@ -56,14 +67,11 @@ def create_analysis_screen():
56
  topic_count = gr.Slider(minimum=2, maximum=10, value=3, step=1,
57
  label="Number of Topics")
58
 
59
- # N-gram parameters
60
  with gr.Group(visible=False) as ngram_params:
61
  gr.Markdown("### N-gram Parameters")
62
- ngram_n = gr.Radio(choices=["1", "2", "3"], value="2",
63
- label="N-gram Size")
64
- ngram_top = gr.Slider(minimum=5, maximum=30, value=10, step=1,
65
- label="Top N-grams to Display")
66
-
67
  # Bias detection parameters
68
  with gr.Group(visible=False) as bias_params:
69
  gr.Markdown("### Bias Detection Parameters")
@@ -84,18 +92,21 @@ def create_analysis_screen():
84
 
85
  # Function to update parameter visibility based on selected analyses
86
  def update_params_visibility(selected):
 
87
  return {
88
  topic_params: gr.update(visible="Topic Modeling" in selected),
89
- ngram_params: gr.update(visible="N-gram Analysis" in selected),
90
  bias_params: gr.update(visible="Bias Detection" in selected),
91
- similarity_params: gr.update(visible="Similarity Metrics" in selected)
 
 
92
  }
93
 
94
  # Set up event handler for analysis selection
95
  analysis_options.change(
96
  fn=update_params_visibility,
97
  inputs=[analysis_options],
98
- outputs=[topic_params, ngram_params, bias_params, similarity_params]
99
  )
100
 
101
  # Run analysis button
@@ -104,11 +115,10 @@ def create_analysis_screen():
104
  # Analysis output area - hidden JSON component to store raw results
105
  analysis_output = gr.JSON(label="Analysis Results", visible=False)
106
 
107
- # Return the bow_top_slider directly so app.py can access it
108
- # Note: Removed the visualization_container from return values since we'll pre-create it
109
  return analysis_options, analysis_params, run_analysis_btn, analysis_output, bow_top_slider, ngram_n, ngram_top
110
 
111
- # function
112
  def process_analysis_request(dataset, selected_analyses, parameters):
113
  """
114
  Process the analysis request and run selected analyses
 
5
  # Import analysis modules
6
  # Uncomment these when implemented
7
  # from processors.topic_modeling import extract_topics, compare_topics
8
+ from processors.ngram_analysis import compare_ngrams
9
  # from processors.bias_detection import compare_bias
10
  from processors.bow_analysis import compare_bow
11
  # from processors.metrics import calculate_similarity
12
  # from processors.diff_highlighter import highlight_differences
 
13
 
14
  def create_analysis_screen():
15
  """
16
  Create the analysis options screen
17
 
18
  Returns:
19
+ tuple: (analysis_options, analysis_params, run_analysis_btn, analysis_output, bow_top_slider, ngram_n, ngram_top)
20
  """
21
  with gr.Column() as analysis_screen:
22
  gr.Markdown("## Analysis Options")
 
47
  elem_id="bow_top_slider"
48
  )
49
 
50
+ # Create N-gram parameters accessible at top level
51
+ ngram_n = gr.Radio(
52
+ choices=["1", "2", "3"], value="2",
53
+ label="N-gram Size",
54
+ visible=False
55
+ )
56
+ ngram_top = gr.Slider(
57
+ minimum=5, maximum=30, value=10, step=1,
58
+ label="Top N-grams to Display",
59
+ visible=False
60
+ )
61
+
62
+ # Parameters for each analysis type
63
  with gr.Group() as analysis_params:
64
  # Topic modeling parameters
65
  with gr.Group(visible=False) as topic_params:
 
67
  topic_count = gr.Slider(minimum=2, maximum=10, value=3, step=1,
68
  label="Number of Topics")
69
 
70
+ # N-gram parameters group (using external ngram_n and ngram_top)
71
  with gr.Group(visible=False) as ngram_params:
72
  gr.Markdown("### N-gram Parameters")
73
+ # We're already using ngram_n and ngram_top defined above
74
+
 
 
 
75
  # Bias detection parameters
76
  with gr.Group(visible=False) as bias_params:
77
  gr.Markdown("### Bias Detection Parameters")
 
92
 
93
  # Function to update parameter visibility based on selected analyses
94
  def update_params_visibility(selected):
95
+ ngram_visible = "N-gram Analysis" in selected
96
  return {
97
  topic_params: gr.update(visible="Topic Modeling" in selected),
98
+ ngram_params: gr.update(visible=ngram_visible),
99
  bias_params: gr.update(visible="Bias Detection" in selected),
100
+ similarity_params: gr.update(visible="Similarity Metrics" in selected),
101
+ ngram_n: gr.update(visible=ngram_visible),
102
+ ngram_top: gr.update(visible=ngram_visible)
103
  }
104
 
105
  # Set up event handler for analysis selection
106
  analysis_options.change(
107
  fn=update_params_visibility,
108
  inputs=[analysis_options],
109
+ outputs=[topic_params, ngram_params, bias_params, similarity_params, ngram_n, ngram_top]
110
  )
111
 
112
  # Run analysis button
 
115
  # Analysis output area - hidden JSON component to store raw results
116
  analysis_output = gr.JSON(label="Analysis Results", visible=False)
117
 
118
+ # Return the components needed by app.py
 
119
  return analysis_options, analysis_params, run_analysis_btn, analysis_output, bow_top_slider, ngram_n, ngram_top
120
 
121
+ # Process analysis request function
122
  def process_analysis_request(dataset, selected_analyses, parameters):
123
  """
124
  Process the analysis request and run selected analyses
visualization/ngram_visualizer.py CHANGED
@@ -7,7 +7,31 @@ from plotly.subplots import make_subplots
7
 
8
 
9
  def create_ngram_visualization(analysis_results):
 
 
 
 
 
 
 
 
10
  """
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
11
  Create visualizations for n-gram analysis results
12
 
13
  Args:
@@ -27,6 +51,11 @@ def create_ngram_visualization(analysis_results):
27
  # Process N-gram analysis if available
28
  if "ngram_analysis" in analyses:
29
  ngram_results = analyses["ngram_analysis"]
 
 
 
 
 
30
 
31
  # Show models being compared
32
  models = ngram_results.get("models", [])
@@ -40,25 +69,38 @@ def create_ngram_visualization(analysis_results):
40
  # Get important n-grams for each model
41
  important_ngrams = ngram_results.get("important_ngrams", {})
42
 
43
- # Prepare data for plotting important n-grams
44
  if important_ngrams:
45
  for model_name, ngrams in important_ngrams.items():
46
- df = pd.DataFrame(ngrams)
47
-
48
- # Create bar chart for top n-grams
49
- fig = px.bar(df, x='ngram', y='count',
50
- title=f"Top {size_name} Used by {model_name}",
51
- labels={'ngram': 'N-gram', 'count': 'Frequency'},
52
- height=400)
53
-
54
- # Improve layout
55
- fig.update_layout(
56
- xaxis_title="N-gram",
57
- yaxis_title="Frequency",
58
- xaxis={'categoryorder': 'total descending'}
59
- )
60
-
61
- output_components.append(gr.Plot(value=fig))
 
 
 
 
 
 
 
 
 
 
 
 
 
62
 
63
  # Visualize differential n-grams (n-grams with biggest frequency difference)
64
  diff_ngrams = ngram_results.get("differential_ngrams", [])
@@ -71,42 +113,17 @@ def create_ngram_visualization(analysis_results):
71
  model1, model2 = models[0], models[1]
72
  diff_data = []
73
 
74
- for ngram in diff_ngrams[:15]: # Limit to top 15 for readability
75
  if ngram in ngram_matrix:
76
  counts = ngram_matrix[ngram]
77
- diff_data.append({
78
- "ngram": ngram,
79
- model1: counts.get(model1, 0),
80
- model2: counts.get(model2, 0)
81
- })
82
-
83
- if diff_data:
84
- diff_df = pd.DataFrame(diff_data)
85
-
86
- # Create grouped bar chart
87
- fig = go.Figure()
88
- fig.add_trace(go.Bar(
89
- x=diff_df['ngram'],
90
- y=diff_df[model1],
91
- name=model1,
92
- marker_color='indianred'
93
- ))
94
- fig.add_trace(go.Bar(
95
- x=diff_df['ngram'],
96
- y=diff_df[model2],
97
- name=model2,
98
- marker_color='lightsalmon'
99
- ))
100
-
101
- fig.update_layout(
102
- title=f"{size_name} Frequency Comparison",
103
- xaxis_title="N-gram",
104
- yaxis_title="Frequency",
105
- barmode='group',
106
- height=500
107
- )
108
-
109
- output_components.append(gr.Plot(value=fig))
110
 
111
  # Add similarity comparison if available
112
  if "comparisons" in ngram_results:
@@ -123,28 +140,4 @@ def create_ngram_visualization(analysis_results):
123
 
124
  output_components.append(gr.Markdown(metrics_text))
125
 
126
- # If no components were added other than header, show a message
127
- if len(output_components) <= 1:
128
- output_components.append(gr.Markdown(f"No detailed N-gram analysis found in results."))
129
-
130
- return output_components
131
-
132
-
133
- def process_and_visualize_ngram_analysis(analysis_results):
134
- """
135
- Process the n-gram analysis results and create visualization components
136
-
137
- Args:
138
- analysis_results (dict): The analysis results
139
-
140
- Returns:
141
- list: List of gradio components for visualization
142
- """
143
- try:
144
- print(f"Starting visualization of n-gram analysis results")
145
- return create_ngram_visualization(analysis_results)
146
- except Exception as e:
147
- import traceback
148
- error_msg = f"N-gram visualization error: {str(e)}\n{traceback.format_exc()}"
149
- print(error_msg)
150
- return [gr.Markdown(f"**Error during n-gram visualization:**\n\n```\n{error_msg}\n```")]
 
7
 
8
 
9
  def create_ngram_visualization(analysis_results):
10
+ # If no components were added other than header, show a message
11
+ if len(output_components) <= 1:
12
+ output_components.append(gr.Markdown(f"No detailed N-gram analysis found in results."))
13
+
14
+ return output_components
15
+
16
+
17
+ def process_and_visualize_ngram_analysis(analysis_results):
18
  """
19
+ Process the n-gram analysis results and create visualization components
20
+
21
+ Args:
22
+ analysis_results (dict): The analysis results
23
+
24
+ Returns:
25
+ list: List of gradio components for visualization
26
+ """
27
+ try:
28
+ print(f"Starting visualization of n-gram analysis results")
29
+ return create_ngram_visualization(analysis_results)
30
+ except Exception as e:
31
+ import traceback
32
+ error_msg = f"N-gram visualization error: {str(e)}\n{traceback.format_exc()}"
33
+ print(error_msg)
34
+ return [gr.Markdown(f"**Error during n-gram visualization:**\n\n```\n{error_msg}\n```")]"""
35
  Create visualizations for n-gram analysis results
36
 
37
  Args:
 
51
  # Process N-gram analysis if available
52
  if "ngram_analysis" in analyses:
53
  ngram_results = analyses["ngram_analysis"]
54
+
55
+ # Check if there's an error in the analysis
56
+ if "error" in ngram_results:
57
+ output_components.append(gr.Markdown(f"**Error in N-gram analysis:** {ngram_results['error']}"))
58
+ continue
59
 
60
  # Show models being compared
61
  models = ngram_results.get("models", [])
 
69
  # Get important n-grams for each model
70
  important_ngrams = ngram_results.get("important_ngrams", {})
71
 
72
+ # Display important n-grams for each model
73
  if important_ngrams:
74
  for model_name, ngrams in important_ngrams.items():
75
+ output_components.append(gr.Markdown(f"#### Top {size_name} Used by {model_name}"))
76
+
77
+ if ngrams:
78
+ # Create a formatted list of n-grams for display
79
+ ngram_list = [f"**{item['ngram']}** ({item['count']})" for item in ngrams[:10]]
80
+ output_components.append(gr.Markdown(", ".join(ngram_list)))
81
+ else:
82
+ output_components.append(gr.Markdown("No significant n-grams found."))
83
+
84
+ # Only if we have enough data, create a bar chart
85
+ if len(ngrams) >= 3:
86
+ try:
87
+ df = pd.DataFrame(ngrams)
88
+ # Create bar chart for top n-grams
89
+ fig = px.bar(df[:10], x='ngram', y='count',
90
+ title=f"Top {size_name} Used by {model_name}",
91
+ labels={'ngram': 'N-gram', 'count': 'Frequency'},
92
+ height=400)
93
+
94
+ # Improve layout
95
+ fig.update_layout(
96
+ xaxis_title="N-gram",
97
+ yaxis_title="Frequency",
98
+ xaxis={'categoryorder': 'total descending'}
99
+ )
100
+
101
+ output_components.append(gr.Plot(value=fig))
102
+ except Exception as e:
103
+ output_components.append(gr.Markdown(f"Visualization error: {str(e)}"))
104
 
105
  # Visualize differential n-grams (n-grams with biggest frequency difference)
106
  diff_ngrams = ngram_results.get("differential_ngrams", [])
 
113
  model1, model2 = models[0], models[1]
114
  diff_data = []
115
 
116
+ for ngram in diff_ngrams[:10]: # Limit to top 10 for readability
117
  if ngram in ngram_matrix:
118
  counts = ngram_matrix[ngram]
119
+ model1_count = counts.get(model1, 0)
120
+ model2_count = counts.get(model2, 0)
121
+
122
+ # Only include if there's a meaningful difference
123
+ if abs(model1_count - model2_count) > 0:
124
+ output_components.append(gr.Markdown(
125
+ f"- **{ngram}**: {model1}: {model1_count}, {model2}: {model2_count}"
126
+ ))
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
127
 
128
  # Add similarity comparison if available
129
  if "comparisons" in ngram_results:
 
140
 
141
  output_components.append(gr.Markdown(metrics_text))
142
 
143
+