Spaces:
Sleeping
Sleeping
Ryan
commited on
Commit
·
e66f533
1
Parent(s):
d7de222
update
Browse files- .DS_Store +0 -0
- .idea/workspace.xml +18 -2
- analysis_runner.py +196 -0
- app.py +22 -610
- data_handler.py +49 -0
- processors/bias_processor.py +72 -0
- processors/bow_processor.py +107 -0
- processors/classifier_processor.py +99 -0
- processors/ngram_processor.py +103 -0
- processors/topic_processor.py +156 -0
- visualization_handler.py +73 -0
.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
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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="
|
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 |
-
#
|
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 |
-
|
309 |
-
|
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 |
-
|
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)
|