pythonprincess commited on
Commit
fbc0e04
·
verified ·
1 Parent(s): 74b655e

Upload 2 files

Browse files
models/bias/__init__.py ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ # Bias Detection Model Package
2
+
models/bias/bias_utils.py ADDED
@@ -0,0 +1,190 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # models/bias/bias_utils.py
2
+
3
+ """
4
+ Bias Detection Utilities for Penny
5
+
6
+ Provides zero-shot classification for detecting potential bias in text responses.
7
+ Uses a classification model to identify neutral content vs. biased language patterns.
8
+ """
9
+
10
+ import asyncio
11
+ import os
12
+ import httpx
13
+ from typing import Dict, Any, Optional, List
14
+ import logging
15
+
16
+ # --- Logging Setup ---
17
+ logger = logging.getLogger(__name__)
18
+
19
+ # --- Hugging Face API Configuration ---
20
+ HF_API_URL = "https://api-inference.huggingface.co/models/facebook/bart-large-mnli"
21
+ HF_TOKEN = os.getenv("HF_TOKEN")
22
+
23
+ AGENT_NAME = "penny-bias-checker"
24
+
25
+ # Define the labels for Zero-Shot Classification.
26
+ CANDIDATE_LABELS = [
27
+ "neutral and objective",
28
+ "contains political bias",
29
+ "uses emotional language",
30
+ "is factually biased",
31
+ ]
32
+
33
+
34
+ def _is_bias_available() -> bool:
35
+ """
36
+ Check if bias detection service is available.
37
+
38
+ Returns:
39
+ bool: True if HF_TOKEN is configured
40
+ """
41
+ return HF_TOKEN is not None and len(HF_TOKEN) > 0
42
+
43
+
44
+ async def check_bias(text: str) -> Dict[str, Any]:
45
+ """
46
+ Runs zero-shot classification to check for bias in the input text.
47
+
48
+ Uses a pre-loaded classification model to analyze text for:
49
+ - Neutral and objective language
50
+ - Political bias
51
+ - Emotional language
52
+ - Factual bias
53
+
54
+ Args:
55
+ text: The string of text to analyze for bias
56
+
57
+ Returns:
58
+ Dictionary containing:
59
+ - analysis: List of labels with confidence scores, sorted by score
60
+ - available: Whether the bias detection service is operational
61
+ - message: Optional error or status message
62
+
63
+ Example:
64
+ >>> result = await check_bias("This is neutral text.")
65
+ >>> result['analysis'][0]['label']
66
+ 'neutral and objective'
67
+ """
68
+
69
+ # Input validation
70
+ if not text or not isinstance(text, str):
71
+ logger.warning("check_bias called with invalid text input")
72
+ return {
73
+ "analysis": [],
74
+ "available": False,
75
+ "message": "Invalid input: text must be a non-empty string"
76
+ }
77
+
78
+ # Strip text to avoid processing whitespace
79
+ text = text.strip()
80
+ if not text:
81
+ logger.warning("check_bias called with empty text after stripping")
82
+ return {
83
+ "analysis": [],
84
+ "available": False,
85
+ "message": "Invalid input: text is empty"
86
+ }
87
+
88
+ # Check API availability
89
+ if not _is_bias_available():
90
+ logger.warning(f"{AGENT_NAME}: API not configured (missing HF_TOKEN)")
91
+ return {
92
+ "analysis": [],
93
+ "available": False,
94
+ "message": "Bias detection service is currently unavailable"
95
+ }
96
+
97
+ try:
98
+ # Prepare API request for zero-shot classification
99
+ headers = {"Authorization": f"Bearer {HF_TOKEN}"}
100
+ payload = {
101
+ "inputs": text,
102
+ "parameters": {
103
+ "candidate_labels": CANDIDATE_LABELS,
104
+ "multi_label": True
105
+ }
106
+ }
107
+
108
+ # Call Hugging Face Inference API
109
+ async with httpx.AsyncClient(timeout=30.0) as client:
110
+ response = await client.post(HF_API_URL, json=payload, headers=headers)
111
+
112
+ if response.status_code != 200:
113
+ logger.error(f"Bias detection API returned status {response.status_code}")
114
+ return {
115
+ "analysis": [],
116
+ "available": False,
117
+ "message": f"Bias detection API error: {response.status_code}"
118
+ }
119
+
120
+ results = response.json()
121
+
122
+ # Validate results structure
123
+ if not results or not isinstance(results, dict):
124
+ logger.error(f"Bias detection returned unexpected format: {type(results)}")
125
+ return {
126
+ "analysis": [],
127
+ "available": True,
128
+ "message": "Inference returned unexpected format"
129
+ }
130
+
131
+ labels = results.get('labels', [])
132
+ scores = results.get('scores', [])
133
+
134
+ if not labels or not scores:
135
+ logger.warning("Bias detection returned empty labels or scores")
136
+ return {
137
+ "analysis": [],
138
+ "available": True,
139
+ "message": "No classification results returned"
140
+ }
141
+
142
+ # Build analysis results
143
+ analysis = [
144
+ {"label": label, "score": float(score)}
145
+ for label, score in zip(labels, scores)
146
+ ]
147
+
148
+ # Sort by confidence score (descending)
149
+ analysis.sort(key=lambda x: x['score'], reverse=True)
150
+
151
+ logger.debug(f"Bias check completed successfully, top result: {analysis[0]['label']} ({analysis[0]['score']:.3f})")
152
+
153
+ return {
154
+ "analysis": analysis,
155
+ "available": True
156
+ }
157
+
158
+ except httpx.TimeoutException:
159
+ logger.error("Bias detection request timed out")
160
+ return {
161
+ "analysis": [],
162
+ "available": False,
163
+ "message": "Bias detection request timed out"
164
+ }
165
+
166
+ except asyncio.CancelledError:
167
+ logger.warning("Bias detection task was cancelled")
168
+ raise
169
+
170
+ except Exception as e:
171
+ logger.error(f"Error during bias detection inference: {e}", exc_info=True)
172
+ return {
173
+ "analysis": [],
174
+ "available": False,
175
+ "message": f"Bias detection error: {str(e)}"
176
+ }
177
+
178
+
179
+ def get_bias_pipeline_status() -> Dict[str, Any]:
180
+ """
181
+ Returns the current status of the bias detection pipeline.
182
+
183
+ Returns:
184
+ Dictionary with pipeline availability status
185
+ """
186
+ return {
187
+ "agent_name": AGENT_NAME,
188
+ "available": _is_bias_available(),
189
+ "api_configured": HF_TOKEN is not None
190
+ }