Ryan commited on
Commit
e66f533
·
1 Parent(s): d7de222
.DS_Store CHANGED
Binary files a/.DS_Store and b/.DS_Store differ
 
.idea/workspace.xml CHANGED
@@ -5,13 +5,29 @@
5
  </component>
6
  <component name="ChangeListManager">
7
  <list default="true" id="8e67814c-7f04-433c-ab7a-2b65a1106d4c" name="Changes" comment="">
8
- <change beforePath="$PROJECT_DIR$/processors/topic_modeling.py" beforeDir="false" afterPath="$PROJECT_DIR$/processors/topic_modeling.py" afterDir="false" />
 
 
 
 
 
 
 
 
 
9
  </list>
10
  <option name="SHOW_DIALOG" value="false" />
11
  <option name="HIGHLIGHT_CONFLICTS" value="true" />
12
  <option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" />
13
  <option name="LAST_RESOLUTION" value="IGNORE" />
14
  </component>
 
 
 
 
 
 
 
15
  <component name="Git.Settings">
16
  <option name="RECENT_GIT_ROOT_PATH" value="$PROJECT_DIR$" />
17
  </component>
@@ -55,7 +71,7 @@
55
  <option name="presentableId" value="Default" />
56
  <updated>1745170754325</updated>
57
  <workItem from="1745170755404" duration="245000" />
58
- <workItem from="1745172030020" duration="11940000" />
59
  </task>
60
  <servers />
61
  </component>
 
5
  </component>
6
  <component name="ChangeListManager">
7
  <list default="true" id="8e67814c-7f04-433c-ab7a-2b65a1106d4c" name="Changes" comment="">
8
+ <change afterPath="$PROJECT_DIR$/analysis_runner.py" afterDir="false" />
9
+ <change afterPath="$PROJECT_DIR$/data_handler.py" afterDir="false" />
10
+ <change afterPath="$PROJECT_DIR$/processors/bias_processor.py" afterDir="false" />
11
+ <change afterPath="$PROJECT_DIR$/processors/bow_processor.py" afterDir="false" />
12
+ <change afterPath="$PROJECT_DIR$/processors/classifier_processor.py" afterDir="false" />
13
+ <change afterPath="$PROJECT_DIR$/processors/ngram_processor.py" afterDir="false" />
14
+ <change afterPath="$PROJECT_DIR$/processors/topic_processor.py" afterDir="false" />
15
+ <change afterPath="$PROJECT_DIR$/visualization_handler.py" afterDir="false" />
16
+ <change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" />
17
+ <change beforePath="$PROJECT_DIR$/app.py" beforeDir="false" afterPath="$PROJECT_DIR$/app.py" afterDir="false" />
18
  </list>
19
  <option name="SHOW_DIALOG" value="false" />
20
  <option name="HIGHLIGHT_CONFLICTS" value="true" />
21
  <option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" />
22
  <option name="LAST_RESOLUTION" value="IGNORE" />
23
  </component>
24
+ <component name="FileTemplateManagerImpl">
25
+ <option name="RECENT_TEMPLATES">
26
+ <list>
27
+ <option value="Jupyter Notebook" />
28
+ </list>
29
+ </option>
30
+ </component>
31
  <component name="Git.Settings">
32
  <option name="RECENT_GIT_ROOT_PATH" value="$PROJECT_DIR$" />
33
  </component>
 
71
  <option name="presentableId" value="Default" />
72
  <updated>1745170754325</updated>
73
  <workItem from="1745170755404" duration="245000" />
74
+ <workItem from="1745172030020" duration="14852000" />
75
  </task>
76
  <servers />
77
  </component>
analysis_runner.py ADDED
@@ -0,0 +1,196 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import logging
2
+ import traceback
3
+ import gradio as gr
4
+ from processors.bow_processor import process_bow_analysis
5
+ from processors.ngram_processor import process_ngram_analysis
6
+ from processors.topic_processor import process_topic_modeling
7
+ from processors.classifier_processor import process_classifier_analysis
8
+ from processors.bias_processor import process_bias_detection
9
+
10
+ # Set up logging
11
+ logger = logging.getLogger('gradio_app.analysis_runner')
12
+
13
+ # Try to use the improved version of process_analysis_request if available
14
+ try:
15
+ from improved_analysis_handler import process_analysis_request
16
+
17
+ logger.info("Using improved analysis handler")
18
+ except ImportError:
19
+ logger.info("Using original analysis handler")
20
+ from ui.analysis_screen import process_analysis_request
21
+
22
+
23
+ def run_analysis(dataset, selected_analysis, ngram_n, ngram_top, topic_count):
24
+ """
25
+ Run the selected analysis on the provided dataset and return visualization components
26
+
27
+ Args:
28
+ dataset (dict): The dataset to analyze
29
+ selected_analysis (str): The type of analysis to run
30
+ ngram_n (int): N-gram size for N-gram analysis
31
+ ngram_top (int): Number of top N-grams to display
32
+ topic_count (int): Number of topics for topic modeling
33
+
34
+ Returns:
35
+ tuple: Updated visualization components
36
+ """
37
+ try:
38
+ # Create parameter dictionary
39
+ parameters = {
40
+ "ngram_n": ngram_n,
41
+ "ngram_top": ngram_top,
42
+ "topic_count": topic_count
43
+ }
44
+
45
+ logger.info(f"Running analysis with selected type: {selected_analysis}")
46
+ logger.info(f"Parameters: {parameters}")
47
+
48
+ if not dataset or "entries" not in dataset or not dataset["entries"]:
49
+ return default_no_dataset()
50
+
51
+ # Process the analysis request - passing selected_analysis as a string
52
+ analysis_results, _ = process_analysis_request(dataset, selected_analysis, parameters)
53
+
54
+ # If there's an error or no results
55
+ if not analysis_results or "analyses" not in analysis_results or not analysis_results["analyses"]:
56
+ return default_no_results()
57
+
58
+ # Extract information to display in components
59
+ prompt = list(analysis_results["analyses"].keys())[0]
60
+ analyses = analysis_results["analyses"][prompt]
61
+
62
+ # Check for messages from placeholder analyses
63
+ if "message" in analyses:
64
+ return default_message_response(analyses["message"])
65
+
66
+ # Route to the appropriate processor based on analysis type
67
+ if selected_analysis == "Bag of Words" and "bag_of_words" in analyses:
68
+ return process_bow_analysis(analysis_results, prompt, analyses)
69
+
70
+ elif selected_analysis == "N-gram Analysis" and "ngram_analysis" in analyses:
71
+ return process_ngram_analysis(analysis_results, prompt, analyses)
72
+
73
+ elif selected_analysis == "Topic Modeling" and "topic_modeling" in analyses:
74
+ return process_topic_modeling(analysis_results, prompt, analyses)
75
+
76
+ elif selected_analysis == "Classifier" and "classifier" in analyses:
77
+ return process_classifier_analysis(analysis_results, prompt, analyses)
78
+
79
+ elif selected_analysis == "Bias Detection" and "bias_detection" in analyses:
80
+ return process_bias_detection(analysis_results, prompt, analyses)
81
+
82
+ # If we don't have visualization data from any analysis
83
+ return default_no_visualization(analysis_results)
84
+
85
+ except Exception as e:
86
+ error_msg = f"Error in analysis: {str(e)}\n{traceback.format_exc()}"
87
+ logger.error(error_msg)
88
+
89
+ return default_error_response(error_msg, str(e))
90
+
91
+
92
+ def default_no_dataset():
93
+ """Return default component updates when no dataset is loaded"""
94
+ return (
95
+ {}, # analysis_results_state
96
+ False, # analysis_output visibility
97
+ False, # visualization_area_visible
98
+ gr.update(visible=False), # analysis_title
99
+ gr.update(visible=False), # prompt_title
100
+ gr.update(visible=False), # models_compared
101
+ gr.update(visible=False), # model1_title
102
+ gr.update(visible=False), # model1_words
103
+ gr.update(visible=False), # model2_title
104
+ gr.update(visible=False), # model2_words
105
+ gr.update(visible=False), # similarity_metrics_title
106
+ gr.update(visible=False), # similarity_metrics
107
+ True, # status_message_visible
108
+ gr.update(visible=True, value="❌ **Error:** No dataset loaded. Please create or load a dataset first."),
109
+ # status_message
110
+ gr.update(visible=False) # bias_visualizations
111
+ )
112
+
113
+
114
+ def default_no_results():
115
+ """Return default component updates when no analysis results are found"""
116
+ return (
117
+ {}, # analysis_results_state
118
+ False, # analysis_output visibility
119
+ False, # visualization_area_visible
120
+ gr.update(visible=False), # analysis_title
121
+ gr.update(visible=False), # prompt_title
122
+ gr.update(visible=False), # models_compared
123
+ gr.update(visible=False), # model1_title
124
+ gr.update(visible=False), # model1_words
125
+ gr.update(visible=False), # model2_title
126
+ gr.update(visible=False), # model2_words
127
+ gr.update(visible=False), # similarity_metrics_title
128
+ gr.update(visible=False), # similarity_metrics
129
+ True, # status_message_visible
130
+ gr.update(visible=True, value="❌ **No results found.** Try a different analysis option."), # status_message
131
+ gr.update(visible=False) # bias_visualizations
132
+ )
133
+
134
+
135
+ def default_message_response(message):
136
+ """Return default component updates for a simple message response"""
137
+ return (
138
+ {}, # analysis_results_state
139
+ False, # analysis_output visibility
140
+ False, # visualization_area_visible
141
+ gr.update(visible=False), # analysis_title
142
+ gr.update(visible=False), # prompt_title
143
+ gr.update(visible=False), # models_compared
144
+ gr.update(visible=False), # model1_title
145
+ gr.update(visible=False), # model1_words
146
+ gr.update(visible=False), # model2_title
147
+ gr.update(visible=False), # model2_words
148
+ gr.update(visible=False), # similarity_metrics_title
149
+ gr.update(visible=False), # similarity_metrics
150
+ True, # status_message_visible
151
+ gr.update(visible=True, value=f"ℹ️ **{message}**"), # status_message
152
+ gr.update(visible=False) # bias_visualizations
153
+ )
154
+
155
+
156
+ def default_no_visualization(analysis_results):
157
+ """Return a default set of component updates when no visualization can be shown"""
158
+ return (
159
+ analysis_results,
160
+ False,
161
+ 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
+ gr.update(visible=False),
171
+ True,
172
+ gr.update(visible=True,
173
+ value="❌ **No visualization data found.** Make sure to select a valid analysis option."),
174
+ gr.update(visible=False) # bias_visualizations - Hide it
175
+ )
176
+
177
+
178
+ def default_error_response(error_msg, error_summary):
179
+ """Return default component updates for an error response"""
180
+ return (
181
+ {"error": error_msg}, # analysis_results_state
182
+ True, # analysis_output visibility (show raw JSON for debugging)
183
+ False, # visualization_area_visible
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
+ gr.update(visible=False),
193
+ True, # status_message_visible
194
+ gr.update(visible=True, value=f"❌ **Error during analysis:**\n\n```\n{error_summary}\n```"), # status_message
195
+ gr.update(visible=False) # bias_visualizations - Hide it during errors
196
+ )
app.py CHANGED
@@ -1,29 +1,10 @@
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
4
- from visualization.bow_visualizer import process_and_visualize_analysis
5
- from visualization.bias_visualizer import process_and_visualize_bias_analysis
6
- import nltk
7
- import os
8
- import logging
9
- import sys
10
- import traceback
11
-
12
- def create_bias_visualization_tab(analysis_results):
13
- """
14
- Create a specialized tab for bias visualization
15
-
16
- Args:
17
- analysis_results (dict): The analysis results
18
-
19
- Returns:
20
- gradio.Tab: A tab containing the bias visualization
21
- """
22
- with gr.Tab("Bias Analysis") as tab:
23
- # Let the bias visualizer create all needed components
24
- components = process_and_visualize_bias_analysis(analysis_results)
25
-
26
- return tab
27
 
28
  # Set up logging
29
  logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s')
@@ -33,58 +14,17 @@ logger = logging.getLogger('gradio_app')
33
  # Try to use the improved version if available, otherwise use original
34
  try:
35
  from improved_analysis_handler import process_analysis_request
 
36
  logger.info("Using improved analysis handler")
37
  except ImportError:
38
  logger.info("Using original analysis handler")
39
  from ui.analysis_screen import process_analysis_request
40
 
41
- # Download necessary NLTK resources
42
- def download_nltk_resources():
43
- """Download required NLTK resources if not already downloaded"""
44
- try:
45
- # Create nltk_data directory in the user's home directory if it doesn't exist
46
- nltk_data_path = os.path.expanduser("~/nltk_data")
47
- os.makedirs(nltk_data_path, exist_ok=True)
48
-
49
- # Add this path to NLTK's data path
50
- nltk.data.path.append(nltk_data_path)
51
-
52
- # Download required resources
53
- resources = ['punkt', 'wordnet', 'stopwords', 'vader_lexicon']
54
- for resource in resources:
55
- try:
56
- # Different resources can be in different directories in NLTK
57
- locations = [
58
- f'tokenizers/{resource}',
59
- f'corpora/{resource}',
60
- f'taggers/{resource}',
61
- f'{resource}'
62
- ]
63
-
64
- found = False
65
- for location in locations:
66
- try:
67
- nltk.data.find(location)
68
- logger.info(f"Resource {resource} already downloaded")
69
- found = True
70
- break
71
- except LookupError:
72
- continue
73
-
74
- if not found:
75
- logger.info(f"Downloading {resource}...")
76
- nltk.download(resource, quiet=True)
77
- except Exception as e:
78
- logger.error(f"Error with resource {resource}: {e}")
79
-
80
- logger.info("NLTK resources check completed")
81
- except Exception as e:
82
- logger.error(f"Error downloading NLTK resources: {e}")
83
 
84
  def create_app():
85
  """
86
- Create a streamlined Gradio app for dataset input and analysis.
87
-
88
  Returns:
89
  gr.Blocks: The Gradio application
90
  """
@@ -92,14 +32,14 @@ def create_app():
92
  # Application state to share data between tabs
93
  dataset_state = gr.State({})
94
  analysis_results_state = gr.State({})
95
-
96
  # Dataset Input Tab
97
  with gr.Tab("Dataset Input"):
98
  dataset_inputs, example_dropdown, load_example_btn, create_btn, prompt, response1, model1, response2, model2 = create_dataset_input()
99
-
100
  # Add status indicator to show when dataset is created
101
  dataset_status = gr.Markdown("*No dataset loaded*")
102
-
103
  # Load example dataset
104
  load_example_btn.click(
105
  fn=load_example_dataset,
@@ -111,7 +51,7 @@ def create_app():
111
  def create_dataset(p, r1, m1, r2, m2):
112
  if not p or not r1 or not r2:
113
  return {}, "❌ **Error:** Please fill in at least the prompt and both responses"
114
-
115
  dataset = {
116
  "entries": [
117
  {"prompt": p, "response": r1, "model": m1 or "Model 1"},
@@ -119,16 +59,16 @@ def create_app():
119
  ]
120
  }
121
  return dataset, "✅ **Dataset created successfully!** You can now go to the Analysis tab"
122
-
123
  create_btn.click(
124
  fn=create_dataset,
125
  inputs=[prompt, response1, model1, response2, model2],
126
  outputs=[dataset_state, dataset_status]
127
  )
128
-
129
  # Analysis Tab
130
  with gr.Tab("Analysis"):
131
- # Fix the value unpacking to match the actual return values from create_analysis_screen()
132
  analysis_components = create_analysis_screen()
133
  analysis_options = analysis_components[0]
134
  analysis_params = analysis_components[1]
@@ -137,555 +77,27 @@ def create_app():
137
  ngram_n = analysis_components[4]
138
  ngram_top = analysis_components[5]
139
  topic_count = analysis_components[6]
140
-
141
- # Pre-create visualization components (initially hidden)
142
- visualization_area_visible = gr.Checkbox(value=False, visible=False, label="Visualization Visible")
143
- analysis_title = gr.Markdown("## Analysis Results", visible=False)
144
- prompt_title = gr.Markdown(visible=False)
145
- models_compared = gr.Markdown(visible=False)
146
-
147
- # Container for model 1 words
148
- model1_title = gr.Markdown(visible=False)
149
- model1_words = gr.Markdown(visible=False)
150
-
151
- # Container for model 2 words
152
- model2_title = gr.Markdown(visible=False)
153
- model2_words = gr.Markdown(visible=False)
154
-
155
- # Similarity metrics
156
- similarity_metrics_title = gr.Markdown("### Similarity Metrics", visible=False)
157
- similarity_metrics = gr.Markdown(visible=False)
158
-
159
- # Status or error message area
160
- status_message_visible = gr.Checkbox(value=False, visible=False, label="Status Message Visible")
161
- status_message = gr.Markdown(visible=False)
162
-
163
- # Create bias visualization container (initially hidden)
164
- with gr.Column(visible=False) as bias_visualizations:
165
- gr.Markdown("### Bias Analysis Visualizations")
166
- # This will be populated dynamically
167
-
168
- # Define a helper function to extract parameter values and run the analysis
169
- def run_analysis(dataset, selected_analysis, ngram_n, ngram_top, topic_count):
170
- try:
171
- # Create parameter dictionary
172
- parameters = {
173
- "ngram_n": ngram_n,
174
- "ngram_top": ngram_top,
175
- "topic_count": topic_count
176
- }
177
-
178
- logger.info(f"Running analysis with selected type: {selected_analysis}")
179
- logger.info(f"Parameters: {parameters}")
180
-
181
- if not dataset or "entries" not in dataset or not dataset["entries"]:
182
- return (
183
- {}, # analysis_results_state
184
- False, # analysis_output visibility
185
- False, # visualization_area_visible
186
- gr.update(visible=False), # analysis_title
187
- gr.update(visible=False), # prompt_title
188
- gr.update(visible=False), # models_compared
189
- gr.update(visible=False), # model1_title
190
- gr.update(visible=False), # model1_words
191
- gr.update(visible=False), # model2_title
192
- gr.update(visible=False), # model2_words
193
- gr.update(visible=False), # similarity_metrics_title
194
- gr.update(visible=False), # similarity_metrics
195
- True, # status_message_visible
196
- gr.update(visible=True, value="❌ **Error:** No dataset loaded. Please create or load a dataset first."), # status_message
197
- gr.update(visible=False) # bias_visualizations
198
- )
199
-
200
- # Process the analysis request - passing selected_analysis as a string
201
- analysis_results, _ = process_analysis_request(dataset, selected_analysis, parameters)
202
-
203
- # If there's an error or no results
204
- if not analysis_results or "analyses" not in analysis_results or not analysis_results["analyses"]:
205
- return (
206
- analysis_results,
207
- False,
208
- 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
- gr.update(visible=False),
217
- gr.update(visible=False),
218
- True,
219
- gr.update(visible=True, value="❌ **No results found.** Try a different analysis option."),
220
- gr.update(visible=False) # bias_visualizations
221
- )
222
-
223
- # Extract information to display in components
224
- prompt = list(analysis_results["analyses"].keys())[0]
225
- analyses = analysis_results["analyses"][prompt]
226
-
227
- # Initialize visualization components visibilities and contents
228
- visualization_area_visible = False
229
- prompt_title_visible = False
230
- prompt_title_value = ""
231
- models_compared_visible = False
232
- models_compared_value = ""
233
-
234
- model1_title_visible = False
235
- model1_title_value = ""
236
- model1_words_visible = False
237
- model1_words_value = ""
238
-
239
- model2_title_visible = False
240
- model2_title_value = ""
241
- model2_words_visible = False
242
- model2_words_value = ""
243
-
244
- similarity_title_visible = False
245
- similarity_metrics_visible = False
246
- similarity_metrics_value = ""
247
-
248
- # Check for messages from placeholder analyses
249
- if "message" in analyses:
250
- return (
251
- analysis_results,
252
- False,
253
- False,
254
- gr.update(visible=False),
255
- gr.update(visible=False),
256
- gr.update(visible=False),
257
- gr.update(visible=False),
258
- gr.update(visible=False),
259
- gr.update(visible=False),
260
- gr.update(visible=False),
261
- gr.update(visible=False),
262
- gr.update(visible=False),
263
- True,
264
- gr.update(visible=True, value=f"ℹ️ **{analyses['message']}**"),
265
- gr.update(visible=False) # bias_visualizations
266
- )
267
-
268
- # Process based on the selected analysis type
269
- if selected_analysis == "Bag of Words" and "bag_of_words" in analyses:
270
- visualization_area_visible = True
271
- bow_results = analyses["bag_of_words"]
272
- models = bow_results.get("models", [])
273
-
274
- if len(models) >= 2:
275
- prompt_title_visible = True
276
- prompt_title_value = f"## Analysis of Prompt: \"{prompt[:100]}...\""
277
-
278
- models_compared_visible = True
279
- models_compared_value = f"### Comparing responses from {models[0]} and {models[1]}"
280
-
281
- # Extract and format information for display
282
- model1_name = models[0]
283
- model2_name = models[1]
284
-
285
- # Format important words for each model
286
- important_words = bow_results.get("important_words", {})
287
-
288
- if model1_name in important_words:
289
- model1_title_visible = True
290
- model1_title_value = f"#### Top Words Used by {model1_name}"
291
-
292
- word_list = [f"**{item['word']}** ({item['count']})" for item in important_words[model1_name][:10]]
293
- model1_words_visible = True
294
- model1_words_value = ", ".join(word_list)
295
-
296
- if model2_name in important_words:
297
- model2_title_visible = True
298
- model2_title_value = f"#### Top Words Used by {model2_name}"
299
-
300
- word_list = [f"**{item['word']}** ({item['count']})" for item in important_words[model2_name][:10]]
301
- model2_words_visible = True
302
- model2_words_value = ", ".join(word_list)
303
-
304
- # Format similarity metrics
305
- comparisons = bow_results.get("comparisons", {})
306
- comparison_key = f"{model1_name} vs {model2_name}"
307
 
308
- if comparison_key in comparisons:
309
- metrics = comparisons[comparison_key]
310
- cosine = metrics.get("cosine_similarity", 0)
311
- jaccard = metrics.get("jaccard_similarity", 0)
312
- semantic = metrics.get("semantic_similarity", 0)
313
- common_words = metrics.get("common_word_count", 0)
314
-
315
- similarity_title_visible = True
316
- similarity_metrics_visible = True
317
- similarity_metrics_value = f"""
318
- - **Cosine Similarity**: {cosine:.2f} (higher means more similar word frequency patterns)
319
- - **Jaccard Similarity**: {jaccard:.2f} (higher means more word overlap)
320
- - **Semantic Similarity**: {semantic:.2f} (higher means more similar meaning)
321
- - **Common Words**: {common_words} words appear in both responses
322
- """
323
-
324
- # Check for N-gram analysis
325
- elif selected_analysis == "N-gram Analysis" and "ngram_analysis" in analyses:
326
- visualization_area_visible = True
327
- ngram_results = analyses["ngram_analysis"]
328
- models = ngram_results.get("models", [])
329
- ngram_size = ngram_results.get("ngram_size", 2)
330
- size_name = "Unigrams" if ngram_size == 1 else f"{ngram_size}-grams"
331
-
332
- if len(models) >= 2:
333
- prompt_title_visible = True
334
- prompt_title_value = f"## Analysis of Prompt: \"{prompt[:100]}...\""
335
-
336
- models_compared_visible = True
337
- models_compared_value = f"### {size_name} Analysis: Comparing responses from {models[0]} and {models[1]}"
338
-
339
- # Extract and format information for display
340
- model1_name = models[0]
341
- model2_name = models[1]
342
-
343
- # Format important n-grams for each model
344
- important_ngrams = ngram_results.get("important_ngrams", {})
345
-
346
- if model1_name in important_ngrams:
347
- model1_title_visible = True
348
- model1_title_value = f"#### Top {size_name} Used by {model1_name}"
349
-
350
- ngram_list = [f"**{item['ngram']}** ({item['count']})" for item in important_ngrams[model1_name][:10]]
351
- model1_words_visible = True
352
- model1_words_value = ", ".join(ngram_list)
353
-
354
- if model2_name in important_ngrams:
355
- model2_title_visible = True
356
- model2_title_value = f"#### Top {size_name} Used by {model2_name}"
357
-
358
- ngram_list = [f"**{item['ngram']}** ({item['count']})" for item in important_ngrams[model2_name][:10]]
359
- model2_words_visible = True
360
- model2_words_value = ", ".join(ngram_list)
361
-
362
- # Format similarity metrics if available
363
- if "comparisons" in ngram_results:
364
- comparison_key = f"{model1_name} vs {model2_name}"
365
-
366
- if comparison_key in ngram_results["comparisons"]:
367
- metrics = ngram_results["comparisons"][comparison_key]
368
- common_count = metrics.get("common_ngram_count", 0)
369
-
370
- similarity_title_visible = True
371
- similarity_metrics_visible = True
372
- similarity_metrics_value = f"""
373
- - **Common {size_name}**: {common_count} {size_name.lower()} appear in both responses
374
- """
375
-
376
- # Check for Topic Modeling analysis - IMPROVED HANDLING
377
- elif selected_analysis == "Topic Modeling" and "topic_modeling" in analyses:
378
- visualization_area_visible = True
379
- topic_results = analyses["topic_modeling"]
380
-
381
- # Check for errors in topic modeling
382
- if "error" in topic_results:
383
- return (
384
- analysis_results,
385
- False, # Don't show raw JSON
386
- False, # Don't show visualization area
387
- gr.update(visible=False),
388
- gr.update(visible=False),
389
- gr.update(visible=False),
390
- gr.update(visible=False),
391
- gr.update(visible=False),
392
- gr.update(visible=False),
393
- gr.update(visible=False),
394
- gr.update(visible=False),
395
- gr.update(visible=False),
396
- True, # Show status message
397
- gr.update(visible=True, value=f"❌ **Topic modeling error:** {topic_results['error']}"),
398
- gr.update(visible=False) # bias_visualizations
399
- )
400
-
401
- models = topic_results.get("models", [])
402
- method = topic_results.get("method", "lda").upper()
403
- n_topics = topic_results.get("n_topics", 3)
404
-
405
- if len(models) >= 2:
406
- prompt_title_visible = True
407
- prompt_title_value = f"## Analysis of Prompt: \"{prompt[:100]}...\""
408
-
409
- models_compared_visible = True
410
- models_compared_value = f"### Topic Modeling Analysis ({method}, {n_topics} topics)"
411
-
412
- # Extract and format topic information
413
- topics = topic_results.get("topics", [])
414
-
415
- if topics:
416
- # Format topic info for display
417
- topic_info = []
418
- for topic in topics[:5]: # Show first 5 topics
419
- topic_id = topic.get("id", 0)
420
- words = topic.get("words", [])[:5] # Top 5 words per topic
421
-
422
- if words:
423
- topic_info.append(f"**Topic {topic_id+1}**: {', '.join(words)}")
424
-
425
- if topic_info:
426
- model1_title_visible = True
427
- model1_title_value = "#### Discovered Topics"
428
- model1_words_visible = True
429
- model1_words_value = "\n".join(topic_info)
430
-
431
- # Get topic distributions for models
432
- model_topics = topic_results.get("model_topics", {})
433
-
434
- if model_topics:
435
- model1_name = models[0]
436
- model2_name = models[1]
437
-
438
- # Format topic distribution info
439
- if model1_name in model_topics and model2_name in model_topics:
440
- model2_title_visible = True
441
- model2_title_value = "#### Topic Distribution"
442
- model2_words_visible = True
443
-
444
- # Simple distribution display
445
- dist1 = model_topics[model1_name]
446
- dist2 = model_topics[model2_name]
447
-
448
- model2_words_value = f"""
449
- **{model1_name}**: {', '.join([f"Topic {i+1}: {v:.2f}" for i, v in enumerate(dist1[:5])])}
450
-
451
- **{model2_name}**: {', '.join([f"Topic {i+1}: {v:.2f}" for i, v in enumerate(dist2[:5])])}
452
- """
453
-
454
- # Add similarity metrics if available
455
- comparisons = topic_results.get("comparisons", {})
456
- if comparisons:
457
- comparison_key = f"{model1_name} vs {model2_name}"
458
-
459
- if comparison_key in comparisons:
460
- metrics = comparisons[comparison_key]
461
- js_div = metrics.get("js_divergence", 0)
462
-
463
- # Add interpretation
464
- similarity_text = ""
465
- if js_div < 0.2:
466
- similarity_text = "very similar"
467
- elif js_div < 0.4:
468
- similarity_text = "somewhat similar"
469
- elif js_div < 0.6:
470
- similarity_text = "moderately different"
471
- else:
472
- similarity_text = "very different"
473
-
474
- similarity_title_visible = True
475
- similarity_metrics_visible = True
476
- similarity_metrics_value = f"""
477
- - **Topic Distribution Divergence**: {js_div:.4f}
478
- - The topic distributions between models are **{similarity_text}**
479
- - *Lower divergence values indicate more similar topic distributions*
480
- """
481
-
482
- # Check for Classifier analysis
483
- elif selected_analysis == "Classifier" and "classifier" in analyses:
484
- visualization_area_visible = True
485
- classifier_results = analyses["classifier"]
486
- models = classifier_results.get("models", [])
487
-
488
- if len(models) >= 2:
489
- prompt_title_visible = True
490
- prompt_title_value = f"## Analysis of Prompt: \"{prompt[:100]}...\""
491
-
492
- models_compared_visible = True
493
- models_compared_value = f"### Classifier Analysis for {models[0]} and {models[1]}"
494
-
495
- # Extract and format classifier information
496
- model1_name = models[0]
497
- model2_name = models[1]
498
-
499
- # Display classifications for each model
500
- classifications = classifier_results.get("classifications", {})
501
-
502
- if classifications:
503
- model1_title_visible = True
504
- model1_title_value = f"#### Classification Results"
505
- model1_words_visible = True
506
-
507
- model1_results = classifications.get(model1_name, {})
508
- model2_results = classifications.get(model2_name, {})
509
-
510
- model1_words_value = f"""
511
- **{model1_name}**:
512
- - Formality: {model1_results.get('formality', 'N/A')}
513
- - Sentiment: {model1_results.get('sentiment', 'N/A')}
514
- - Complexity: {model1_results.get('complexity', 'N/A')}
515
-
516
- **{model2_name}**:
517
- - Formality: {model2_results.get('formality', 'N/A')}
518
- - Sentiment: {model2_results.get('sentiment', 'N/A')}
519
- - Complexity: {model2_results.get('complexity', 'N/A')}
520
- """
521
-
522
- # Show comparison
523
- model2_title_visible = True
524
- model2_title_value = f"#### Classification Comparison"
525
- model2_words_visible = True
526
-
527
- differences = classifier_results.get("differences", {})
528
- model2_words_value = "\n".join([
529
- f"- **{category}**: {diff}"
530
- for category, diff in differences.items()
531
- ])
532
-
533
- # Add visualization handling for Bias Detection
534
- elif selected_analysis == "Bias Detection" and "bias_detection" in analyses:
535
- logger.info("Processing Bias Detection visualization")
536
-
537
- models = analyses["bias_detection"].get("models", ["Model 1", "Model 2"])
538
- logger.info(f"Bias models: {models}")
539
-
540
- try:
541
- # Initialize bias_visualizations container
542
- bias_visualizations.clear()
543
-
544
- # Create bias visualization components
545
- from visualization.bias_visualizer import process_and_visualize_bias_analysis
546
- bias_components = process_and_visualize_bias_analysis(analysis_results)
547
-
548
- logger.info(f"Created {len(bias_components)} bias visualization components")
549
-
550
- # Add components to container
551
- for component in bias_components:
552
- bias_visualizations.append(component)
553
-
554
- logger.info("Added components to bias_visualizations")
555
-
556
- return (
557
- analysis_results, # analysis_results_state
558
- False, # analysis_output visibility
559
- True, # visualization_area_visible
560
- gr.update(visible=True), # analysis_title
561
- gr.update(visible=True, value=f"## Analysis of Prompt: \"{prompt[:100]}...\""), # prompt_title
562
- gr.update(visible=True, value=f"### Comparing responses from {models[0]} and {models[1]}"), # models_compared
563
- gr.update(visible=True, value="#### Bias detection visualization is available below"), # model1_title
564
- gr.update(visible=True, value="The detailed bias analysis includes sentiment analysis, partisan term detection, and framing analysis."), # model1_words
565
- gr.update(visible=False), # model2_title
566
- gr.update(visible=False), # model2_words
567
- gr.update(visible=False), # similarity_metrics_title
568
- gr.update(visible=False), # similarity_metrics
569
- False, # status_message_visible
570
- gr.update(visible=False), # status_message
571
- gr.update(visible=True) # bias_visualizations - Make this visible
572
- )
573
- except Exception as e:
574
- import traceback
575
- logger.error(f"Error generating bias visualization: {str(e)}\n{traceback.format_exc()}")
576
-
577
- return (
578
- analysis_results,
579
- True, # Show raw JSON for debugging
580
- False,
581
- gr.update(visible=False),
582
- gr.update(visible=False),
583
- gr.update(visible=False),
584
- gr.update(visible=False),
585
- gr.update(visible=False),
586
- gr.update(visible=False),
587
- gr.update(visible=False),
588
- gr.update(visible=False),
589
- gr.update(visible=False),
590
- True,
591
- gr.update(visible=True, value=f"❌ **Error generating bias visualization:** {str(e)}"),
592
- gr.update(visible=False) # bias_visualizations
593
- )
594
 
595
- # If we don't have visualization data from any analysis
596
- if not visualization_area_visible:
597
- return (
598
- analysis_results,
599
- False,
600
- False,
601
- gr.update(visible=False),
602
- gr.update(visible=False),
603
- gr.update(visible=False),
604
- gr.update(visible=False),
605
- gr.update(visible=False),
606
- gr.update(visible=False),
607
- gr.update(visible=False),
608
- gr.update(visible=False),
609
- gr.update(visible=False),
610
- True,
611
- gr.update(visible=True, value="❌ **No visualization data found.** Make sure to select a valid analysis option."),
612
- gr.update(visible=False) # bias_visualizations - Hide it
613
- )
614
-
615
- # For all other analysis types, make sure bias_visualizations is not visible
616
- return (
617
- analysis_results, # analysis_results_state
618
- False, # analysis_output visibility
619
- True, # visualization_area_visible
620
- gr.update(visible=True), # analysis_title
621
- gr.update(visible=prompt_title_visible, value=prompt_title_value), # prompt_title
622
- gr.update(visible=models_compared_visible, value=models_compared_value), # models_compared
623
- gr.update(visible=model1_title_visible, value=model1_title_value), # model1_title
624
- gr.update(visible=model1_words_visible, value=model1_words_value), # model1_words
625
- gr.update(visible=model2_title_visible, value=model2_title_value), # model2_title
626
- gr.update(visible=model2_words_visible, value=model2_words_value), # model2_words
627
- gr.update(visible=similarity_title_visible), # similarity_metrics_title
628
- gr.update(visible=similarity_metrics_visible, value=similarity_metrics_value), # similarity_metrics
629
- False, # status_message_visible
630
- gr.update(visible=False), # status_message
631
- gr.update(visible=False) # bias_visualizations - Not visible for other analyses
632
- )
633
-
634
- except Exception as e:
635
- import traceback
636
- error_msg = f"Error in analysis: {str(e)}\n{traceback.format_exc()}"
637
- logger.error(error_msg)
638
-
639
- return (
640
- {"error": error_msg}, # analysis_results_state
641
- True, # analysis_output visibility (show raw JSON for debugging)
642
- False, # visualization_area_visible
643
- gr.update(visible=False),
644
- gr.update(visible=False),
645
- gr.update(visible=False),
646
- gr.update(visible=False),
647
- gr.update(visible=False),
648
- gr.update(visible=False),
649
- gr.update(visible=False),
650
- gr.update(visible=False),
651
- gr.update(visible=False),
652
- True, # status_message_visible
653
- gr.update(visible=True, value=f"❌ **Error during analysis:**\n\n```\n{str(e)}\n```"), # status_message
654
- gr.update(visible=False) # bias_visualizations - Hide it during errors
655
- )
656
-
657
- # Connect the run button to the analysis function
658
  run_analysis_btn.click(
659
  fn=run_analysis,
660
  inputs=[dataset_state, analysis_options, ngram_n, ngram_top, topic_count],
661
- outputs=[
662
- analysis_results_state,
663
- analysis_output,
664
- visualization_area_visible,
665
- analysis_title,
666
- prompt_title,
667
- models_compared,
668
- model1_title,
669
- model1_words,
670
- model2_title,
671
- model2_words,
672
- similarity_metrics_title,
673
- similarity_metrics,
674
- status_message_visible,
675
- status_message,
676
- bias_visualizations # Add this output for bias visualization
677
- ]
678
  )
679
-
680
  return app
681
 
 
682
  if __name__ == "__main__":
683
  # Download required NLTK resources before launching the app
684
  download_nltk_resources()
685
-
686
  logger.info("Starting LLM Response Comparator application")
687
  logger.info("===== Application Startup =====")
688
-
689
  # Create and launch the application
690
  app = create_app()
691
  app.launch()
 
1
  import gradio as gr
2
+ import logging
3
+ from data_handler import download_nltk_resources
4
+ from analysis_runner import run_analysis
5
+ from visualization_handler import create_visualization_components
6
  from ui.dataset_input import create_dataset_input, load_example_dataset
7
  from ui.analysis_screen import create_analysis_screen
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
8
 
9
  # Set up logging
10
  logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s')
 
14
  # Try to use the improved version if available, otherwise use original
15
  try:
16
  from improved_analysis_handler import process_analysis_request
17
+
18
  logger.info("Using improved analysis handler")
19
  except ImportError:
20
  logger.info("Using original analysis handler")
21
  from ui.analysis_screen import process_analysis_request
22
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
23
 
24
  def create_app():
25
  """
26
+ Create a streamlined Gradio app for dataset input and analysis.
27
+
28
  Returns:
29
  gr.Blocks: The Gradio application
30
  """
 
32
  # Application state to share data between tabs
33
  dataset_state = gr.State({})
34
  analysis_results_state = gr.State({})
35
+
36
  # Dataset Input Tab
37
  with gr.Tab("Dataset Input"):
38
  dataset_inputs, example_dropdown, load_example_btn, create_btn, prompt, response1, model1, response2, model2 = create_dataset_input()
39
+
40
  # Add status indicator to show when dataset is created
41
  dataset_status = gr.Markdown("*No dataset loaded*")
42
+
43
  # Load example dataset
44
  load_example_btn.click(
45
  fn=load_example_dataset,
 
51
  def create_dataset(p, r1, m1, r2, m2):
52
  if not p or not r1 or not r2:
53
  return {}, "❌ **Error:** Please fill in at least the prompt and both responses"
54
+
55
  dataset = {
56
  "entries": [
57
  {"prompt": p, "response": r1, "model": m1 or "Model 1"},
 
59
  ]
60
  }
61
  return dataset, "✅ **Dataset created successfully!** You can now go to the Analysis tab"
62
+
63
  create_btn.click(
64
  fn=create_dataset,
65
  inputs=[prompt, response1, model1, response2, model2],
66
  outputs=[dataset_state, dataset_status]
67
  )
68
+
69
  # Analysis Tab
70
  with gr.Tab("Analysis"):
71
+ # Create analysis screen
72
  analysis_components = create_analysis_screen()
73
  analysis_options = analysis_components[0]
74
  analysis_params = analysis_components[1]
 
77
  ngram_n = analysis_components[4]
78
  ngram_top = analysis_components[5]
79
  topic_count = analysis_components[6]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
80
 
81
+ # Create visualization components
82
+ visualization_components = create_visualization_components()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
83
 
84
+ # Connect the run button to the analysis function
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
85
  run_analysis_btn.click(
86
  fn=run_analysis,
87
  inputs=[dataset_state, analysis_options, ngram_n, ngram_top, topic_count],
88
+ outputs=visualization_components
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
89
  )
90
+
91
  return app
92
 
93
+
94
  if __name__ == "__main__":
95
  # Download required NLTK resources before launching the app
96
  download_nltk_resources()
97
+
98
  logger.info("Starting LLM Response Comparator application")
99
  logger.info("===== Application Startup =====")
100
+
101
  # Create and launch the application
102
  app = create_app()
103
  app.launch()
data_handler.py ADDED
@@ -0,0 +1,49 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import nltk
3
+ import logging
4
+
5
+ # Set up logging
6
+ logger = logging.getLogger('gradio_app.data_handler')
7
+
8
+
9
+ def download_nltk_resources():
10
+ """Download required NLTK resources if not already downloaded"""
11
+ try:
12
+ # Create nltk_data directory in the user's home directory if it doesn't exist
13
+ nltk_data_path = os.path.expanduser("~/nltk_data")
14
+ os.makedirs(nltk_data_path, exist_ok=True)
15
+
16
+ # Add this path to NLTK's data path
17
+ nltk.data.path.append(nltk_data_path)
18
+
19
+ # Download required resources
20
+ resources = ['punkt', 'wordnet', 'stopwords', 'vader_lexicon']
21
+ for resource in resources:
22
+ try:
23
+ # Different resources can be in different directories in NLTK
24
+ locations = [
25
+ f'tokenizers/{resource}',
26
+ f'corpora/{resource}',
27
+ f'taggers/{resource}',
28
+ f'{resource}'
29
+ ]
30
+
31
+ found = False
32
+ for location in locations:
33
+ try:
34
+ nltk.data.find(location)
35
+ logger.info(f"Resource {resource} already downloaded")
36
+ found = True
37
+ break
38
+ except LookupError:
39
+ continue
40
+
41
+ if not found:
42
+ logger.info(f"Downloading {resource}...")
43
+ nltk.download(resource, quiet=True)
44
+ except Exception as e:
45
+ logger.error(f"Error with resource {resource}: {e}")
46
+
47
+ logger.info("NLTK resources check completed")
48
+ except Exception as e:
49
+ logger.error(f"Error downloading NLTK resources: {e}")
processors/bias_processor.py ADDED
@@ -0,0 +1,72 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+ import logging
3
+ import traceback
4
+
5
+ # Set up logging
6
+ logger = logging.getLogger('gradio_app.processors.bias')
7
+
8
+
9
+ def process_bias_detection(analysis_results, prompt, analyses):
10
+ """
11
+ Process Bias Detection analysis and return UI updates
12
+
13
+ Args:
14
+ analysis_results (dict): Complete analysis results
15
+ prompt (str): The prompt being analyzed
16
+ analyses (dict): Analysis data for the prompt
17
+
18
+ Returns:
19
+ tuple: UI component updates
20
+ """
21
+ logger.info("Processing Bias Detection visualization")
22
+
23
+ models = analyses["bias_detection"].get("models", ["Model 1", "Model 2"])
24
+ logger.info(f"Bias models: {models}")
25
+
26
+ try:
27
+ # Create bias visualization components
28
+ from visualization.bias_visualizer import process_and_visualize_bias_analysis
29
+ bias_components = process_and_visualize_bias_analysis(analysis_results)
30
+
31
+ logger.info(f"Created {len(bias_components)} bias visualization components")
32
+
33
+ return (
34
+ analysis_results, # analysis_results_state
35
+ False, # analysis_output visibility
36
+ True, # visualization_area_visible
37
+ gr.update(visible=True), # analysis_title
38
+ gr.update(visible=True, value=f"## Analysis of Prompt: \"{prompt[:100]}...\""), # prompt_title
39
+ gr.update(visible=True, value=f"### Comparing responses from {models[0]} and {models[1]}"),
40
+ # models_compared
41
+ gr.update(visible=True, value="#### Bias detection visualization is available below"), # model1_title
42
+ gr.update(visible=True,
43
+ value="The detailed bias analysis includes sentiment analysis, partisan term detection, and framing analysis."),
44
+ # model1_words
45
+ gr.update(visible=False), # model2_title
46
+ gr.update(visible=False), # model2_words
47
+ gr.update(visible=False), # similarity_metrics_title
48
+ gr.update(visible=False), # similarity_metrics
49
+ False, # status_message_visible
50
+ gr.update(visible=False), # status_message
51
+ gr.update(visible=True) # bias_visualizations - Make this visible
52
+ )
53
+ except Exception as e:
54
+ logger.error(f"Error generating bias visualization: {str(e)}\n{traceback.format_exc()}")
55
+
56
+ return (
57
+ analysis_results,
58
+ True, # Show raw JSON for debugging
59
+ False,
60
+ gr.update(visible=False),
61
+ gr.update(visible=False),
62
+ gr.update(visible=False),
63
+ gr.update(visible=False),
64
+ gr.update(visible=False),
65
+ gr.update(visible=False),
66
+ gr.update(visible=False),
67
+ gr.update(visible=False),
68
+ gr.update(visible=False),
69
+ True,
70
+ gr.update(visible=True, value=f"❌ **Error generating bias visualization:** {str(e)}"),
71
+ gr.update(visible=False) # bias_visualizations
72
+ )
processors/bow_processor.py ADDED
@@ -0,0 +1,107 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+ import logging
3
+
4
+ # Set up logging
5
+ logger = logging.getLogger('gradio_app.processors.bow')
6
+
7
+
8
+ def process_bow_analysis(analysis_results, prompt, analyses):
9
+ """
10
+ Process Bag of Words analysis and return UI updates
11
+
12
+ Args:
13
+ analysis_results (dict): Complete analysis results
14
+ prompt (str): The prompt being analyzed
15
+ analyses (dict): Analysis data for the prompt
16
+
17
+ Returns:
18
+ tuple: UI component updates
19
+ """
20
+ visualization_area_visible = True
21
+ bow_results = analyses["bag_of_words"]
22
+ models = bow_results.get("models", [])
23
+
24
+ if len(models) < 2:
25
+ from analysis_runner import default_no_visualization
26
+ return default_no_visualization(analysis_results)
27
+
28
+ prompt_title_visible = True
29
+ prompt_title_value = f"## Analysis of Prompt: \"{prompt[:100]}...\""
30
+
31
+ models_compared_visible = True
32
+ models_compared_value = f"### Comparing responses from {models[0]} and {models[1]}"
33
+
34
+ # Extract and format information for display
35
+ model1_name = models[0]
36
+ model2_name = models[1]
37
+
38
+ # Format important words for each model
39
+ important_words = bow_results.get("important_words", {})
40
+
41
+ model1_title_visible = False
42
+ model1_title_value = ""
43
+ model1_words_visible = False
44
+ model1_words_value = ""
45
+
46
+ if model1_name in important_words:
47
+ model1_title_visible = True
48
+ model1_title_value = f"#### Top Words Used by {model1_name}"
49
+
50
+ word_list = [f"**{item['word']}** ({item['count']})" for item in important_words[model1_name][:10]]
51
+ model1_words_visible = True
52
+ model1_words_value = ", ".join(word_list)
53
+
54
+ model2_title_visible = False
55
+ model2_title_value = ""
56
+ model2_words_visible = False
57
+ model2_words_value = ""
58
+
59
+ if model2_name in important_words:
60
+ model2_title_visible = True
61
+ model2_title_value = f"#### Top Words Used by {model2_name}"
62
+
63
+ word_list = [f"**{item['word']}** ({item['count']})" for item in important_words[model2_name][:10]]
64
+ model2_words_visible = True
65
+ model2_words_value = ", ".join(word_list)
66
+
67
+ similarity_title_visible = False
68
+ similarity_metrics_visible = False
69
+ similarity_metrics_value = ""
70
+
71
+ # Format similarity metrics
72
+ comparisons = bow_results.get("comparisons", {})
73
+ comparison_key = f"{model1_name} vs {model2_name}"
74
+
75
+ if comparison_key in comparisons:
76
+ metrics = comparisons[comparison_key]
77
+ cosine = metrics.get("cosine_similarity", 0)
78
+ jaccard = metrics.get("jaccard_similarity", 0)
79
+ semantic = metrics.get("semantic_similarity", 0)
80
+ common_words = metrics.get("common_word_count", 0)
81
+
82
+ similarity_title_visible = True
83
+ similarity_metrics_visible = True
84
+ similarity_metrics_value = f"""
85
+ - **Cosine Similarity**: {cosine:.2f} (higher means more similar word frequency patterns)
86
+ - **Jaccard Similarity**: {jaccard:.2f} (higher means more word overlap)
87
+ - **Semantic Similarity**: {semantic:.2f} (higher means more similar meaning)
88
+ - **Common Words**: {common_words} words appear in both responses
89
+ """
90
+
91
+ return (
92
+ analysis_results, # analysis_results_state
93
+ False, # analysis_output visibility
94
+ True, # visualization_area_visible
95
+ gr.update(visible=True), # analysis_title
96
+ gr.update(visible=prompt_title_visible, value=prompt_title_value), # prompt_title
97
+ gr.update(visible=models_compared_visible, value=models_compared_value), # models_compared
98
+ gr.update(visible=model1_title_visible, value=model1_title_value), # model1_title
99
+ gr.update(visible=model1_words_visible, value=model1_words_value), # model1_words
100
+ gr.update(visible=model2_title_visible, value=model2_title_value), # model2_title
101
+ gr.update(visible=model2_words_visible, value=model2_words_value), # model2_words
102
+ gr.update(visible=similarity_title_visible), # similarity_metrics_title
103
+ gr.update(visible=similarity_metrics_visible, value=similarity_metrics_value), # similarity_metrics
104
+ False, # status_message_visible
105
+ gr.update(visible=False), # status_message
106
+ gr.update(visible=False) # bias_visualizations - Not visible for BoW analysis
107
+ )
processors/classifier_processor.py ADDED
@@ -0,0 +1,99 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+ import logging
3
+
4
+ # Set up logging
5
+ logger = logging.getLogger('gradio_app.processors.classifier')
6
+
7
+
8
+ def process_classifier_analysis(analysis_results, prompt, analyses):
9
+ """
10
+ Process Classifier analysis and return UI updates
11
+
12
+ Args:
13
+ analysis_results (dict): Complete analysis results
14
+ prompt (str): The prompt being analyzed
15
+ analyses (dict): Analysis data for the prompt
16
+
17
+ Returns:
18
+ tuple: UI component updates
19
+ """
20
+ visualization_area_visible = True
21
+ classifier_results = analyses["classifier"]
22
+ models = classifier_results.get("models", [])
23
+
24
+ if len(models) < 2:
25
+ from analysis_runner import default_no_visualization
26
+ return default_no_visualization(analysis_results)
27
+
28
+ prompt_title_visible = True
29
+ prompt_title_value = f"## Analysis of Prompt: \"{prompt[:100]}...\""
30
+
31
+ models_compared_visible = True
32
+ models_compared_value = f"### Classifier Analysis for {models[0]} and {models[1]}"
33
+
34
+ # Extract and format classifier information
35
+ model1_name = models[0]
36
+ model2_name = models[1]
37
+
38
+ # Display classifications for each model
39
+ classifications = classifier_results.get("classifications", {})
40
+
41
+ model1_title_visible = False
42
+ model1_title_value = ""
43
+ model1_words_visible = False
44
+ model1_words_value = ""
45
+
46
+ if classifications:
47
+ model1_title_visible = True
48
+ model1_title_value = f"#### Classification Results"
49
+ model1_words_visible = True
50
+
51
+ model1_results = classifications.get(model1_name, {})
52
+ model2_results = classifications.get(model2_name, {})
53
+
54
+ model1_words_value = f"""
55
+ **{model1_name}**:
56
+ - Formality: {model1_results.get('formality', 'N/A')}
57
+ - Sentiment: {model1_results.get('sentiment', 'N/A')}
58
+ - Complexity: {model1_results.get('complexity', 'N/A')}
59
+
60
+ **{model2_name}**:
61
+ - Formality: {model2_results.get('formality', 'N/A')}
62
+ - Sentiment: {model2_results.get('sentiment', 'N/A')}
63
+ - Complexity: {model2_results.get('complexity', 'N/A')}
64
+ """
65
+
66
+ # Show comparison
67
+ model2_title_visible = False
68
+ model2_title_value = ""
69
+ model2_words_visible = False
70
+ model2_words_value = ""
71
+
72
+ differences = classifier_results.get("differences", {})
73
+ if differences:
74
+ model2_title_visible = True
75
+ model2_title_value = f"#### Classification Comparison"
76
+ model2_words_visible = True
77
+
78
+ model2_words_value = "\n".join([
79
+ f"- **{category}**: {diff}"
80
+ for category, diff in differences.items()
81
+ ])
82
+
83
+ return (
84
+ analysis_results, # analysis_results_state
85
+ False, # analysis_output visibility
86
+ True, # visualization_area_visible
87
+ gr.update(visible=True), # analysis_title
88
+ gr.update(visible=prompt_title_visible, value=prompt_title_value), # prompt_title
89
+ gr.update(visible=models_compared_visible, value=models_compared_value), # models_compared
90
+ gr.update(visible=model1_title_visible, value=model1_title_value), # model1_title
91
+ gr.update(visible=model1_words_visible, value=model1_words_value), # model1_words
92
+ gr.update(visible=model2_title_visible, value=model2_title_value), # model2_title
93
+ gr.update(visible=model2_words_visible, value=model2_words_value), # model2_words
94
+ gr.update(visible=False), # similarity_metrics_title
95
+ gr.update(visible=False), # similarity_metrics
96
+ False, # status_message_visible
97
+ gr.update(visible=False), # status_message
98
+ gr.update(visible=False) # bias_visualizations - Not visible for Classifier analysis
99
+ )
processors/ngram_processor.py ADDED
@@ -0,0 +1,103 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+ import logging
3
+
4
+ # Set up logging
5
+ logger = logging.getLogger('gradio_app.processors.ngram')
6
+
7
+
8
+ def process_ngram_analysis(analysis_results, prompt, analyses):
9
+ """
10
+ Process N-gram analysis and return UI updates
11
+
12
+ Args:
13
+ analysis_results (dict): Complete analysis results
14
+ prompt (str): The prompt being analyzed
15
+ analyses (dict): Analysis data for the prompt
16
+
17
+ Returns:
18
+ tuple: UI component updates
19
+ """
20
+ visualization_area_visible = True
21
+ ngram_results = analyses["ngram_analysis"]
22
+ models = ngram_results.get("models", [])
23
+ ngram_size = ngram_results.get("ngram_size", 2)
24
+ size_name = "Unigrams" if ngram_size == 1 else f"{ngram_size}-grams"
25
+
26
+ if len(models) < 2:
27
+ from analysis_runner import default_no_visualization
28
+ return default_no_visualization(analysis_results)
29
+
30
+ prompt_title_visible = True
31
+ prompt_title_value = f"## Analysis of Prompt: \"{prompt[:100]}...\""
32
+
33
+ models_compared_visible = True
34
+ models_compared_value = f"### {size_name} Analysis: Comparing responses from {models[0]} and {models[1]}"
35
+
36
+ # Extract and format information for display
37
+ model1_name = models[0]
38
+ model2_name = models[1]
39
+
40
+ # Format important n-grams for each model
41
+ important_ngrams = ngram_results.get("important_ngrams", {})
42
+
43
+ model1_title_visible = False
44
+ model1_title_value = ""
45
+ model1_words_visible = False
46
+ model1_words_value = ""
47
+
48
+ if model1_name in important_ngrams:
49
+ model1_title_visible = True
50
+ model1_title_value = f"#### Top {size_name} Used by {model1_name}"
51
+
52
+ ngram_list = [f"**{item['ngram']}** ({item['count']})" for item in important_ngrams[model1_name][:10]]
53
+ model1_words_visible = True
54
+ model1_words_value = ", ".join(ngram_list)
55
+
56
+ model2_title_visible = False
57
+ model2_title_value = ""
58
+ model2_words_visible = False
59
+ model2_words_value = ""
60
+
61
+ if model2_name in important_ngrams:
62
+ model2_title_visible = True
63
+ model2_title_value = f"#### Top {size_name} Used by {model2_name}"
64
+
65
+ ngram_list = [f"**{item['ngram']}** ({item['count']})" for item in important_ngrams[model2_name][:10]]
66
+ model2_words_visible = True
67
+ model2_words_value = ", ".join(ngram_list)
68
+
69
+ similarity_title_visible = False
70
+ similarity_metrics_visible = False
71
+ similarity_metrics_value = ""
72
+
73
+ # Format similarity metrics if available
74
+ if "comparisons" in ngram_results:
75
+ comparison_key = f"{model1_name} vs {model2_name}"
76
+
77
+ if comparison_key in ngram_results["comparisons"]:
78
+ metrics = ngram_results["comparisons"][comparison_key]
79
+ common_count = metrics.get("common_ngram_count", 0)
80
+
81
+ similarity_title_visible = True
82
+ similarity_metrics_visible = True
83
+ similarity_metrics_value = f"""
84
+ - **Common {size_name}**: {common_count} {size_name.lower()} appear in both responses
85
+ """
86
+
87
+ return (
88
+ analysis_results, # analysis_results_state
89
+ False, # analysis_output visibility
90
+ True, # visualization_area_visible
91
+ gr.update(visible=True), # analysis_title
92
+ gr.update(visible=prompt_title_visible, value=prompt_title_value), # prompt_title
93
+ gr.update(visible=models_compared_visible, value=models_compared_value), # models_compared
94
+ gr.update(visible=model1_title_visible, value=model1_title_value), # model1_title
95
+ gr.update(visible=model1_words_visible, value=model1_words_value), # model1_words
96
+ gr.update(visible=model2_title_visible, value=model2_title_value), # model2_title
97
+ gr.update(visible=model2_words_visible, value=model2_words_value), # model2_words
98
+ gr.update(visible=similarity_title_visible), # similarity_metrics_title
99
+ gr.update(visible=similarity_metrics_visible, value=similarity_metrics_value), # similarity_metrics
100
+ False, # status_message_visible
101
+ gr.update(visible=False), # status_message
102
+ gr.update(visible=False) # bias_visualizations - Not visible for N-gram analysis
103
+ )
processors/topic_processor.py ADDED
@@ -0,0 +1,156 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+ import logging
3
+
4
+ # Set up logging
5
+ logger = logging.getLogger('gradio_app.processors.topic')
6
+
7
+
8
+ def process_topic_modeling(analysis_results, prompt, analyses):
9
+ """
10
+ Process Topic Modeling analysis and return UI updates
11
+
12
+ Args:
13
+ analysis_results (dict): Complete analysis results
14
+ prompt (str): The prompt being analyzed
15
+ analyses (dict): Analysis data for the prompt
16
+
17
+ Returns:
18
+ tuple: UI component updates
19
+ """
20
+ topic_results = analyses["topic_modeling"]
21
+
22
+ # Check for errors in topic modeling
23
+ if "error" in topic_results:
24
+ return (
25
+ analysis_results,
26
+ False, # Don't show raw JSON
27
+ False, # Don't show visualization area
28
+ gr.update(visible=False),
29
+ gr.update(visible=False),
30
+ gr.update(visible=False),
31
+ gr.update(visible=False),
32
+ gr.update(visible=False),
33
+ gr.update(visible=False),
34
+ gr.update(visible=False),
35
+ gr.update(visible=False),
36
+ gr.update(visible=False),
37
+ True, # Show status message
38
+ gr.update(visible=True, value=f"❌ **Topic modeling error:** {topic_results['error']}"),
39
+ gr.update(visible=False) # bias_visualizations
40
+ )
41
+
42
+ visualization_area_visible = True
43
+ models = topic_results.get("models", [])
44
+ method = topic_results.get("method", "lda").upper()
45
+ n_topics = topic_results.get("n_topics", 3)
46
+
47
+ if len(models) < 2:
48
+ from analysis_runner import default_no_visualization
49
+ return default_no_visualization(analysis_results)
50
+
51
+ prompt_title_visible = True
52
+ prompt_title_value = f"## Analysis of Prompt: \"{prompt[:100]}...\""
53
+
54
+ models_compared_visible = True
55
+ models_compared_value = f"### Topic Modeling Analysis ({method}, {n_topics} topics)"
56
+
57
+ # Initialize component visibility and values
58
+ model1_title_visible = False
59
+ model1_title_value = ""
60
+ model1_words_visible = False
61
+ model1_words_value = ""
62
+ model2_title_visible = False
63
+ model2_title_value = ""
64
+ model2_words_visible = False
65
+ model2_words_value = ""
66
+ similarity_title_visible = False
67
+ similarity_metrics_visible = False
68
+ similarity_metrics_value = ""
69
+
70
+ # Extract and format topic information
71
+ topics = topic_results.get("topics", [])
72
+
73
+ if topics:
74
+ # Format topic info for display
75
+ topic_info = []
76
+ for topic in topics[:5]: # Show first 5 topics
77
+ topic_id = topic.get("id", 0)
78
+ words = topic.get("words", [])[:5] # Top 5 words per topic
79
+
80
+ if words:
81
+ topic_info.append(f"**Topic {topic_id + 1}**: {', '.join(words)}")
82
+
83
+ if topic_info:
84
+ model1_title_visible = True
85
+ model1_title_value = "#### Discovered Topics"
86
+ model1_words_visible = True
87
+ model1_words_value = "\n".join(topic_info)
88
+
89
+ # Get topic distributions for models
90
+ model_topics = topic_results.get("model_topics", {})
91
+
92
+ if model_topics:
93
+ model1_name = models[0]
94
+ model2_name = models[1]
95
+
96
+ # Format topic distribution info
97
+ if model1_name in model_topics and model2_name in model_topics:
98
+ model2_title_visible = True
99
+ model2_title_value = "#### Topic Distribution"
100
+ model2_words_visible = True
101
+
102
+ # Simple distribution display
103
+ dist1 = model_topics[model1_name]
104
+ dist2 = model_topics[model2_name]
105
+
106
+ model2_words_value = f"""
107
+ **{model1_name}**: {', '.join([f"Topic {i + 1}: {v:.2f}" for i, v in enumerate(dist1[:5])])}
108
+
109
+ **{model2_name}**: {', '.join([f"Topic {i + 1}: {v:.2f}" for i, v in enumerate(dist2[:5])])}
110
+ """
111
+
112
+ # Add similarity metrics if available
113
+ comparisons = topic_results.get("comparisons", {})
114
+ if comparisons:
115
+ comparison_key = f"{model1_name} vs {model2_name}"
116
+
117
+ if comparison_key in comparisons:
118
+ metrics = comparisons[comparison_key]
119
+ js_div = metrics.get("js_divergence", 0)
120
+
121
+ # Add interpretation
122
+ similarity_text = ""
123
+ if js_div < 0.2:
124
+ similarity_text = "very similar"
125
+ elif js_div < 0.4:
126
+ similarity_text = "somewhat similar"
127
+ elif js_div < 0.6:
128
+ similarity_text = "moderately different"
129
+ else:
130
+ similarity_text = "very different"
131
+
132
+ similarity_title_visible = True
133
+ similarity_metrics_visible = True
134
+ similarity_metrics_value = f"""
135
+ - **Topic Distribution Divergence**: {js_div:.4f}
136
+ - The topic distributions between models are **{similarity_text}**
137
+ - *Lower divergence values indicate more similar topic distributions*
138
+ """
139
+
140
+ return (
141
+ analysis_results, # analysis_results_state
142
+ False, # analysis_output visibility
143
+ True, # visualization_area_visible
144
+ gr.update(visible=True), # analysis_title
145
+ gr.update(visible=prompt_title_visible, value=prompt_title_value), # prompt_title
146
+ gr.update(visible=models_compared_visible, value=models_compared_value), # models_compared
147
+ gr.update(visible=model1_title_visible, value=model1_title_value), # model1_title
148
+ gr.update(visible=model1_words_visible, value=model1_words_value), # model1_words
149
+ gr.update(visible=model2_title_visible, value=model2_title_value), # model2_title
150
+ gr.update(visible=model2_words_visible, value=model2_words_value), # model2_words
151
+ gr.update(visible=similarity_title_visible), # similarity_metrics_title
152
+ gr.update(visible=similarity_metrics_visible, value=similarity_metrics_value), # similarity_metrics
153
+ False, # status_message_visible
154
+ gr.update(visible=False), # status_message
155
+ gr.update(visible=False) # bias_visualizations - Not visible for Topic Modeling
156
+ )
visualization_handler.py ADDED
@@ -0,0 +1,73 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+ import logging
3
+
4
+ # Set up logging
5
+ logger = logging.getLogger('gradio_app.visualization_handler')
6
+
7
+
8
+ def create_visualization_components():
9
+ """
10
+ Creates all the visualization components used in the analysis tab
11
+
12
+ Returns:
13
+ list: A list of all gradio components for visualization
14
+ """
15
+ # Pre-create visualization components (initially hidden)
16
+ visualization_area_visible = gr.Checkbox(value=False, visible=False, label="Visualization Visible")
17
+ analysis_title = gr.Markdown("## Analysis Results", visible=False)
18
+ prompt_title = gr.Markdown(visible=False)
19
+ models_compared = gr.Markdown(visible=False)
20
+
21
+ # Container for model 1 words
22
+ model1_title = gr.Markdown(visible=False)
23
+ model1_words = gr.Markdown(visible=False)
24
+
25
+ # Container for model 2 words
26
+ model2_title = gr.Markdown(visible=False)
27
+ model2_words = gr.Markdown(visible=False)
28
+
29
+ # Similarity metrics
30
+ similarity_metrics_title = gr.Markdown("### Similarity Metrics", visible=False)
31
+ similarity_metrics = gr.Markdown(visible=False)
32
+
33
+ # Status or error message area
34
+ status_message_visible = gr.Checkbox(value=False, visible=False, label="Status Message Visible")
35
+ status_message = gr.Markdown(visible=False)
36
+
37
+ # Create bias visualization container (initially hidden)
38
+ with gr.Column(visible=False) as bias_visualizations:
39
+ gr.Markdown("### Bias Analysis Visualizations")
40
+ # This will be populated dynamically
41
+
42
+ # Return all components as a list
43
+ return [
44
+ analysis_results_state := gr.State({}),
45
+ analysis_output := gr.JSON(visible=False),
46
+ visualization_area_visible,
47
+ analysis_title,
48
+ prompt_title,
49
+ models_compared,
50
+ model1_title,
51
+ model1_words,
52
+ model2_title,
53
+ model2_words,
54
+ similarity_metrics_title,
55
+ similarity_metrics,
56
+ status_message_visible,
57
+ status_message,
58
+ bias_visualizations
59
+ ]
60
+
61
+
62
+ def process_and_visualize_bias_analysis(analysis_results):
63
+ """
64
+ Wrapper for bias visualization function from visualization.bias_visualizer
65
+
66
+ Args:
67
+ analysis_results (dict): The analysis results
68
+
69
+ Returns:
70
+ list: Components for bias visualization
71
+ """
72
+ from visualization.bias_visualizer import process_and_visualize_bias_analysis
73
+ return process_and_visualize_bias_analysis(analysis_results)