Ryan commited on
Commit
c435293
·
1 Parent(s): a800293
Files changed (3) hide show
  1. app.py +441 -395
  2. processors/text_classifiers.py +152 -0
  3. ui/analysis_screen.py +130 -168
app.py CHANGED
@@ -63,346 +63,408 @@ def create_app():
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, ngram_n, ngram_top, topic_count = 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_analysis, bow_top, ngram_n, ngram_top, topic_count):
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
- "topic_count": topic_count
150
- }
151
- print(f"Running analysis with selected type: {selected_analysis}")
152
- print("Parameters:", parameters)
153
-
154
- # Process the analysis request - note we're now passing selected_analysis as a string, not a list
155
- analysis_results, _ = process_analysis_request(dataset, selected_analysis, parameters)
156
-
157
- # If there's an error or no results
158
- if not analysis_results or "analyses" not in analysis_results or not analysis_results["analyses"]:
159
- return (
160
- analysis_results,
161
- False,
162
- 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
- gr.update(visible=False),
171
- gr.update(visible=False),
172
- True,
173
- gr.update(visible=True, value="❌ **No results found.** Try a different analysis option.")
174
- )
175
-
176
- # Extract information to display in components
177
- prompt = list(analysis_results["analyses"].keys())[0]
178
- analyses = analysis_results["analyses"][prompt]
179
-
180
- # Initialize visualization components visibilities and contents
181
- visualization_area_visible = False
182
- prompt_title_visible = False
183
- prompt_title_value = ""
184
- models_compared_visible = False
185
- models_compared_value = ""
186
-
187
- model1_title_visible = False
188
- model1_title_value = ""
189
- model1_words_visible = False
190
- model1_words_value = ""
191
-
192
- model2_title_visible = False
193
- model2_title_value = ""
194
- model2_words_visible = False
195
- model2_words_value = ""
196
-
197
- similarity_title_visible = False
198
- similarity_metrics_visible = False
199
- similarity_metrics_value = ""
200
-
201
- # Check for messages from placeholder analyses
202
- if "message" in analyses:
203
- return (
204
- analysis_results,
205
- False,
206
- 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
- gr.update(visible=False),
214
- gr.update(visible=False),
215
- gr.update(visible=False),
216
- True,
217
- gr.update(visible=True, value=f"ℹ️ **{analyses['message']}**")
218
- )
219
-
220
- # Check for Bag of Words analysis
221
- if "bag_of_words" in analyses:
222
- visualization_area_visible = True
223
- bow_results = analyses["bag_of_words"]
224
- models = bow_results.get("models", [])
225
 
226
- if len(models) >= 2:
227
- prompt_title_visible = True
228
- prompt_title_value = f"## Analysis of Prompt: \"{prompt[:100]}...\""
229
-
230
- models_compared_visible = True
231
- models_compared_value = f"### Comparing responses from {models[0]} and {models[1]}"
232
-
233
- # Extract and format information for display
234
- model1_name = models[0]
235
- model2_name = models[1]
236
-
237
- # Format important words for each model
238
- important_words = bow_results.get("important_words", {})
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
239
 
240
- if model1_name in important_words:
241
- model1_title_visible = True
242
- model1_title_value = f"#### Top Words Used by {model1_name}"
243
 
244
- word_list = [f"**{item['word']}** ({item['count']})" for item in important_words[model1_name][:10]]
245
- model1_words_visible = True
246
- model1_words_value = ", ".join(word_list)
247
-
248
- if model2_name in important_words:
249
- model2_title_visible = True
250
- model2_title_value = f"#### Top Words Used by {model2_name}"
251
 
252
- word_list = [f"**{item['word']}** ({item['count']})" for item in important_words[model2_name][:10]]
253
- model2_words_visible = True
254
- model2_words_value = ", ".join(word_list)
255
-
256
- # Format similarity metrics
257
- comparisons = bow_results.get("comparisons", {})
258
- comparison_key = f"{model1_name} vs {model2_name}"
259
-
260
- if comparison_key in comparisons:
261
- metrics = comparisons[comparison_key]
262
- cosine = metrics.get("cosine_similarity", 0)
263
- jaccard = metrics.get("jaccard_similarity", 0)
264
- semantic = metrics.get("semantic_similarity", 0)
265
- common_words = metrics.get("common_word_count", 0)
266
 
267
- similarity_title_visible = True
268
- similarity_metrics_visible = True
269
- similarity_metrics_value = f"""
270
- - **Cosine Similarity**: {cosine:.2f} (higher means more similar word frequency patterns)
271
- - **Jaccard Similarity**: {jaccard:.2f} (higher means more word overlap)
272
- - **Semantic Similarity**: {semantic:.2f} (higher means more similar meaning)
273
- - **Common Words**: {common_words} words appear in both responses
274
- """
275
 
276
- # Check for N-gram analysis
277
- if "ngram_analysis" in analyses:
278
- visualization_area_visible = True
279
- ngram_results = analyses["ngram_analysis"]
280
- models = ngram_results.get("models", [])
281
- ngram_size = ngram_results.get("ngram_size", 2)
282
- size_name = "Unigrams" if ngram_size == 1 else f"{ngram_size}-grams"
283
-
284
- if len(models) >= 2:
285
- prompt_title_visible = True
286
- prompt_title_value = f"## Analysis of Prompt: \"{prompt[:100]}...\""
287
-
288
- models_compared_visible = True
289
- models_compared_value = f"### {size_name} Analysis: Comparing responses from {models[0]} and {models[1]}"
290
-
291
- # Extract and format information for display
292
- model1_name = models[0]
293
- model2_name = models[1]
294
-
295
- # Format important n-grams for each model
296
- important_ngrams = ngram_results.get("important_ngrams", {})
297
-
298
- if model1_name in important_ngrams:
299
- model1_title_visible = True
300
- model1_title_value = f"#### Top {size_name} Used by {model1_name}"
301
 
302
- ngram_list = [f"**{item['ngram']}** ({item['count']})" for item in important_ngrams[model1_name][:10]]
303
- model1_words_visible = True
304
- model1_words_value = ", ".join(ngram_list)
305
-
306
- if model2_name in important_ngrams:
307
- model2_title_visible = True
308
- model2_title_value = f"#### Top {size_name} Used by {model2_name}"
309
 
310
- ngram_list = [f"**{item['ngram']}** ({item['count']})" for item in important_ngrams[model2_name][:10]]
311
- model2_words_visible = True
312
- model2_words_value = ", ".join(ngram_list)
313
-
314
- # Format similarity metrics if available
315
- if "comparisons" in ngram_results:
316
  comparison_key = f"{model1_name} vs {model2_name}"
317
-
318
- if comparison_key in ngram_results["comparisons"]:
319
- metrics = ngram_results["comparisons"][comparison_key]
320
- common_count = metrics.get("common_ngram_count", 0)
 
 
 
321
 
322
  similarity_title_visible = True
323
  similarity_metrics_visible = True
324
  similarity_metrics_value = f"""
325
- - **Common {size_name}**: {common_count} {size_name.lower()} appear in both responses
 
 
 
326
  """
327
-
328
- # Check for Topic Modeling analysis
329
- if "topic_modeling" in analyses:
330
- visualization_area_visible = True
331
- topic_results = analyses["topic_modeling"]
332
- models = topic_results.get("models", [])
333
- method = topic_results.get("method", "lda").upper()
334
- n_topics = topic_results.get("n_topics", 3)
335
-
336
- if len(models) >= 2:
337
- prompt_title_visible = True
338
- prompt_title_value = f"## Analysis of Prompt: \"{prompt[:100]}...\""
339
-
340
- models_compared_visible = True
341
- models_compared_value = f"### Topic Modeling Analysis ({method}, {n_topics} topics)"
342
-
343
- # Extract and format topic information
344
- topics = topic_results.get("topics", [])
345
-
346
- if topics:
347
- # Format topic info for display
348
- topic_info = []
349
- for topic in topics[:3]: # Show first 3 topics
350
- topic_id = topic.get("id", 0)
351
- words = topic.get("words", [])[:5] # Top 5 words per topic
352
 
353
- if words:
354
- topic_info.append(f"**Topic {topic_id+1}**: {', '.join(words)}")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
355
 
356
- if topic_info:
 
 
 
357
  model1_title_visible = True
358
- model1_title_value = "#### Discovered Topics"
 
 
359
  model1_words_visible = True
360
- model1_words_value = "\n".join(topic_info)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
361
 
362
- # Get topic distributions for models
363
- model_topics = topic_results.get("model_topics", {})
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
364
 
365
- if model_topics:
 
 
 
 
 
 
 
366
  model1_name = models[0]
367
  model2_name = models[1]
368
 
369
- # Format topic distribution info
370
- if model1_name in model_topics and model2_name in model_topics:
371
- model2_title_visible = True
372
- model2_title_value = "#### Topic Distribution"
373
- model2_words_visible = True
 
 
374
 
375
- # Simple distribution display
376
- dist1 = model_topics[model1_name]
377
- dist2 = model_topics[model2_name]
378
 
379
- model2_words_value = f"""
380
- **{model1_name}**: {', '.join([f"Topic {i+1}: {v:.2f}" for i, v in enumerate(dist1[:3])])}
 
 
 
381
 
382
- **{model2_name}**: {', '.join([f"Topic {i+1}: {v:.2f}" for i, v in enumerate(dist2[:3])])}
 
 
 
383
  """
384
 
385
- # Add similarity metrics if available
386
- comparisons = topic_results.get("comparisons", {})
387
- if comparisons:
388
- comparison_key = f"{model1_name} vs {model2_name}"
389
-
390
- if comparison_key in comparisons:
391
- metrics = comparisons[comparison_key]
392
- js_div = metrics.get("js_divergence", 0)
393
 
394
- similarity_title_visible = True
395
- similarity_metrics_visible = True
396
- similarity_metrics_value = f"""
397
- - **Topic Distribution Divergence**: {js_div:.4f} (lower means more similar topic distributions)
398
- """
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
399
 
400
- # If we don't have visualization data from any analysis
401
- if not visualization_area_visible:
 
 
 
402
  return (
403
- analysis_results,
404
- False,
405
- False,
406
  gr.update(visible=False),
407
  gr.update(visible=False),
408
  gr.update(visible=False),
@@ -412,103 +474,87 @@ def create_app():
412
  gr.update(visible=False),
413
  gr.update(visible=False),
414
  gr.update(visible=False),
415
- True,
416
- gr.update(visible=True, value="❌ **No visualization data found.** Make sure to select a valid analysis option.")
417
  )
418
-
419
- # Return all updated component values
420
- return (
421
- analysis_results, # analysis_results_state
422
- False, # analysis_output visibility
423
- True, # visualization_area_visible
424
- gr.update(visible=True), # analysis_title
425
- gr.update(visible=prompt_title_visible, value=prompt_title_value), # prompt_title
426
- gr.update(visible=models_compared_visible, value=models_compared_value), # models_compared
427
- gr.update(visible=model1_title_visible, value=model1_title_value), # model1_title
428
- gr.update(visible=model1_words_visible, value=model1_words_value), # model1_words
429
- gr.update(visible=model2_title_visible, value=model2_title_value), # model2_title
430
- gr.update(visible=model2_words_visible, value=model2_words_value), # model2_words
431
- gr.update(visible=similarity_title_visible), # similarity_metrics_title
432
- gr.update(visible=similarity_metrics_visible, value=similarity_metrics_value), # similarity_metrics
433
- False, # status_message_visible
434
- gr.update(visible=False) # status_message
435
  )
 
 
 
 
 
 
 
 
436
 
437
- except Exception as e:
438
- import traceback
439
- error_msg = f"Error in analysis: {str(e)}\n{traceback.format_exc()}"
440
- print(error_msg)
 
 
441
 
 
 
 
 
 
 
 
442
  return (
443
- {"error": error_msg}, # analysis_results_state
444
- True, # analysis_output visibility (show raw JSON for debugging)
445
- False, # visualization_area_visible
446
- gr.update(visible=False),
447
- gr.update(visible=False),
448
- gr.update(visible=False),
449
- gr.update(visible=False),
450
- gr.update(visible=False),
451
- gr.update(visible=False),
452
- gr.update(visible=False),
453
- gr.update(visible=False),
454
- gr.update(visible=False),
455
- True, # status_message_visible
456
- gr.update(visible=True, value=f"❌ **Error during analysis:**\n\n```\n{str(e)}\n```") # status_message
457
  )
 
 
 
 
 
 
 
 
 
 
 
 
 
458
 
 
459
  def update_visibility(viz_visible, status_visible):
460
- return [
461
- gr.update(visible=viz_visible), # analysis_title
462
- gr.update(visible=viz_visible), # prompt_title
463
- gr.update(visible=viz_visible), # models_compared
464
- gr.update(visible=viz_visible), # model1_title
465
- gr.update(visible=viz_visible), # model1_words
466
- gr.update(visible=viz_visible), # model2_title
467
- gr.update(visible=viz_visible), # model2_words
468
- gr.update(visible=viz_visible), # similarity_metrics_title
469
- gr.update(visible=viz_visible), # similarity_metrics
470
- gr.update(visible=status_visible) # status_message
471
- ]
472
-
473
- # Connect visibility checkboxes to update function
474
- visualization_area_visible.change(
475
- fn=update_visibility,
476
- inputs=[visualization_area_visible, status_message_visible],
477
- outputs=[
478
- analysis_title,
479
- prompt_title,
480
- models_compared,
481
- model1_title,
482
- model1_words,
483
- model2_title,
484
- model2_words,
485
- similarity_metrics_title,
486
- similarity_metrics,
487
- status_message
488
- ]
489
- )
490
 
491
- # Run analysis with proper parameters - update to include topic_count
492
- run_analysis_btn.click(
493
- fn=run_analysis,
494
- inputs=[dataset_state, analysis_options, bow_top_slider, ngram_n, ngram_top, topic_count],
495
- outputs=[
496
- analysis_results_state,
497
- analysis_output,
498
- visualization_area_visible,
499
- analysis_title,
500
- prompt_title,
501
- models_compared,
502
- model1_title,
503
- model1_words,
504
- model2_title,
505
- model2_words,
506
- similarity_metrics_title,
507
- similarity_metrics,
508
- status_message_visible,
509
- status_message
510
- ]
511
- )
512
 
513
  return app
514
 
 
63
 
64
  # Dataset Input Tab
65
  with gr.Tab("Dataset Input"):
66
+ # ...existing code...
67
+
68
+ # Analysis Tab
69
+ with gr.Tab("Analysis"):
70
+ # Use create_analysis_screen to get UI components including visualization container
71
+ analysis_options, analysis_params, run_analysis_btn, analysis_output, bow_top_slider, ngram_n, ngram_top, topic_count = create_analysis_screen()
 
 
 
 
 
 
 
 
 
 
72
 
73
+ # Pre-create visualization components (initially hidden)
74
+ visualization_area_visible = gr.Checkbox(value=False, visible=False, label="Visualization Visible")
75
+ analysis_title = gr.Markdown("## Analysis Results", visible=False)
76
+ prompt_title = gr.Markdown(visible=False)
77
+ models_compared = gr.Markdown(visible=False)
 
 
78
 
79
+ # Container for model 1 words
80
+ model1_title = gr.Markdown(visible=False)
81
+ model1_words = gr.Markdown(visible=False)
82
+
83
+ # Container for model 2 words
84
+ model2_title = gr.Markdown(visible=False)
85
+ model2_words = gr.Markdown(visible=False)
86
+
87
+ # Similarity metrics
88
+ similarity_metrics_title = gr.Markdown("### Similarity Metrics", visible=False)
89
+ similarity_metrics = gr.Markdown(visible=False)
90
+
91
+ # Status or error message area
92
+ status_message_visible = gr.Checkbox(value=False, visible=False, label="Status Message Visible")
93
+ status_message = gr.Markdown(visible=False)
94
+
95
+ # Define a helper function to extract parameter values and run the analysis
96
+ def run_analysis(dataset, selected_analysis, bow_top, ngram_n, ngram_top, topic_count):
97
+ try:
98
+ if not dataset or "entries" not in dataset or not dataset["entries"]:
99
+ return (
100
+ {}, # analysis_results_state
101
+ False, # analysis_output visibility
102
+ False, # visualization_area_visible
103
+ gr.update(visible=False), # analysis_title
104
+ gr.update(visible=False), # prompt_title
105
+ gr.update(visible=False), # models_compared
106
+ gr.update(visible=False), # model1_title
107
+ gr.update(visible=False), # model1_words
108
+ gr.update(visible=False), # model2_title
109
+ gr.update(visible=False), # model2_words
110
+ gr.update(visible=False), # similarity_metrics_title
111
+ gr.update(visible=False), # similarity_metrics
112
+ True, # status_message_visible
113
+ gr.update(visible=True, value="❌ **Error:** No dataset loaded. Please create or load a dataset first.") # status_message
114
+ )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
115
 
116
+ parameters = {
117
+ "bow_top": bow_top,
118
+ "ngram_n": ngram_n,
119
+ "ngram_top": ngram_top,
120
+ "topic_count": topic_count
121
+ }
122
+ print(f"Running analysis with selected type: {selected_analysis}")
123
+ print("Parameters:", parameters)
124
+
125
+ # Process the analysis request - passing selected_analysis as a string
126
+ analysis_results, _ = process_analysis_request(dataset, selected_analysis, parameters)
127
+
128
+ # If there's an error or no results
129
+ if not analysis_results or "analyses" not in analysis_results or not analysis_results["analyses"]:
130
+ return (
131
+ analysis_results,
132
+ False,
133
+ False,
134
+ gr.update(visible=False),
135
+ gr.update(visible=False),
136
+ gr.update(visible=False),
137
+ gr.update(visible=False),
138
+ gr.update(visible=False),
139
+ gr.update(visible=False),
140
+ gr.update(visible=False),
141
+ gr.update(visible=False),
142
+ gr.update(visible=False),
143
+ True,
144
+ gr.update(visible=True, value="❌ **No results found.** Try a different analysis option.")
145
+ )
146
+
147
+ # Extract information to display in components
148
+ prompt = list(analysis_results["analyses"].keys())[0]
149
+ analyses = analysis_results["analyses"][prompt]
150
+
151
+ # Initialize visualization components visibilities and contents
152
+ visualization_area_visible = False
153
+ prompt_title_visible = False
154
+ prompt_title_value = ""
155
+ models_compared_visible = False
156
+ models_compared_value = ""
157
+
158
+ model1_title_visible = False
159
+ model1_title_value = ""
160
+ model1_words_visible = False
161
+ model1_words_value = ""
162
+
163
+ model2_title_visible = False
164
+ model2_title_value = ""
165
+ model2_words_visible = False
166
+ model2_words_value = ""
167
+
168
+ similarity_title_visible = False
169
+ similarity_metrics_visible = False
170
+ similarity_metrics_value = ""
171
+
172
+ # Check for messages from placeholder analyses
173
+ if "message" in analyses:
174
+ return (
175
+ analysis_results,
176
+ False,
177
+ False,
178
+ gr.update(visible=False),
179
+ gr.update(visible=False),
180
+ gr.update(visible=False),
181
+ gr.update(visible=False),
182
+ gr.update(visible=False),
183
+ gr.update(visible=False),
184
+ gr.update(visible=False),
185
+ gr.update(visible=False),
186
+ gr.update(visible=False),
187
+ True,
188
+ gr.update(visible=True, value=f"ℹ️ **{analyses['message']}**")
189
+ )
190
+
191
+ # Process based on the selected analysis type
192
+ if selected_analysis == "Bag of Words" and "bag_of_words" in analyses:
193
+ visualization_area_visible = True
194
+ bow_results = analyses["bag_of_words"]
195
+ models = bow_results.get("models", [])
196
 
197
+ if len(models) >= 2:
198
+ prompt_title_visible = True
199
+ prompt_title_value = f"## Analysis of Prompt: \"{prompt[:100]}...\""
200
 
201
+ models_compared_visible = True
202
+ models_compared_value = f"### Comparing responses from {models[0]} and {models[1]}"
 
 
 
 
 
203
 
204
+ # Extract and format information for display
205
+ model1_name = models[0]
206
+ model2_name = models[1]
 
 
 
 
 
 
 
 
 
 
 
207
 
208
+ # Format important words for each model
209
+ important_words = bow_results.get("important_words", {})
 
 
 
 
 
 
210
 
211
+ if model1_name in important_words:
212
+ model1_title_visible = True
213
+ model1_title_value = f"#### Top Words Used by {model1_name}"
214
+
215
+ word_list = [f"**{item['word']}** ({item['count']})" for item in important_words[model1_name][:10]]
216
+ model1_words_visible = True
217
+ model1_words_value = ", ".join(word_list)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
218
 
219
+ if model2_name in important_words:
220
+ model2_title_visible = True
221
+ model2_title_value = f"#### Top Words Used by {model2_name}"
222
+
223
+ word_list = [f"**{item['word']}** ({item['count']})" for item in important_words[model2_name][:10]]
224
+ model2_words_visible = True
225
+ model2_words_value = ", ".join(word_list)
226
 
227
+ # Format similarity metrics
228
+ comparisons = bow_results.get("comparisons", {})
 
 
 
 
229
  comparison_key = f"{model1_name} vs {model2_name}"
230
+
231
+ if comparison_key in comparisons:
232
+ metrics = comparisons[comparison_key]
233
+ cosine = metrics.get("cosine_similarity", 0)
234
+ jaccard = metrics.get("jaccard_similarity", 0)
235
+ semantic = metrics.get("semantic_similarity", 0)
236
+ common_words = metrics.get("common_word_count", 0)
237
 
238
  similarity_title_visible = True
239
  similarity_metrics_visible = True
240
  similarity_metrics_value = f"""
241
+ - **Cosine Similarity**: {cosine:.2f} (higher means more similar word frequency patterns)
242
+ - **Jaccard Similarity**: {jaccard:.2f} (higher means more word overlap)
243
+ - **Semantic Similarity**: {semantic:.2f} (higher means more similar meaning)
244
+ - **Common Words**: {common_words} words appear in both responses
245
  """
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
246
 
247
+ # Check for N-gram analysis
248
+ elif selected_analysis == "N-gram Analysis" and "ngram_analysis" in analyses:
249
+ visualization_area_visible = True
250
+ ngram_results = analyses["ngram_analysis"]
251
+ models = ngram_results.get("models", [])
252
+ ngram_size = ngram_results.get("ngram_size", 2)
253
+ size_name = "Unigrams" if ngram_size == 1 else f"{ngram_size}-grams"
254
+
255
+ if len(models) >= 2:
256
+ prompt_title_visible = True
257
+ prompt_title_value = f"## Analysis of Prompt: \"{prompt[:100]}...\""
258
+
259
+ models_compared_visible = True
260
+ models_compared_value = f"### {size_name} Analysis: Comparing responses from {models[0]} and {models[1]}"
261
+
262
+ # Extract and format information for display
263
+ model1_name = models[0]
264
+ model2_name = models[1]
265
 
266
+ # Format important n-grams for each model
267
+ important_ngrams = ngram_results.get("important_ngrams", {})
268
+
269
+ if model1_name in important_ngrams:
270
  model1_title_visible = True
271
+ model1_title_value = f"#### Top {size_name} Used by {model1_name}"
272
+
273
+ ngram_list = [f"**{item['ngram']}** ({item['count']})" for item in important_ngrams[model1_name][:10]]
274
  model1_words_visible = True
275
+ model1_words_value = ", ".join(ngram_list)
276
+
277
+ if model2_name in important_ngrams:
278
+ model2_title_visible = True
279
+ model2_title_value = f"#### Top {size_name} Used by {model2_name}"
280
+
281
+ ngram_list = [f"**{item['ngram']}** ({item['count']})" for item in important_ngrams[model2_name][:10]]
282
+ model2_words_visible = True
283
+ model2_words_value = ", ".join(ngram_list)
284
+
285
+ # Format similarity metrics if available
286
+ if "comparisons" in ngram_results:
287
+ comparison_key = f"{model1_name} vs {model2_name}"
288
+
289
+ if comparison_key in ngram_results["comparisons"]:
290
+ metrics = ngram_results["comparisons"][comparison_key]
291
+ common_count = metrics.get("common_ngram_count", 0)
292
+
293
+ similarity_title_visible = True
294
+ similarity_metrics_visible = True
295
+ similarity_metrics_value = f"""
296
+ - **Common {size_name}**: {common_count} {size_name.lower()} appear in both responses
297
+ """
298
+
299
+ # Check for Topic Modeling analysis
300
+ elif selected_analysis == "Topic Modeling" and "topic_modeling" in analyses:
301
+ visualization_area_visible = True
302
+ topic_results = analyses["topic_modeling"]
303
+ models = topic_results.get("models", [])
304
+ method = topic_results.get("method", "lda").upper()
305
+ n_topics = topic_results.get("n_topics", 3)
306
 
307
+ if len(models) >= 2:
308
+ prompt_title_visible = True
309
+ prompt_title_value = f"## Analysis of Prompt: \"{prompt[:100]}...\""
310
+
311
+ models_compared_visible = True
312
+ models_compared_value = f"### Topic Modeling Analysis ({method}, {n_topics} topics)"
313
+
314
+ # Extract and format topic information
315
+ topics = topic_results.get("topics", [])
316
+
317
+ if topics:
318
+ # Format topic info for display
319
+ topic_info = []
320
+ for topic in topics[:3]: # Show first 3 topics
321
+ topic_id = topic.get("id", 0)
322
+ words = topic.get("words", [])[:5] # Top 5 words per topic
323
+
324
+ if words:
325
+ topic_info.append(f"**Topic {topic_id+1}**: {', '.join(words)}")
326
+
327
+ if topic_info:
328
+ model1_title_visible = True
329
+ model1_title_value = "#### Discovered Topics"
330
+ model1_words_visible = True
331
+ model1_words_value = "\n".join(topic_info)
332
+
333
+ # Get topic distributions for models
334
+ model_topics = topic_results.get("model_topics", {})
335
+
336
+ if model_topics:
337
+ model1_name = models[0]
338
+ model2_name = models[1]
339
+
340
+ # Format topic distribution info
341
+ if model1_name in model_topics and model2_name in model_topics:
342
+ model2_title_visible = True
343
+ model2_title_value = "#### Topic Distribution"
344
+ model2_words_visible = True
345
+
346
+ # Simple distribution display
347
+ dist1 = model_topics[model1_name]
348
+ dist2 = model_topics[model2_name]
349
+
350
+ model2_words_value = f"""
351
+ **{model1_name}**: {', '.join([f"Topic {i+1}: {v:.2f}" for i, v in enumerate(dist1[:3])])}
352
+
353
+ **{model2_name}**: {', '.join([f"Topic {i+1}: {v:.2f}" for i, v in enumerate(dist2[:3])])}
354
+ """
355
+
356
+ # Add similarity metrics if available
357
+ comparisons = topic_results.get("comparisons", {})
358
+ if comparisons:
359
+ comparison_key = f"{model1_name} vs {model2_name}"
360
+
361
+ if comparison_key in comparisons:
362
+ metrics = comparisons[comparison_key]
363
+ js_div = metrics.get("js_divergence", 0)
364
+
365
+ similarity_title_visible = True
366
+ similarity_metrics_visible = True
367
+ similarity_metrics_value = f"""
368
+ - **Topic Distribution Divergence**: {js_div:.4f} (lower means more similar topic distributions)
369
+ """
370
+
371
+ # Check for Classifier analysis
372
+ elif selected_analysis == "Classifier" and "classifier" in analyses:
373
+ visualization_area_visible = True
374
+ classifier_results = analyses["classifier"]
375
+ models = classifier_results.get("models", [])
376
 
377
+ if len(models) >= 2:
378
+ prompt_title_visible = True
379
+ prompt_title_value = f"## Analysis of Prompt: \"{prompt[:100]}...\""
380
+
381
+ models_compared_visible = True
382
+ models_compared_value = f"### Classifier Analysis for {models[0]} and {models[1]}"
383
+
384
+ # Extract and format classifier information
385
  model1_name = models[0]
386
  model2_name = models[1]
387
 
388
+ # Display classifications for each model
389
+ classifications = classifier_results.get("classifications", {})
390
+
391
+ if classifications:
392
+ model1_title_visible = True
393
+ model1_title_value = f"#### Classification Results"
394
+ model1_words_visible = True
395
 
396
+ model1_results = classifications.get(model1_name, {})
397
+ model2_results = classifications.get(model2_name, {})
 
398
 
399
+ model1_words_value = f"""
400
+ **{model1_name}**:
401
+ - Formality: {model1_results.get('formality', 'N/A')}
402
+ - Sentiment: {model1_results.get('sentiment', 'N/A')}
403
+ - Complexity: {model1_results.get('complexity', 'N/A')}
404
 
405
+ **{model2_name}**:
406
+ - Formality: {model2_results.get('formality', 'N/A')}
407
+ - Sentiment: {model2_results.get('sentiment', 'N/A')}
408
+ - Complexity: {model2_results.get('complexity', 'N/A')}
409
  """
410
 
411
+ # Show comparison
412
+ model2_title_visible = True
413
+ model2_title_value = f"#### Classification Comparison"
414
+ model2_words_visible = True
 
 
 
 
415
 
416
+ differences = classifier_results.get("differences", {})
417
+ model2_words_value = "\n".join([
418
+ f"- **{category}**: {diff}"
419
+ for category, diff in differences.items()
420
+ ])
421
+
422
+ # If we don't have visualization data from any analysis
423
+ if not visualization_area_visible:
424
+ return (
425
+ analysis_results,
426
+ False,
427
+ False,
428
+ gr.update(visible=False),
429
+ gr.update(visible=False),
430
+ gr.update(visible=False),
431
+ gr.update(visible=False),
432
+ gr.update(visible=False),
433
+ gr.update(visible=False),
434
+ gr.update(visible=False),
435
+ gr.update(visible=False),
436
+ gr.update(visible=False),
437
+ True,
438
+ gr.update(visible=True, value="❌ **No visualization data found.** Make sure to select a valid analysis option.")
439
+ )
440
+
441
+ # Return all updated component values
442
+ return (
443
+ analysis_results, # analysis_results_state
444
+ False, # analysis_output visibility
445
+ True, # visualization_area_visible
446
+ gr.update(visible=True), # analysis_title
447
+ gr.update(visible=prompt_title_visible, value=prompt_title_value), # prompt_title
448
+ gr.update(visible=models_compared_visible, value=models_compared_value), # models_compared
449
+ gr.update(visible=model1_title_visible, value=model1_title_value), # model1_title
450
+ gr.update(visible=model1_words_visible, value=model1_words_value), # model1_words
451
+ gr.update(visible=model2_title_visible, value=model2_title_value), # model2_title
452
+ gr.update(visible=model2_words_visible, value=model2_words_value), # model2_words
453
+ gr.update(visible=similarity_title_visible), # similarity_metrics_title
454
+ gr.update(visible=similarity_metrics_visible, value=similarity_metrics_value), # similarity_metrics
455
+ False, # status_message_visible
456
+ gr.update(visible=False) # status_message
457
+ )
458
 
459
+ except Exception as e:
460
+ import traceback
461
+ error_msg = f"Error in analysis: {str(e)}\n{traceback.format_exc()}"
462
+ print(error_msg)
463
+
464
  return (
465
+ {"error": error_msg}, # analysis_results_state
466
+ True, # analysis_output visibility (show raw JSON for debugging)
467
+ False, # visualization_area_visible
468
  gr.update(visible=False),
469
  gr.update(visible=False),
470
  gr.update(visible=False),
 
474
  gr.update(visible=False),
475
  gr.update(visible=False),
476
  gr.update(visible=False),
477
+ True, # status_message_visible
478
+ gr.update(visible=True, value=f"❌ **Error during analysis:**\n\n```\n{str(e)}\n```") # status_message
479
  )
480
+
481
+ # Add a new LLM Analysis tab
482
+ with gr.Tab("LLM Analysis"):
483
+ gr.Markdown("## LLM-Based Response Analysis")
484
+
485
+ with gr.Row():
486
+ with gr.Column():
487
+ llm_analysis_type = gr.Radio(
488
+ choices=["Response Quality", "Response Comparison", "Factual Accuracy"],
489
+ label="Analysis Type",
490
+ value="Response Comparison"
 
 
 
 
 
 
491
  )
492
+
493
+ llm_model = gr.Dropdown(
494
+ choices=["OpenAI GPT-4", "Anthropic Claude", "Local LLM"],
495
+ label="Analysis Model",
496
+ value="OpenAI GPT-4"
497
+ )
498
+
499
+ run_llm_analysis_btn = gr.Button("Run LLM Analysis", variant="primary")
500
 
501
+ with gr.Column():
502
+ llm_analysis_prompt = gr.Textbox(
503
+ label="Custom Analysis Instructions (Optional)",
504
+ placeholder="Enter any specific instructions for the analysis...",
505
+ lines=3
506
+ )
507
 
508
+ llm_analysis_status = gr.Markdown("*No analysis has been run*")
509
+
510
+ llm_analysis_result = gr.Markdown(visible=False)
511
+
512
+ # Placeholder function for LLM analysis
513
+ def run_llm_analysis(dataset, analysis_type, model, custom_prompt):
514
+ if not dataset or "entries" not in dataset or not dataset["entries"]:
515
  return (
516
+ gr.update(visible=True, value="❌ **Error:** No dataset loaded. Please create or load a dataset first."),
517
+ gr.update(visible=False)
 
 
 
 
 
 
 
 
 
 
 
 
518
  )
519
+
520
+ # Placeholder for actual implementation
521
+ return (
522
+ gr.update(visible=True, value="⏳ **Implementation in progress**\n\nLLM-based analysis will be available in a future update."),
523
+ gr.update(visible=False)
524
+ )
525
+
526
+ # Connect the run button to the analysis function
527
+ run_llm_analysis_btn.click(
528
+ fn=run_llm_analysis,
529
+ inputs=[dataset_state, llm_analysis_type, llm_model, llm_analysis_prompt],
530
+ outputs=[llm_analysis_status, llm_analysis_result]
531
+ )
532
 
533
+ # Visibility update functions - unchanged
534
  def update_visibility(viz_visible, status_visible):
535
+ # ...existing code...
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
536
 
537
+ # Run analysis with proper parameters
538
+ run_analysis_btn.click(
539
+ fn=run_analysis,
540
+ inputs=[dataset_state, analysis_options, bow_top_slider, ngram_n, ngram_top, topic_count],
541
+ outputs=[
542
+ analysis_results_state,
543
+ analysis_output,
544
+ visualization_area_visible,
545
+ analysis_title,
546
+ prompt_title,
547
+ models_compared,
548
+ model1_title,
549
+ model1_words,
550
+ model2_title,
551
+ model2_words,
552
+ similarity_metrics_title,
553
+ similarity_metrics,
554
+ status_message_visible,
555
+ status_message
556
+ ]
557
+ )
558
 
559
  return app
560
 
processors/text_classifiers.py ADDED
@@ -0,0 +1,152 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import nltk
2
+ from nltk.sentiment import SentimentIntensityAnalyzer
3
+ import statistics
4
+ import re
5
+
6
+ def download_nltk_resources():
7
+ """Download required NLTK resources if not already downloaded"""
8
+ try:
9
+ nltk.download('vader_lexicon', quiet=True)
10
+ except:
11
+ pass
12
+
13
+ # Ensure NLTK resources are available
14
+ download_nltk_resources()
15
+
16
+ def classify_formality(text):
17
+ """
18
+ Classify text formality based on simple heuristics
19
+
20
+ Args:
21
+ text (str): Text to analyze
22
+
23
+ Returns:
24
+ str: Formality level (Formal, Neutral, or Informal)
25
+ """
26
+ # Simple formality indicators
27
+ formal_indicators = [
28
+ r'\b(therefore|thus|consequently|furthermore|moreover|however)\b',
29
+ r'\b(in accordance with|with respect to|regarding|concerning)\b',
30
+ r'\b(shall|must|may|will be required to)\b',
31
+ r'\b(it is|there are|there is)\b',
32
+ r'\b(Mr\.|Ms\.|Dr\.|Prof\.)\b'
33
+ ]
34
+
35
+ informal_indicators = [
36
+ r'\b(like|yeah|cool|awesome|gonna|wanna|gotta)\b',
37
+ r'(\!{2,}|\?{2,})',
38
+ r'\b(lol|haha|wow|omg|btw)\b',
39
+ r'\b(don\'t|can\'t|won\'t|shouldn\'t)\b',
40
+ r'(\.{3,})'
41
+ ]
42
+
43
+ # Calculate scores
44
+ formal_score = sum([len(re.findall(pattern, text, re.IGNORECASE)) for pattern in formal_indicators])
45
+ informal_score = sum([len(re.findall(pattern, text, re.IGNORECASE)) for pattern in informal_indicators])
46
+
47
+ # Normalize by text length
48
+ words = len(text.split())
49
+ if words > 0:
50
+ formal_score = formal_score / (words / 100) # per 100 words
51
+ informal_score = informal_score / (words / 100) # per 100 words
52
+
53
+ # Determine formality
54
+ if formal_score > informal_score * 1.5:
55
+ return "Formal"
56
+ elif informal_score > formal_score * 1.5:
57
+ return "Informal"
58
+ else:
59
+ return "Neutral"
60
+
61
+ def classify_sentiment(text):
62
+ """
63
+ Classify text sentiment using NLTK's VADER
64
+
65
+ Args:
66
+ text (str): Text to analyze
67
+
68
+ Returns:
69
+ str: Sentiment (Positive, Neutral, or Negative)
70
+ """
71
+ try:
72
+ sia = SentimentIntensityAnalyzer()
73
+ sentiment = sia.polarity_scores(text)
74
+
75
+ if sentiment['compound'] >= 0.05:
76
+ return "Positive"
77
+ elif sentiment['compound'] <= -0.05:
78
+ return "Negative"
79
+ else:
80
+ return "Neutral"
81
+ except:
82
+ return "Neutral"
83
+
84
+ def classify_complexity(text):
85
+ """
86
+ Classify text complexity based on sentence length and word length
87
+
88
+ Args:
89
+ text (str): Text to analyze
90
+
91
+ Returns:
92
+ str: Complexity level (Simple, Average, or Complex)
93
+ """
94
+ # Split into sentences
95
+ sentences = nltk.sent_tokenize(text)
96
+
97
+ if not sentences:
98
+ return "Average"
99
+
100
+ # Calculate average sentence length
101
+ sentence_lengths = [len(s.split()) for s in sentences]
102
+ avg_sentence_length = statistics.mean(sentence_lengths) if sentence_lengths else 0
103
+
104
+ # Calculate average word length
105
+ words = [word for sentence in sentences for word in nltk.word_tokenize(sentence)
106
+ if word.isalnum()] # only consider alphanumeric tokens
107
+
108
+ avg_word_length = statistics.mean([len(word) for word in words]) if words else 0
109
+
110
+ # Determine complexity
111
+ if avg_sentence_length > 20 or avg_word_length > 6:
112
+ return "Complex"
113
+ elif avg_sentence_length < 12 or avg_word_length < 4:
114
+ return "Simple"
115
+ else:
116
+ return "Average"
117
+
118
+ def compare_classifications(text1, text2):
119
+ """
120
+ Compare classifications between two texts
121
+
122
+ Args:
123
+ text1 (str): First text
124
+ text2 (str): Second text
125
+
126
+ Returns:
127
+ dict: Comparison results
128
+ """
129
+ formality1 = classify_formality(text1)
130
+ formality2 = classify_formality(text2)
131
+
132
+ sentiment1 = classify_sentiment(text1)
133
+ sentiment2 = classify_sentiment(text2)
134
+
135
+ complexity1 = classify_complexity(text1)
136
+ complexity2 = classify_complexity(text2)
137
+
138
+ results = {}
139
+
140
+ if formality1 != formality2:
141
+ results["Formality"] = f"Model 1 is {formality1.lower()}, while Model 2 is {formality2.lower()}"
142
+
143
+ if sentiment1 != sentiment2:
144
+ results["Sentiment"] = f"Model 1 has a {sentiment1.lower()} tone, while Model 2 has a {sentiment2.lower()} tone"
145
+
146
+ if complexity1 != complexity2:
147
+ results["Complexity"] = f"Model 1 uses {complexity1.lower()} language, while Model 2 uses {complexity2.lower()} language"
148
+
149
+ if not results:
150
+ results["Summary"] = "Both responses have similar writing characteristics"
151
+
152
+ return results
ui/analysis_screen.py CHANGED
@@ -9,194 +9,156 @@ from processors.ngram_analysis import compare_ngrams
9
  from processors.bow_analysis import compare_bow
10
  # from processors.metrics import calculate_similarity
11
  # from processors.diff_highlighter import highlight_differences
 
 
12
 
13
  def create_analysis_screen():
14
  """
15
- Create the analysis options screen
16
 
17
  Returns:
18
- tuple: (analysis_options, analysis_params, run_analysis_btn, analysis_output, bow_top_slider, ngram_n, ngram_top, topic_count)
19
  """
20
- with gr.Column() as analysis_screen:
21
  gr.Markdown("## Analysis Options")
22
- gr.Markdown("Select which analysis you want to run on the LLM responses.")
23
 
24
- # Change from CheckboxGroup to Radio for analysis selection
25
- with gr.Group():
26
- analysis_options = gr.Radio(
27
- choices=[
28
- "Bag of Words",
29
- "N-gram Analysis",
30
- "Topic Modeling",
31
- "Bias Detection",
32
- "Classifier", # New option for future development
33
- "LLM Analysis" # New option for future development
34
- ],
35
- value="Bag of Words", # Default selection
36
- label="Select Analysis Type"
37
- )
38
-
39
- # Create slider directly here for easier access
40
- gr.Markdown("### Bag of Words Parameters")
41
- bow_top_slider = gr.Slider(
42
- minimum=10, maximum=100, value=25, step=5,
43
- label="Top Words to Compare",
44
- elem_id="bow_top_slider"
45
- )
46
-
47
- # Create N-gram parameters accessible at top level
48
- ngram_n = gr.Radio(
49
- choices=["1", "2", "3"], value="2",
50
- label="N-gram Size",
51
- visible=False
52
- )
53
- ngram_top = gr.Slider(
54
- minimum=5, maximum=30, value=10, step=1,
55
- label="Top N-grams to Display",
56
- visible=False
57
  )
58
 
59
- # Create topic modeling parameter accessible at top level
60
- topic_count = gr.Slider(
61
- minimum=2, maximum=10, value=3, step=1,
62
- label="Number of Topics",
63
- visible=False
64
- )
65
-
66
- # Parameters for each analysis type
67
- with gr.Group() as analysis_params:
68
- # Topic modeling parameters
69
- with gr.Group(visible=False) as topic_params:
70
- gr.Markdown("### Topic Modeling Parameters")
71
- # We'll use the topic_count defined above
72
 
73
- # N-gram parameters group (using external ngram_n and ngram_top)
74
- with gr.Group(visible=False) as ngram_params:
75
- gr.Markdown("### N-gram Parameters")
76
- # We're already using ngram_n and ngram_top defined above
77
-
78
- # Bias detection parameters
79
- with gr.Group(visible=False) as bias_params:
80
- gr.Markdown("### Bias Detection Parameters")
81
- bias_methods = gr.CheckboxGroup(
82
- choices=["Sentiment Analysis", "Partisan Leaning", "Framing Analysis"],
83
- value=["Sentiment Analysis", "Partisan Leaning"],
84
- label="Bias Detection Methods"
85
- )
86
 
87
- # Classifier parameters for future development
88
- with gr.Group(visible=False) as classifier_params:
89
- gr.Markdown("### Classifier Parameters")
90
- gr.Markdown("*Classifier options will be available in a future update*")
91
 
92
- # LLM Analysis parameters for future development
93
- with gr.Group(visible=False) as llm_params:
94
- gr.Markdown("### LLM Analysis Parameters")
95
- gr.Markdown("*LLM Analysis options will be available in a future update*")
96
-
97
- # Function to update parameter visibility based on selected analysis
98
- def update_params_visibility(selected):
99
- return {
100
- topic_params: gr.update(visible=selected == "Topic Modeling"),
101
- ngram_params: gr.update(visible=selected == "N-gram Analysis"),
102
- bias_params: gr.update(visible=selected == "Bias Detection"),
103
- classifier_params: gr.update(visible=selected == "Classifier"),
104
- llm_params: gr.update(visible=selected == "LLM Analysis"),
105
- ngram_n: gr.update(visible=selected == "N-gram Analysis"),
106
- ngram_top: gr.update(visible=selected == "N-gram Analysis"),
107
- topic_count: gr.update(visible=selected == "Topic Modeling"),
108
- bow_top_slider: gr.update(visible=selected == "Bag of Words")
109
- }
110
-
111
- # Set up event handler for analysis selection
112
- analysis_options.change(
113
- fn=update_params_visibility,
114
- inputs=[analysis_options],
115
- outputs=[
116
- topic_params,
117
- ngram_params,
118
- bias_params,
119
- classifier_params,
120
- llm_params,
121
- ngram_n,
122
- ngram_top,
123
- topic_count,
124
- bow_top_slider
125
- ]
126
- )
127
 
128
- # Run analysis button
129
- run_analysis_btn = gr.Button("Run Analysis", variant="primary", size="large")
 
 
130
 
131
- # Analysis output area - hidden JSON component to store raw results
132
- analysis_output = gr.JSON(label="Analysis Results", visible=False)
133
-
134
- # Return the components needed by app.py
135
  return analysis_options, analysis_params, run_analysis_btn, analysis_output, bow_top_slider, ngram_n, ngram_top, topic_count
136
 
137
  # Process analysis request function
138
  def process_analysis_request(dataset, selected_analysis, parameters):
139
  """
140
- Process the analysis request and run selected analysis
 
 
 
 
 
 
 
 
141
  """
142
- try:
143
- print(f"Processing analysis request with: {selected_analysis}")
144
- print(f"Parameters: {parameters}")
145
-
146
- if not dataset or "entries" not in dataset or not dataset["entries"]:
147
- return {}, gr.update(visible=True,
148
- value=json.dumps({"error": "No dataset provided or dataset is empty"}, indent=2))
149
-
150
- analysis_results = {"analyses": {}}
151
-
152
- # Extract prompt and responses
153
- prompt = dataset["entries"][0]["prompt"]
154
- response_texts = [entry["response"] for entry in dataset["entries"]]
155
- model_names = [entry["model"] for entry in dataset["entries"]]
156
-
157
- print(f"Analyzing prompt: '{prompt[:50]}...'")
158
- print(f"Models: {model_names}")
159
-
160
- analysis_results["analyses"][prompt] = {}
161
-
162
- # Run Bag of Words analysis if selected
163
- if selected_analysis == "Bag of Words":
164
- top_words = parameters.get("bow_top", 25)
165
- print(f"Running BOW analysis with top_words={top_words}")
166
- bow_results = compare_bow(response_texts, model_names, top_words)
167
- analysis_results["analyses"][prompt]["bag_of_words"] = bow_results
168
-
169
- # Run N-gram analysis if selected
170
- elif selected_analysis == "N-gram Analysis":
171
- ngram_n = int(parameters.get("ngram_n", "2"))
172
- ngram_top = parameters.get("ngram_top", 10)
173
- print(f"Running N-gram analysis with n={ngram_n}, top_n={ngram_top}")
174
- ngram_results = compare_ngrams(response_texts, model_names, ngram_n, ngram_top)
175
- analysis_results["analyses"][prompt]["ngram_analysis"] = ngram_results
176
-
177
- # Run Topic Modeling analysis if selected
178
- elif selected_analysis == "Topic Modeling":
179
- topic_count = int(parameters.get("topic_count", 3))
180
- print(f"Running Topic Modeling analysis with topic_count={topic_count}")
181
- topic_results = compare_topics(response_texts, model_names, n_topics=topic_count)
182
- analysis_results["analyses"][prompt]["topic_modeling"] = topic_results
183
-
184
- # Add placeholder for future analysis types
185
- elif selected_analysis == "Bias Detection":
186
- analysis_results["analyses"][prompt]["message"] = "Bias Detection will be available in a future update"
187
-
188
- elif selected_analysis == "Classifier":
189
- analysis_results["analyses"][prompt]["message"] = "Classifier will be available in a future update"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
190
 
191
- elif selected_analysis == "LLM Analysis":
192
- analysis_results["analyses"][prompt]["message"] = "LLM Analysis will be available in a future update"
193
-
194
- print("Analysis complete - results:", analysis_results)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
195
 
196
- # Return results and update the output component
197
- return analysis_results, gr.update(visible=False, value=analysis_results) # Hide the raw JSON
198
- except Exception as e:
199
- import traceback
200
- error_msg = f"Analysis error: {str(e)}\n{traceback.format_exc()}"
201
- print(error_msg)
202
- return {}, gr.update(visible=True, value=json.dumps({"error": error_msg}, indent=2))
 
9
  from processors.bow_analysis import compare_bow
10
  # from processors.metrics import calculate_similarity
11
  # from processors.diff_highlighter import highlight_differences
12
+ # Add this import at the top
13
+ from analysis.text_classifiers import classify_formality, classify_sentiment, classify_complexity, compare_classifications
14
 
15
  def create_analysis_screen():
16
  """
17
+ Create the UI components for the analysis options screen.
18
 
19
  Returns:
20
+ tuple: The analysis UI components
21
  """
22
+ with gr.Column() as analysis_container:
23
  gr.Markdown("## Analysis Options")
 
24
 
25
+ # Change from checkboxes to radio buttons for analysis type
26
+ analysis_options = gr.Radio(
27
+ choices=["Bag of Words", "N-gram Analysis", "Topic Modeling", "Classifier"],
28
+ label="Analysis Type",
29
+ value="Bag of Words"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
30
  )
31
 
32
+ # Parameters for different analysis types
33
+ with gr.Column() as analysis_params:
34
+ bow_top_slider = gr.Slider(minimum=5, maximum=50, step=5, value=20,
35
+ label="Number of top words to display (Bag of Words)")
 
 
 
 
 
 
 
 
 
36
 
37
+ ngram_n = gr.Slider(minimum=1, maximum=5, step=1, value=2,
38
+ label="N-gram size")
 
 
 
 
 
 
 
 
 
 
 
39
 
40
+ ngram_top = gr.Slider(minimum=5, maximum=50, step=5, value=15,
41
+ label="Number of top n-grams to display")
 
 
42
 
43
+ topic_count = gr.Slider(minimum=2, maximum=10, step=1, value=3,
44
+ label="Number of topics (Topic Modeling)")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
45
 
46
+ run_analysis_btn = gr.Button("Run Analysis", size="lg", variant="primary")
47
+
48
+ # Output area - JSON view for debugging or advanced users
49
+ analysis_output = gr.JSON(value={}, visible=False, label="Raw Analysis Results")
50
 
 
 
 
 
51
  return analysis_options, analysis_params, run_analysis_btn, analysis_output, bow_top_slider, ngram_n, ngram_top, topic_count
52
 
53
  # Process analysis request function
54
  def process_analysis_request(dataset, selected_analysis, parameters):
55
  """
56
+ Process the analysis request based on the selected options.
57
+
58
+ Args:
59
+ dataset (dict): The input dataset
60
+ selected_analysis (str): The selected analysis type
61
+ parameters (dict): Additional parameters for the analysis
62
+
63
+ Returns:
64
+ tuple: A tuple containing (analysis_results, visualization_data)
65
  """
66
+ if not dataset or "entries" not in dataset or not dataset["entries"]:
67
+ return {}, None
68
+
69
+ # Initialize the results structure
70
+ results = {"analyses": {}}
71
+
72
+ # Get the prompt text from the first entry
73
+ prompt_text = dataset["entries"][0].get("prompt", "")
74
+ if not prompt_text:
75
+ return {"error": "No prompt found in dataset"}, None
76
+
77
+ # Initialize the analysis container for this prompt
78
+ results["analyses"][prompt_text] = {}
79
+
80
+ # Get model names and responses
81
+ model1_name = dataset["entries"][0].get("model", "Model 1")
82
+ model2_name = dataset["entries"][1].get("model", "Model 2")
83
+
84
+ model1_response = dataset["entries"][0].get("response", "")
85
+ model2_response = dataset["entries"][1].get("response", "")
86
+
87
+ # Process based on the selected analysis type
88
+ if selected_analysis == "Bag of Words":
89
+ # Perform Bag of Words analysis
90
+ results["analyses"][prompt_text]["bag_of_words"] = {
91
+ "models": [model1_name, model2_name],
92
+ "important_words": {
93
+ model1_name: extract_important_words(model1_response, top_n=parameters.get("bow_top", 20)),
94
+ model2_name: extract_important_words(model2_response, top_n=parameters.get("bow_top", 20))
95
+ },
96
+ "comparisons": {
97
+ f"{model1_name} vs {model2_name}": calculate_text_similarity(model1_response, model2_response)
98
+ }
99
+ }
100
+
101
+ elif selected_analysis == "N-gram Analysis":
102
+ # Perform N-gram analysis
103
+ ngram_size = parameters.get("ngram_n", 2)
104
+ top_n = parameters.get("ngram_top", 15)
105
+
106
+ results["analyses"][prompt_text]["ngram_analysis"] = {
107
+ "models": [model1_name, model2_name],
108
+ "ngram_size": ngram_size,
109
+ "important_ngrams": {
110
+ model1_name: extract_ngrams(model1_response, n=ngram_size, top_n=top_n),
111
+ model2_name: extract_ngrams(model2_response, n=ngram_size, top_n=top_n)
112
+ },
113
+ "comparisons": {
114
+ f"{model1_name} vs {model2_name}": compare_ngrams(model1_response, model2_response, n=ngram_size)
115
+ }
116
+ }
117
+
118
+ elif selected_analysis == "Topic Modeling":
119
+ # Perform topic modeling analysis
120
+ topic_count = parameters.get("topic_count", 3)
121
+
122
+ try:
123
+ topic_results = perform_topic_modeling(
124
+ [model1_response, model2_response],
125
+ model_names=[model1_name, model2_name],
126
+ n_topics=topic_count
127
+ )
128
 
129
+ results["analyses"][prompt_text]["topic_modeling"] = topic_results
130
+ except Exception as e:
131
+ import traceback
132
+ print(f"Topic modeling error: {str(e)}\n{traceback.format_exc()}")
133
+ results["analyses"][prompt_text]["topic_modeling"] = {
134
+ "models": [model1_name, model2_name],
135
+ "error": str(e),
136
+ "message": "Topic modeling failed. Try with longer text or different parameters."
137
+ }
138
+
139
+ elif selected_analysis == "Classifier":
140
+ # Perform classifier analysis (placeholder implementation)
141
+ results["analyses"][prompt_text]["classifier"] = {
142
+ "models": [model1_name, model2_name],
143
+ "classifications": {
144
+ model1_name: {
145
+ "formality": classify_formality(model1_response),
146
+ "sentiment": classify_sentiment(model1_response),
147
+ "complexity": classify_complexity(model1_response)
148
+ },
149
+ model2_name: {
150
+ "formality": classify_formality(model2_response),
151
+ "sentiment": classify_sentiment(model2_response),
152
+ "complexity": classify_complexity(model2_response)
153
+ }
154
+ },
155
+ "differences": compare_classifications(model1_response, model2_response)
156
+ }
157
+
158
+ else:
159
+ # Unknown analysis type
160
+ results["analyses"][prompt_text]["message"] = "Please select a valid analysis type."
161
+
162
+ # Return both the analysis results and a placeholder for visualization data
163
+ return results, None
164