aimodelshare 0.1.29__py3-none-any.whl → 0.1.64__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of aimodelshare might be problematic. Click here for more details.
- aimodelshare/__init__.py +94 -14
- aimodelshare/aimsonnx.py +417 -262
- aimodelshare/api.py +13 -12
- aimodelshare/auth.py +163 -0
- aimodelshare/aws.py +4 -4
- aimodelshare/base_image.py +1 -1
- aimodelshare/containerisation.py +1 -1
- aimodelshare/data_sharing/download_data.py +103 -70
- aimodelshare/generatemodelapi.py +7 -6
- aimodelshare/main/authorization.txt +275 -275
- aimodelshare/main/eval_lambda.txt +81 -13
- aimodelshare/model.py +493 -197
- aimodelshare/modeluser.py +89 -1
- aimodelshare/moral_compass/README.md +408 -0
- aimodelshare/moral_compass/__init__.py +37 -0
- aimodelshare/moral_compass/_version.py +3 -0
- aimodelshare/moral_compass/api_client.py +601 -0
- aimodelshare/moral_compass/apps/__init__.py +26 -0
- aimodelshare/moral_compass/apps/ai_consequences.py +297 -0
- aimodelshare/moral_compass/apps/judge.py +299 -0
- aimodelshare/moral_compass/apps/tutorial.py +198 -0
- aimodelshare/moral_compass/apps/what_is_ai.py +426 -0
- aimodelshare/moral_compass/challenge.py +365 -0
- aimodelshare/moral_compass/config.py +187 -0
- aimodelshare/playground.py +26 -14
- aimodelshare/preprocessormodules.py +60 -6
- aimodelshare/pyspark/authorization.txt +258 -258
- aimodelshare/pyspark/eval_lambda.txt +1 -1
- aimodelshare/reproducibility.py +20 -5
- aimodelshare/utils/__init__.py +78 -0
- aimodelshare/utils/optional_deps.py +38 -0
- aimodelshare-0.1.64.dist-info/METADATA +298 -0
- {aimodelshare-0.1.29.dist-info → aimodelshare-0.1.64.dist-info}/RECORD +36 -25
- {aimodelshare-0.1.29.dist-info → aimodelshare-0.1.64.dist-info}/WHEEL +1 -1
- aimodelshare-0.1.64.dist-info/licenses/LICENSE +5 -0
- {aimodelshare-0.1.29.dist-info → aimodelshare-0.1.64.dist-info}/top_level.txt +0 -1
- aimodelshare-0.1.29.dist-info/METADATA +0 -78
- aimodelshare-0.1.29.dist-info/licenses/LICENSE +0 -22
- tests/__init__.py +0 -0
- tests/test_aimsonnx.py +0 -135
- tests/test_playground.py +0 -721
|
@@ -0,0 +1,297 @@
|
|
|
1
|
+
"""
|
|
2
|
+
AI Consequences - Gradio application for the Justice & Equity Challenge.
|
|
3
|
+
|
|
4
|
+
This app teaches:
|
|
5
|
+
1. The consequences of wrong AI predictions in criminal justice
|
|
6
|
+
2. Understanding false positives and false negatives
|
|
7
|
+
3. The ethical stakes of relying on AI for high-stakes decisions
|
|
8
|
+
|
|
9
|
+
Structure:
|
|
10
|
+
- Factory function `create_ai_consequences_app()` returns a Gradio Blocks object
|
|
11
|
+
- Convenience wrapper `launch_ai_consequences_app()` launches it inline (for notebooks)
|
|
12
|
+
"""
|
|
13
|
+
import contextlib
|
|
14
|
+
import os
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def create_ai_consequences_app(theme_primary_hue: str = "indigo") -> "gr.Blocks":
|
|
18
|
+
"""Create the AI Consequences Gradio Blocks app (not launched yet)."""
|
|
19
|
+
try:
|
|
20
|
+
import gradio as gr
|
|
21
|
+
except ImportError as e:
|
|
22
|
+
raise ImportError(
|
|
23
|
+
"Gradio is required for the AI consequences app. Install with `pip install gradio`."
|
|
24
|
+
) from e
|
|
25
|
+
|
|
26
|
+
css = """
|
|
27
|
+
.large-text {
|
|
28
|
+
font-size: 20px !important;
|
|
29
|
+
}
|
|
30
|
+
.warning-box {
|
|
31
|
+
background: #fef2f2 !important;
|
|
32
|
+
border-left: 6px solid #dc2626 !important;
|
|
33
|
+
}
|
|
34
|
+
"""
|
|
35
|
+
|
|
36
|
+
with gr.Blocks(theme=gr.themes.Soft(primary_hue=theme_primary_hue), css=css) as demo:
|
|
37
|
+
gr.Markdown("<h1 style='text-align:center;'>⚠️ What If the AI Was Wrong?</h1>")
|
|
38
|
+
gr.Markdown(
|
|
39
|
+
"""
|
|
40
|
+
<div style='text-align:center; font-size:18px; max-width: 900px; margin: auto;
|
|
41
|
+
padding: 20px; background-color: #fef2f2; border-radius: 12px; border: 2px solid #dc2626;'>
|
|
42
|
+
You just made decisions based on an AI's predictions.<br>
|
|
43
|
+
But AI systems are not perfect. Let's explore what happens when they make mistakes.
|
|
44
|
+
</div>
|
|
45
|
+
"""
|
|
46
|
+
)
|
|
47
|
+
gr.HTML("<hr style='margin:24px 0;'>")
|
|
48
|
+
|
|
49
|
+
# Step 1: Introduction
|
|
50
|
+
with gr.Column(visible=True) as step_1:
|
|
51
|
+
gr.Markdown("<h2 style='text-align:center;'>The Stakes of AI Predictions</h2>")
|
|
52
|
+
gr.Markdown(
|
|
53
|
+
"""
|
|
54
|
+
<div style='font-size: 20px; background:#dbeafe; padding:28px; border-radius:16px;'>
|
|
55
|
+
<p>In the previous exercise, you relied on an AI system to predict which defendants
|
|
56
|
+
were at <b>High</b>, <b>Medium</b>, or <b>Low</b> risk of re-offending.</p>
|
|
57
|
+
|
|
58
|
+
<p style='margin-top:20px;'><b>But what if those predictions were incorrect?</b></p>
|
|
59
|
+
|
|
60
|
+
<p style='margin-top:20px;'>AI systems make two types of errors that have very different consequences:</p>
|
|
61
|
+
|
|
62
|
+
<ul style='font-size:18px; margin-top:12px;'>
|
|
63
|
+
<li><b>False Positives</b> - Incorrectly predicting HIGH risk</li>
|
|
64
|
+
<li><b>False Negatives</b> - Incorrectly predicting LOW risk</li>
|
|
65
|
+
</ul>
|
|
66
|
+
|
|
67
|
+
<p style='margin-top:20px;'>Let's examine each type of error and its real-world impact.</p>
|
|
68
|
+
</div>
|
|
69
|
+
"""
|
|
70
|
+
)
|
|
71
|
+
step_1_next = gr.Button("Next: False Positives ▶️", variant="primary", size="lg")
|
|
72
|
+
|
|
73
|
+
# Step 2: False Positives
|
|
74
|
+
with gr.Column(visible=False) as step_2:
|
|
75
|
+
gr.Markdown("<h2 style='text-align:center;'>🔴 False Positives: Predicting Danger Where None Exists</h2>")
|
|
76
|
+
gr.Markdown(
|
|
77
|
+
"""
|
|
78
|
+
<div style='font-size: 20px; background:#fef3c7; padding:28px; border-radius:16px; border: 3px solid #f59e0b;'>
|
|
79
|
+
<h3 style='color:#b45309; margin-top:0;'>What is a False Positive?</h3>
|
|
80
|
+
|
|
81
|
+
<p>A <b>false positive</b> occurs when the AI predicts someone is <b style='color:#dc2626;'>HIGH RISK</b>,
|
|
82
|
+
but they would NOT have actually re-offended if released.</p>
|
|
83
|
+
|
|
84
|
+
<div style='background:white; padding:20px; border-radius:8px; margin:20px 0;'>
|
|
85
|
+
<h4 style='margin-top:0;'>Example Scenario:</h4>
|
|
86
|
+
<p style='font-size:18px;'>
|
|
87
|
+
• Sarah was flagged as <b style='color:#dc2626;'>HIGH RISK</b><br>
|
|
88
|
+
• Based on this, the judge kept her in prison<br>
|
|
89
|
+
• In reality, Sarah would have rebuilt her life and never committed another crime
|
|
90
|
+
</p>
|
|
91
|
+
</div>
|
|
92
|
+
|
|
93
|
+
<h3 style='color:#b45309;'>The Human Cost:</h3>
|
|
94
|
+
<ul style='font-size:18px;'>
|
|
95
|
+
<li>Innocent people spend unnecessary time in prison</li>
|
|
96
|
+
<li>Families are separated for longer than needed</li>
|
|
97
|
+
<li>Job opportunities and rehabilitation are delayed</li>
|
|
98
|
+
<li>Trust in the justice system erodes</li>
|
|
99
|
+
<li>Disproportionate impact on marginalized communities</li>
|
|
100
|
+
</ul>
|
|
101
|
+
|
|
102
|
+
<div style='background:#fef2f2; padding:16px; border-radius:8px; margin-top:20px; border-left:6px solid #dc2626;'>
|
|
103
|
+
<p style='font-size:18px; margin:0;'><b>Key Point:</b> False positives mean the AI is being
|
|
104
|
+
<b>too cautious</b>, keeping people locked up who should be free.</p>
|
|
105
|
+
</div>
|
|
106
|
+
</div>
|
|
107
|
+
"""
|
|
108
|
+
)
|
|
109
|
+
with gr.Row():
|
|
110
|
+
step_2_back = gr.Button("◀️ Back", size="lg")
|
|
111
|
+
step_2_next = gr.Button("Next: False Negatives ▶️", variant="primary", size="lg")
|
|
112
|
+
|
|
113
|
+
# Step 3: False Negatives
|
|
114
|
+
with gr.Column(visible=False) as step_3:
|
|
115
|
+
gr.Markdown("<h2 style='text-align:center;'>🔵 False Negatives: Missing Real Danger</h2>")
|
|
116
|
+
gr.Markdown(
|
|
117
|
+
"""
|
|
118
|
+
<div style='font-size: 20px; background:#f0fdf4; padding:28px; border-radius:16px; border: 3px solid #16a34a;'>
|
|
119
|
+
<h3 style='color:#15803d; margin-top:0;'>What is a False Negative?</h3>
|
|
120
|
+
|
|
121
|
+
<p>A <b>false negative</b> occurs when the AI predicts someone is <b style='color:#16a34a;'>LOW RISK</b>,
|
|
122
|
+
but they DO actually re-offend after being released.</p>
|
|
123
|
+
|
|
124
|
+
<div style='background:white; padding:20px; border-radius:8px; margin:20px 0;'>
|
|
125
|
+
<h4 style='margin-top:0;'>Example Scenario:</h4>
|
|
126
|
+
<p style='font-size:18px;'>
|
|
127
|
+
• James was flagged as <b style='color:#16a34a;'>LOW RISK</b><br>
|
|
128
|
+
• Based on this, the judge released him<br>
|
|
129
|
+
• Unfortunately, James did commit another serious crime
|
|
130
|
+
</p>
|
|
131
|
+
</div>
|
|
132
|
+
|
|
133
|
+
<h3 style='color:#15803d;'>The Human Cost:</h3>
|
|
134
|
+
<ul style='font-size:18px;'>
|
|
135
|
+
<li>New victims of preventable crimes</li>
|
|
136
|
+
<li>Loss of public trust in the justice system</li>
|
|
137
|
+
<li>Media scrutiny and backlash against judges</li>
|
|
138
|
+
<li>Political pressure to be "tough on crime"</li>
|
|
139
|
+
<li>Potential harm to communities and families</li>
|
|
140
|
+
</ul>
|
|
141
|
+
|
|
142
|
+
<div style='background:#fef2f2; padding:16px; border-radius:8px; margin-top:20px; border-left:6px solid #dc2626;'>
|
|
143
|
+
<p style='font-size:18px; margin:0;'><b>Key Point:</b> False negatives mean the AI is being
|
|
144
|
+
<b>too lenient</b>, releasing people who pose a real danger to society.</p>
|
|
145
|
+
</div>
|
|
146
|
+
</div>
|
|
147
|
+
"""
|
|
148
|
+
)
|
|
149
|
+
with gr.Row():
|
|
150
|
+
step_3_back = gr.Button("◀️ Back", size="lg")
|
|
151
|
+
step_3_next = gr.Button("Next: The Dilemma ▶️", variant="primary", size="lg")
|
|
152
|
+
|
|
153
|
+
# Step 4: The Dilemma
|
|
154
|
+
with gr.Column(visible=False) as step_4:
|
|
155
|
+
gr.Markdown("<h2 style='text-align:center;'>⚖️ The Impossible Balance</h2>")
|
|
156
|
+
gr.Markdown(
|
|
157
|
+
"""
|
|
158
|
+
<div style='font-size: 20px; background:#faf5ff; padding:28px; border-radius:16px; border: 3px solid #9333ea;'>
|
|
159
|
+
<h3 style='color:#7e22ce; margin-top:0;'>Every AI System Makes Trade-offs</h3>
|
|
160
|
+
|
|
161
|
+
<p>Here's the harsh reality: <b>No AI system can eliminate both types of errors.</b></p>
|
|
162
|
+
|
|
163
|
+
<div style='background:white; padding:24px; border-radius:12px; margin:24px 0;'>
|
|
164
|
+
<p style='font-size:18px; margin-bottom:16px;'><b>If you make the AI more cautious:</b></p>
|
|
165
|
+
<ul style='font-size:18px;'>
|
|
166
|
+
<li>✓ Fewer false negatives (fewer dangerous people released)</li>
|
|
167
|
+
<li>✗ More false positives (more innocent people kept in prison)</li>
|
|
168
|
+
</ul>
|
|
169
|
+
|
|
170
|
+
<hr style='margin:20px 0;'>
|
|
171
|
+
|
|
172
|
+
<p style='font-size:18px; margin-bottom:16px;'><b>If you make the AI more lenient:</b></p>
|
|
173
|
+
<ul style='font-size:18px;'>
|
|
174
|
+
<li>✓ Fewer false positives (more innocent people freed)</li>
|
|
175
|
+
<li>✗ More false negatives (more dangerous people released)</li>
|
|
176
|
+
</ul>
|
|
177
|
+
</div>
|
|
178
|
+
|
|
179
|
+
<h3 style='color:#7e22ce;'>The Ethical Question:</h3>
|
|
180
|
+
<div style='background:#fef2f2; padding:20px; border-radius:8px; border-left:6px solid #dc2626;'>
|
|
181
|
+
<p style='font-size:20px; font-weight:bold; margin:0;'>
|
|
182
|
+
Which mistake is worse?
|
|
183
|
+
</p>
|
|
184
|
+
<p style='font-size:18px; margin-top:12px; margin-bottom:0;'>
|
|
185
|
+
• Keeping innocent people in prison?<br>
|
|
186
|
+
• Or releasing dangerous individuals?
|
|
187
|
+
</p>
|
|
188
|
+
</div>
|
|
189
|
+
|
|
190
|
+
<p style='margin-top:24px; font-size:18px;'><b>There is no universally "correct" answer.</b>
|
|
191
|
+
Different societies, legal systems, and ethical frameworks weigh these trade-offs differently.</p>
|
|
192
|
+
|
|
193
|
+
<div style='background:#dbeafe; padding:16px; border-radius:8px; margin-top:20px;'>
|
|
194
|
+
<p style='font-size:18px; margin:0;'><b>This is why understanding AI is crucial.</b>
|
|
195
|
+
We need to know how these systems work so we can make informed decisions about when
|
|
196
|
+
and how to use them.</p>
|
|
197
|
+
</div>
|
|
198
|
+
</div>
|
|
199
|
+
"""
|
|
200
|
+
)
|
|
201
|
+
with gr.Row():
|
|
202
|
+
step_4_back = gr.Button("◀️ Back", size="lg")
|
|
203
|
+
step_4_next = gr.Button("Continue to Learn About AI ▶️", variant="primary", size="lg")
|
|
204
|
+
|
|
205
|
+
# Step 5: Completion
|
|
206
|
+
with gr.Column(visible=False) as step_5:
|
|
207
|
+
gr.Markdown(
|
|
208
|
+
"""
|
|
209
|
+
<div style='text-align:center;'>
|
|
210
|
+
<h2 style='font-size: 2.5rem;'>✅ Section Complete!</h2>
|
|
211
|
+
<div style='font-size: 1.3rem; background:#e0f2fe; padding:28px; border-radius:16px;
|
|
212
|
+
border: 2px solid #0284c7;'>
|
|
213
|
+
<p>You now understand the consequences of AI errors in high-stakes decisions.</p>
|
|
214
|
+
|
|
215
|
+
<p style='margin-top:24px;'><b>Next up:</b> Learn what AI actually is and how these
|
|
216
|
+
prediction systems work.</p>
|
|
217
|
+
|
|
218
|
+
<p style='margin-top:24px;'>This knowledge will help you understand how to build
|
|
219
|
+
better, more ethical AI systems.</p>
|
|
220
|
+
|
|
221
|
+
<h1 style='margin:20px 0; font-size: 3rem;'>👇 SCROLL DOWN 👇</h1>
|
|
222
|
+
<p style='font-size:1.1rem;'>Find the next section below to continue your journey.</p>
|
|
223
|
+
</div>
|
|
224
|
+
</div>
|
|
225
|
+
"""
|
|
226
|
+
)
|
|
227
|
+
back_to_dilemma_btn = gr.Button("◀️ Back to Review")
|
|
228
|
+
|
|
229
|
+
# Navigation logic
|
|
230
|
+
step_1_next.click(
|
|
231
|
+
lambda: (gr.update(visible=False), gr.update(visible=True), gr.update(visible=False),
|
|
232
|
+
gr.update(visible=False), gr.update(visible=False)),
|
|
233
|
+
inputs=None,
|
|
234
|
+
outputs=[step_1, step_2, step_3, step_4, step_5]
|
|
235
|
+
)
|
|
236
|
+
|
|
237
|
+
step_2_back.click(
|
|
238
|
+
lambda: (gr.update(visible=True), gr.update(visible=False), gr.update(visible=False),
|
|
239
|
+
gr.update(visible=False), gr.update(visible=False)),
|
|
240
|
+
inputs=None,
|
|
241
|
+
outputs=[step_1, step_2, step_3, step_4, step_5]
|
|
242
|
+
)
|
|
243
|
+
|
|
244
|
+
step_2_next.click(
|
|
245
|
+
lambda: (gr.update(visible=False), gr.update(visible=False), gr.update(visible=True),
|
|
246
|
+
gr.update(visible=False), gr.update(visible=False)),
|
|
247
|
+
inputs=None,
|
|
248
|
+
outputs=[step_1, step_2, step_3, step_4, step_5]
|
|
249
|
+
)
|
|
250
|
+
|
|
251
|
+
step_3_back.click(
|
|
252
|
+
lambda: (gr.update(visible=False), gr.update(visible=True), gr.update(visible=False),
|
|
253
|
+
gr.update(visible=False), gr.update(visible=False)),
|
|
254
|
+
inputs=None,
|
|
255
|
+
outputs=[step_1, step_2, step_3, step_4, step_5]
|
|
256
|
+
)
|
|
257
|
+
|
|
258
|
+
step_3_next.click(
|
|
259
|
+
lambda: (gr.update(visible=False), gr.update(visible=False), gr.update(visible=False),
|
|
260
|
+
gr.update(visible=True), gr.update(visible=False)),
|
|
261
|
+
inputs=None,
|
|
262
|
+
outputs=[step_1, step_2, step_3, step_4, step_5]
|
|
263
|
+
)
|
|
264
|
+
|
|
265
|
+
step_4_back.click(
|
|
266
|
+
lambda: (gr.update(visible=False), gr.update(visible=False), gr.update(visible=True),
|
|
267
|
+
gr.update(visible=False), gr.update(visible=False)),
|
|
268
|
+
inputs=None,
|
|
269
|
+
outputs=[step_1, step_2, step_3, step_4, step_5]
|
|
270
|
+
)
|
|
271
|
+
|
|
272
|
+
step_4_next.click(
|
|
273
|
+
lambda: (gr.update(visible=False), gr.update(visible=False), gr.update(visible=False),
|
|
274
|
+
gr.update(visible=False), gr.update(visible=True)),
|
|
275
|
+
inputs=None,
|
|
276
|
+
outputs=[step_1, step_2, step_3, step_4, step_5]
|
|
277
|
+
)
|
|
278
|
+
|
|
279
|
+
back_to_dilemma_btn.click(
|
|
280
|
+
lambda: (gr.update(visible=False), gr.update(visible=False), gr.update(visible=False),
|
|
281
|
+
gr.update(visible=True), gr.update(visible=False)),
|
|
282
|
+
inputs=None,
|
|
283
|
+
outputs=[step_1, step_2, step_3, step_4, step_5]
|
|
284
|
+
)
|
|
285
|
+
|
|
286
|
+
return demo
|
|
287
|
+
|
|
288
|
+
|
|
289
|
+
def launch_ai_consequences_app(height: int = 1000, share: bool = False, debug: bool = False) -> None:
|
|
290
|
+
"""Convenience wrapper to create and launch the AI consequences app inline."""
|
|
291
|
+
demo = create_ai_consequences_app()
|
|
292
|
+
try:
|
|
293
|
+
import gradio as gr # noqa: F401
|
|
294
|
+
except ImportError as e:
|
|
295
|
+
raise ImportError("Gradio must be installed to launch the AI consequences app.") from e
|
|
296
|
+
with contextlib.redirect_stdout(open(os.devnull, 'w')), contextlib.redirect_stderr(open(os.devnull, 'w')):
|
|
297
|
+
demo.launch(share=share, inline=True, debug=debug, height=height)
|
|
@@ -0,0 +1,299 @@
|
|
|
1
|
+
"""
|
|
2
|
+
You Be the Judge - Gradio application for the Justice & Equity Challenge.
|
|
3
|
+
|
|
4
|
+
This app teaches:
|
|
5
|
+
1. How to make decisions based on AI predictions
|
|
6
|
+
2. The stakes involved in using AI for criminal justice decisions
|
|
7
|
+
3. The importance of understanding what AI gets wrong
|
|
8
|
+
|
|
9
|
+
Structure:
|
|
10
|
+
- Factory function `create_judge_app()` returns a Gradio Blocks object
|
|
11
|
+
- Convenience wrapper `launch_judge_app()` launches it inline (for notebooks)
|
|
12
|
+
"""
|
|
13
|
+
import contextlib
|
|
14
|
+
import os
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def _generate_defendant_profiles():
|
|
18
|
+
"""Generate synthetic defendant profiles for the exercise."""
|
|
19
|
+
import random
|
|
20
|
+
random.seed(42) # For reproducibility
|
|
21
|
+
|
|
22
|
+
profiles = [
|
|
23
|
+
{
|
|
24
|
+
"id": 1,
|
|
25
|
+
"name": "Carlos M.",
|
|
26
|
+
"age": 23,
|
|
27
|
+
"gender": "Male",
|
|
28
|
+
"race": "Hispanic",
|
|
29
|
+
"prior_offenses": 2,
|
|
30
|
+
"current_charge": "Drug possession",
|
|
31
|
+
"ai_risk": "High",
|
|
32
|
+
"ai_confidence": "85%"
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
"id": 2,
|
|
36
|
+
"name": "Sarah J.",
|
|
37
|
+
"age": 34,
|
|
38
|
+
"gender": "Female",
|
|
39
|
+
"race": "White",
|
|
40
|
+
"prior_offenses": 0,
|
|
41
|
+
"current_charge": "Theft",
|
|
42
|
+
"ai_risk": "Low",
|
|
43
|
+
"ai_confidence": "72%"
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
"id": 3,
|
|
47
|
+
"name": "DeShawn W.",
|
|
48
|
+
"age": 19,
|
|
49
|
+
"gender": "Male",
|
|
50
|
+
"race": "Black",
|
|
51
|
+
"prior_offenses": 1,
|
|
52
|
+
"current_charge": "Assault",
|
|
53
|
+
"ai_risk": "Medium",
|
|
54
|
+
"ai_confidence": "68%"
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
"id": 4,
|
|
58
|
+
"name": "Maria R.",
|
|
59
|
+
"age": 41,
|
|
60
|
+
"gender": "Female",
|
|
61
|
+
"race": "Hispanic",
|
|
62
|
+
"prior_offenses": 3,
|
|
63
|
+
"current_charge": "Fraud",
|
|
64
|
+
"ai_risk": "Medium",
|
|
65
|
+
"ai_confidence": "70%"
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
"id": 5,
|
|
69
|
+
"name": "James K.",
|
|
70
|
+
"age": 28,
|
|
71
|
+
"gender": "Male",
|
|
72
|
+
"race": "White",
|
|
73
|
+
"prior_offenses": 5,
|
|
74
|
+
"current_charge": "Burglary",
|
|
75
|
+
"ai_risk": "High",
|
|
76
|
+
"ai_confidence": "91%"
|
|
77
|
+
}
|
|
78
|
+
]
|
|
79
|
+
|
|
80
|
+
return profiles
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def create_judge_app(theme_primary_hue: str = "indigo") -> "gr.Blocks":
|
|
84
|
+
"""Create the You Be the Judge Gradio Blocks app (not launched yet)."""
|
|
85
|
+
try:
|
|
86
|
+
import gradio as gr
|
|
87
|
+
except ImportError as e:
|
|
88
|
+
raise ImportError(
|
|
89
|
+
"Gradio is required for the judge app. Install with `pip install gradio`."
|
|
90
|
+
) from e
|
|
91
|
+
|
|
92
|
+
profiles = _generate_defendant_profiles()
|
|
93
|
+
|
|
94
|
+
# State to track decisions
|
|
95
|
+
decisions = {}
|
|
96
|
+
|
|
97
|
+
def format_profile(profile):
|
|
98
|
+
"""Format a defendant profile for display."""
|
|
99
|
+
risk_color = {
|
|
100
|
+
"High": "#ef4444",
|
|
101
|
+
"Medium": "#f59e0b",
|
|
102
|
+
"Low": "#22c55e"
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
color = risk_color.get(profile["ai_risk"], "#6b7280")
|
|
106
|
+
|
|
107
|
+
return f"""
|
|
108
|
+
<div style='background:#f9fafb; padding:20px; border-radius:12px; border-left: 6px solid {color};'>
|
|
109
|
+
<h3 style='margin-top:0; color:#1f2937;'>Defendant #{profile['id']}: {profile['name']}</h3>
|
|
110
|
+
<div style='display: grid; grid-template-columns: 1fr 1fr; gap: 12px; font-size: 16px;'>
|
|
111
|
+
<div><b>Age:</b> {profile['age']}</div>
|
|
112
|
+
<div><b>Gender:</b> {profile['gender']}</div>
|
|
113
|
+
<div><b>Race:</b> {profile['race']}</div>
|
|
114
|
+
<div><b>Prior Offenses:</b> {profile['prior_offenses']}</div>
|
|
115
|
+
<div style='grid-column: span 2;'><b>Current Charge:</b> {profile['current_charge']}</div>
|
|
116
|
+
</div>
|
|
117
|
+
<div style='margin-top:16px; padding:12px; background:white; border-radius:8px;'>
|
|
118
|
+
<b>🤖 AI Risk Assessment:</b>
|
|
119
|
+
<span style='color:{color}; font-size:20px; font-weight:bold;'>{profile['ai_risk']} Risk</span>
|
|
120
|
+
<span style='color:#6b7280; margin-left:8px;'>(Confidence: {profile['ai_confidence']})</span>
|
|
121
|
+
</div>
|
|
122
|
+
</div>
|
|
123
|
+
"""
|
|
124
|
+
|
|
125
|
+
def make_decision(defendant_id, decision):
|
|
126
|
+
"""Record a decision for a defendant."""
|
|
127
|
+
decisions[defendant_id] = decision
|
|
128
|
+
return f"✓ Decision recorded: {decision}"
|
|
129
|
+
|
|
130
|
+
def get_summary():
|
|
131
|
+
"""Get summary of all decisions made."""
|
|
132
|
+
if not decisions:
|
|
133
|
+
return "No decisions made yet."
|
|
134
|
+
|
|
135
|
+
released = sum(1 for d in decisions.values() if d == "Release")
|
|
136
|
+
kept = sum(1 for d in decisions.values() if d == "Keep in Prison")
|
|
137
|
+
|
|
138
|
+
summary = f"""
|
|
139
|
+
<div style='background:#dbeafe; padding:20px; border-radius:12px;'>
|
|
140
|
+
<h3 style='margin-top:0;'>📊 Your Decisions Summary</h3>
|
|
141
|
+
<div style='font-size:18px;'>
|
|
142
|
+
<p><b>Prisoners Released:</b> {released} of {len(decisions)}</p>
|
|
143
|
+
<p><b>Prisoners Kept in Prison:</b> {kept} of {len(decisions)}</p>
|
|
144
|
+
</div>
|
|
145
|
+
</div>
|
|
146
|
+
"""
|
|
147
|
+
return summary
|
|
148
|
+
|
|
149
|
+
css = """
|
|
150
|
+
.decision-button {
|
|
151
|
+
font-size: 18px !important;
|
|
152
|
+
padding: 12px 24px !important;
|
|
153
|
+
}
|
|
154
|
+
"""
|
|
155
|
+
|
|
156
|
+
with gr.Blocks(theme=gr.themes.Soft(primary_hue=theme_primary_hue), css=css) as demo:
|
|
157
|
+
gr.Markdown("<h1 style='text-align:center;'>⚖️ You Be the Judge</h1>")
|
|
158
|
+
gr.Markdown(
|
|
159
|
+
"""
|
|
160
|
+
<div style='text-align:center; font-size:18px; max-width: 900px; margin: auto;
|
|
161
|
+
padding: 20px; background-color: #fef3c7; border-radius: 12px; border: 2px solid #f59e0b;'>
|
|
162
|
+
<b>Your Role:</b> You are a judge who must decide whether to release defendants from prison.<br>
|
|
163
|
+
An AI system has analyzed each case and provided a risk assessment.<br><br>
|
|
164
|
+
<b>Your Task:</b> Review each defendant's profile and the AI's prediction, then make your decision.
|
|
165
|
+
</div>
|
|
166
|
+
"""
|
|
167
|
+
)
|
|
168
|
+
gr.HTML("<hr style='margin:24px 0;'>")
|
|
169
|
+
|
|
170
|
+
# Introduction
|
|
171
|
+
with gr.Column(visible=True) as intro_section:
|
|
172
|
+
gr.Markdown("<h2 style='text-align:center;'>📋 The Scenario</h2>")
|
|
173
|
+
gr.Markdown(
|
|
174
|
+
"""
|
|
175
|
+
<div style='font-size: 18px; background:#e0f2fe; padding:24px; border-radius:12px;'>
|
|
176
|
+
You are a judge in a busy criminal court. Due to prison overcrowding, you must decide
|
|
177
|
+
which defendants can be safely released.<br><br>
|
|
178
|
+
|
|
179
|
+
To help you, the court has implemented an AI system that predicts the risk of each
|
|
180
|
+
defendant committing new crimes if released. The AI categorizes defendants as:<br><br>
|
|
181
|
+
|
|
182
|
+
<ul style='font-size:18px;'>
|
|
183
|
+
<li><span style='color:#ef4444; font-weight:bold;'>High Risk</span> - Likely to re-offend</li>
|
|
184
|
+
<li><span style='color:#f59e0b; font-weight:bold;'>Medium Risk</span> - Moderate chance of re-offending</li>
|
|
185
|
+
<li><span style='color:#22c55e; font-weight:bold;'>Low Risk</span> - Unlikely to re-offend</li>
|
|
186
|
+
</ul>
|
|
187
|
+
|
|
188
|
+
<b>Remember:</b> Your decisions affect real people's lives and public safety.
|
|
189
|
+
</div>
|
|
190
|
+
"""
|
|
191
|
+
)
|
|
192
|
+
start_btn = gr.Button("Begin Making Decisions ▶️", variant="primary", size="lg")
|
|
193
|
+
|
|
194
|
+
# Defendant profiles section
|
|
195
|
+
with gr.Column(visible=False) as profiles_section:
|
|
196
|
+
gr.Markdown("<h2 style='text-align:center;'>👥 Defendant Profiles</h2>")
|
|
197
|
+
gr.Markdown(
|
|
198
|
+
"""
|
|
199
|
+
<div style='text-align:center; font-size:16px; background:#f3f4f6; padding:12px; border-radius:8px;'>
|
|
200
|
+
Review each defendant's information and the AI's risk assessment, then make your decision.
|
|
201
|
+
</div>
|
|
202
|
+
"""
|
|
203
|
+
)
|
|
204
|
+
gr.HTML("<br>")
|
|
205
|
+
|
|
206
|
+
# Create UI for each defendant
|
|
207
|
+
for profile in profiles:
|
|
208
|
+
with gr.Column():
|
|
209
|
+
gr.HTML(format_profile(profile))
|
|
210
|
+
|
|
211
|
+
with gr.Row():
|
|
212
|
+
release_btn = gr.Button(
|
|
213
|
+
"✓ Release Prisoner",
|
|
214
|
+
variant="primary",
|
|
215
|
+
elem_classes=["decision-button"]
|
|
216
|
+
)
|
|
217
|
+
keep_btn = gr.Button(
|
|
218
|
+
"✗ Keep in Prison",
|
|
219
|
+
variant="secondary",
|
|
220
|
+
elem_classes=["decision-button"]
|
|
221
|
+
)
|
|
222
|
+
|
|
223
|
+
decision_status = gr.Markdown("")
|
|
224
|
+
|
|
225
|
+
# Wire up buttons
|
|
226
|
+
release_btn.click(
|
|
227
|
+
lambda p_id=profile["id"]: make_decision(p_id, "Release"),
|
|
228
|
+
inputs=None,
|
|
229
|
+
outputs=decision_status
|
|
230
|
+
)
|
|
231
|
+
keep_btn.click(
|
|
232
|
+
lambda p_id=profile["id"]: make_decision(p_id, "Keep in Prison"),
|
|
233
|
+
inputs=None,
|
|
234
|
+
outputs=decision_status
|
|
235
|
+
)
|
|
236
|
+
|
|
237
|
+
gr.HTML("<hr style='margin:24px 0;'>")
|
|
238
|
+
|
|
239
|
+
# Summary section
|
|
240
|
+
summary_display = gr.HTML("")
|
|
241
|
+
show_summary_btn = gr.Button("📊 Show My Decisions Summary", variant="primary", size="lg")
|
|
242
|
+
show_summary_btn.click(get_summary, inputs=None, outputs=summary_display)
|
|
243
|
+
|
|
244
|
+
gr.HTML("<br>")
|
|
245
|
+
complete_btn = gr.Button("Complete This Section ▶️", variant="primary", size="lg")
|
|
246
|
+
|
|
247
|
+
# Completion section
|
|
248
|
+
with gr.Column(visible=False) as complete_section:
|
|
249
|
+
gr.Markdown(
|
|
250
|
+
"""
|
|
251
|
+
<div style='text-align:center;'>
|
|
252
|
+
<h2 style='font-size: 2.5rem;'>✅ Decisions Complete!</h2>
|
|
253
|
+
<div style='font-size: 1.3rem; background:#e0f2fe; padding:28px; border-radius:16px;
|
|
254
|
+
border: 2px solid #0284c7;'>
|
|
255
|
+
You've made your decisions based on the AI's recommendations.<br><br>
|
|
256
|
+
But here's the critical question:<br><br>
|
|
257
|
+
<h2 style='color:#dc2626; margin:16px 0;'>What if the AI was wrong?</h2>
|
|
258
|
+
<p style='font-size:1.1rem;'>
|
|
259
|
+
Continue to the next section below to explore the consequences of
|
|
260
|
+
trusting AI predictions in high-stakes situations.
|
|
261
|
+
</p>
|
|
262
|
+
<h1 style='margin:20px 0; font-size: 3rem;'>👇 SCROLL DOWN 👇</h1>
|
|
263
|
+
</div>
|
|
264
|
+
</div>
|
|
265
|
+
"""
|
|
266
|
+
)
|
|
267
|
+
back_to_profiles_btn = gr.Button("◀️ Back to Review Decisions")
|
|
268
|
+
|
|
269
|
+
# Navigation logic
|
|
270
|
+
start_btn.click(
|
|
271
|
+
lambda: (gr.update(visible=False), gr.update(visible=True), gr.update(visible=False)),
|
|
272
|
+
inputs=None,
|
|
273
|
+
outputs=[intro_section, profiles_section, complete_section]
|
|
274
|
+
)
|
|
275
|
+
|
|
276
|
+
complete_btn.click(
|
|
277
|
+
lambda: (gr.update(visible=False), gr.update(visible=False), gr.update(visible=True)),
|
|
278
|
+
inputs=None,
|
|
279
|
+
outputs=[intro_section, profiles_section, complete_section]
|
|
280
|
+
)
|
|
281
|
+
|
|
282
|
+
back_to_profiles_btn.click(
|
|
283
|
+
lambda: (gr.update(visible=False), gr.update(visible=True), gr.update(visible=False)),
|
|
284
|
+
inputs=None,
|
|
285
|
+
outputs=[intro_section, profiles_section, complete_section]
|
|
286
|
+
)
|
|
287
|
+
|
|
288
|
+
return demo
|
|
289
|
+
|
|
290
|
+
|
|
291
|
+
def launch_judge_app(height: int = 1200, share: bool = False, debug: bool = False) -> None:
|
|
292
|
+
"""Convenience wrapper to create and launch the judge app inline."""
|
|
293
|
+
demo = create_judge_app()
|
|
294
|
+
try:
|
|
295
|
+
import gradio as gr # noqa: F401
|
|
296
|
+
except ImportError as e:
|
|
297
|
+
raise ImportError("Gradio must be installed to launch the judge app.") from e
|
|
298
|
+
with contextlib.redirect_stdout(open(os.devnull, 'w')), contextlib.redirect_stderr(open(os.devnull, 'w')):
|
|
299
|
+
demo.launch(share=share, inline=True, debug=debug, height=height)
|