Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
|
@@ -4,9 +4,23 @@ os.environ["OMP_NUM_THREADS"] = "1"
|
|
| 4 |
import gradio as gr
|
| 5 |
import torch
|
| 6 |
import re
|
|
|
|
|
|
|
|
|
|
| 7 |
from transformers import AutoTokenizer, AutoModelForCausalLM, BitsAndBytesConfig
|
| 8 |
import spaces
|
| 9 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 10 |
# Model configuration
|
| 11 |
MODEL_ID = "oberbics/newspaper-argument-mining-V1"
|
| 12 |
|
|
@@ -41,10 +55,13 @@ RULES:
|
|
| 41 |
- More than one argumentative unit possible for one aticle, one unit has one clear clame and all the xml structures"""
|
| 42 |
|
| 43 |
print("Loading tokenizer...")
|
|
|
|
| 44 |
tokenizer = AutoTokenizer.from_pretrained(MODEL_ID)
|
| 45 |
tokenizer.pad_token = tokenizer.eos_token
|
|
|
|
| 46 |
|
| 47 |
print("Loading model...")
|
|
|
|
| 48 |
bnb_config = BitsAndBytesConfig(
|
| 49 |
load_in_4bit=True,
|
| 50 |
bnb_4bit_quant_type="nf4",
|
|
@@ -60,102 +77,39 @@ model = AutoModelForCausalLM.from_pretrained(
|
|
| 60 |
trust_remote_code=True
|
| 61 |
)
|
| 62 |
print("Model loaded successfully!")
|
| 63 |
-
|
| 64 |
-
|
| 65 |
-
def
|
| 66 |
-
"""
|
| 67 |
-
|
| 68 |
-
|
| 69 |
-
|
| 70 |
-
|
| 71 |
-
|
| 72 |
-
|
| 73 |
-
|
| 74 |
-
|
| 75 |
-
|
| 76 |
-
|
| 77 |
-
|
| 78 |
-
'claim': claim,
|
| 79 |
-
'explanation': explanation,
|
| 80 |
-
'human_verification_needed': verification.lower() == 'true',
|
| 81 |
-
'raw_verification': verification
|
| 82 |
-
})
|
| 83 |
-
|
| 84 |
-
return units
|
| 85 |
-
|
| 86 |
-
|
| 87 |
-
def calculate_confidence_score(unit, position):
|
| 88 |
-
"""Calculate confidence based on model's natural ordering + basic quality checks"""
|
| 89 |
-
|
| 90 |
-
# Base confidence from position (model's implicit ranking)
|
| 91 |
-
# First argument = highest confidence, then declining
|
| 92 |
-
position_confidence = max(0.2, 0.95 - (position * 0.15)) # 0.95, 0.80, 0.65, 0.50, 0.35, 0.20...
|
| 93 |
-
|
| 94 |
-
# Only major adjustments for obvious quality issues
|
| 95 |
-
if unit['argument'] == 'NA':
|
| 96 |
-
return 0.0
|
| 97 |
|
| 98 |
-
#
|
| 99 |
-
|
| 100 |
-
|
| 101 |
-
|
| 102 |
-
|
| 103 |
-
|
| 104 |
-
position_confidence -= 0.2
|
| 105 |
-
|
| 106 |
-
return max(0.0, min(1.0, position_confidence))
|
| 107 |
-
|
| 108 |
-
|
| 109 |
-
def filter_high_confidence_arguments(units, confidence_threshold=0.6):
|
| 110 |
-
"""Filter argumentative units by confidence score"""
|
| 111 |
-
scored_units = []
|
| 112 |
-
|
| 113 |
-
for unit in units:
|
| 114 |
-
confidence = calculate_confidence_score(unit)
|
| 115 |
-
unit['confidence_score'] = confidence
|
| 116 |
-
scored_units.append(unit)
|
| 117 |
-
|
| 118 |
-
# Sort by confidence (highest first)
|
| 119 |
-
scored_units.sort(key=lambda x: x['confidence_score'], reverse=True)
|
| 120 |
-
|
| 121 |
-
# Filter by threshold
|
| 122 |
-
high_confidence_units = [unit for unit in scored_units if unit['confidence_score'] >= confidence_threshold]
|
| 123 |
-
|
| 124 |
-
return high_confidence_units, scored_units
|
| 125 |
-
|
| 126 |
-
|
| 127 |
-
def format_filtered_output(high_confidence_units, show_scores=True, debug=False):
|
| 128 |
-
"""Format the high-confidence units for display"""
|
| 129 |
-
if not high_confidence_units:
|
| 130 |
-
return "No high-confidence arguments found."
|
| 131 |
-
|
| 132 |
-
output = []
|
| 133 |
-
for i, unit in enumerate(high_confidence_units, 1):
|
| 134 |
-
if show_scores:
|
| 135 |
-
output.append(f"=== ARGUMENT {i} (Confidence: {unit['confidence_score']:.3f}) ===")
|
| 136 |
-
else:
|
| 137 |
-
output.append(f"=== ARGUMENT {i} ===")
|
| 138 |
-
|
| 139 |
-
if debug:
|
| 140 |
-
# Show lengths and verification status for debugging
|
| 141 |
-
arg_len = len(unit['argument']) if unit['argument'] != 'NA' else 0
|
| 142 |
-
claim_len = len(unit['claim']) if unit['claim'] != 'NA' else 0
|
| 143 |
-
exp_len = len(unit['explanation']) if unit['explanation'] != 'NA' else 0
|
| 144 |
-
output.append(f"[DEBUG: arg_len={arg_len}, claim_len={claim_len}, exp_len={exp_len}, verification={unit['human_verification_needed']}]")
|
| 145 |
-
|
| 146 |
-
output.append(f"<argument>{unit['argument']}</argument>")
|
| 147 |
-
output.append(f"<claim>{unit['claim']}</claim>")
|
| 148 |
-
output.append(f"<explanation>{unit['explanation']}</explanation>")
|
| 149 |
-
output.append(f"<human_verification_needed>{unit['raw_verification']}</human_verification_needed>")
|
| 150 |
-
output.append("")
|
| 151 |
-
|
| 152 |
-
return "\n".join(output)
|
| 153 |
-
|
| 154 |
|
| 155 |
@spaces.GPU
|
| 156 |
-
def extract_arguments(text, temperature=0.1
|
|
|
|
|
|
|
|
|
|
| 157 |
if not text or not text.strip():
|
| 158 |
-
|
|
|
|
|
|
|
|
|
|
| 159 |
|
| 160 |
try:
|
| 161 |
prompt = f"""<|begin_of_text|><|start_header_id|>system<|end_header_id|>
|
|
@@ -181,7 +135,7 @@ Extract arguments from historical text.
|
|
| 181 |
except:
|
| 182 |
temperature = 0.1
|
| 183 |
|
| 184 |
-
|
| 185 |
|
| 186 |
with torch.no_grad():
|
| 187 |
outputs = model.generate(
|
|
@@ -197,78 +151,103 @@ Extract arguments from historical text.
|
|
| 197 |
generated_tokens = outputs[0][input_length:]
|
| 198 |
response = tokenizer.decode(generated_tokens, skip_special_tokens=True)
|
| 199 |
|
| 200 |
-
print(f"DEBUG: Raw response length: {len(response)}")
|
| 201 |
-
print(f"DEBUG: Response starts with: {response[:100]}")
|
| 202 |
-
|
| 203 |
# Fix XML start
|
| 204 |
if not response.startswith('<argument>'):
|
| 205 |
arg_start = response.find('<argument>')
|
| 206 |
if arg_start != -1:
|
| 207 |
response = response[arg_start:]
|
| 208 |
-
print(f"DEBUG: Fixed response start, new length: {len(response)}")
|
| 209 |
|
| 210 |
-
|
| 211 |
-
|
| 212 |
|
| 213 |
-
|
| 214 |
-
|
| 215 |
-
return response, f"Raw output returned (no parseable argumentative units found)"
|
| 216 |
|
| 217 |
-
|
| 218 |
|
| 219 |
-
|
| 220 |
-
|
| 221 |
-
|
| 222 |
-
|
| 223 |
-
print(f"DEBUG: Error in confidence filtering: {str(e)}")
|
| 224 |
-
return response, f"Error in confidence filtering: {str(e)}"
|
| 225 |
|
| 226 |
-
|
| 227 |
-
|
| 228 |
-
all_output = []
|
| 229 |
-
for i, unit in enumerate(all_units, 1):
|
| 230 |
-
status = "✓ HIGH CONFIDENCE" if unit['confidence_score'] >= confidence_threshold else "⚠ LOW CONFIDENCE"
|
| 231 |
-
all_output.append(f"=== ARGUMENT {i} - {status} (Score: {unit['confidence_score']:.3f}) ===")
|
| 232 |
-
all_output.append(f"<argument>{unit['argument']}</argument>")
|
| 233 |
-
all_output.append(f"<claim>{unit['claim']}</claim>")
|
| 234 |
-
all_output.append(f"<explanation>{unit['explanation']}</explanation>")
|
| 235 |
-
all_output.append(f"<human_verification_needed>{unit['raw_verification']}</human_verification_needed>")
|
| 236 |
-
all_output.append("")
|
| 237 |
-
|
| 238 |
-
return "\n".join(all_output), f"Found {len(units)} total units, {len(high_confidence_units)} high-confidence"
|
| 239 |
|
| 240 |
-
|
| 241 |
-
|
| 242 |
-
|
| 243 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 244 |
|
|
|
|
| 245 |
except Exception as e:
|
| 246 |
-
|
| 247 |
-
print(f"DEBUG: {error_msg}")
|
| 248 |
-
return error_msg, "Processing failed - check console for details"
|
| 249 |
|
| 250 |
-
|
| 251 |
-
|
| 252 |
-
|
| 253 |
-
|
| 254 |
-
|
| 255 |
-
|
| 256 |
-
gr.
|
| 257 |
-
|
| 258 |
-
|
| 259 |
-
|
| 260 |
-
|
| 261 |
-
|
| 262 |
-
|
| 263 |
-
|
| 264 |
-
|
| 265 |
-
|
| 266 |
-
|
| 267 |
-
|
| 268 |
-
|
| 269 |
-
|
| 270 |
-
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 271 |
|
| 272 |
if __name__ == "__main__":
|
| 273 |
-
demo.launch()
|
| 274 |
-
|
|
|
|
| 4 |
import gradio as gr
|
| 5 |
import torch
|
| 6 |
import re
|
| 7 |
+
import json
|
| 8 |
+
import datetime
|
| 9 |
+
import logging
|
| 10 |
from transformers import AutoTokenizer, AutoModelForCausalLM, BitsAndBytesConfig
|
| 11 |
import spaces
|
| 12 |
|
| 13 |
+
# Configure logging
|
| 14 |
+
logging.basicConfig(
|
| 15 |
+
level=logging.INFO,
|
| 16 |
+
format='%(asctime)s - %(levelname)s - %(message)s',
|
| 17 |
+
handlers=[
|
| 18 |
+
logging.FileHandler('argument_extraction.log'),
|
| 19 |
+
logging.StreamHandler()
|
| 20 |
+
]
|
| 21 |
+
)
|
| 22 |
+
logger = logging.getLogger(__name__)
|
| 23 |
+
|
| 24 |
# Model configuration
|
| 25 |
MODEL_ID = "oberbics/newspaper-argument-mining-V1"
|
| 26 |
|
|
|
|
| 55 |
- More than one argumentative unit possible for one aticle, one unit has one clear clame and all the xml structures"""
|
| 56 |
|
| 57 |
print("Loading tokenizer...")
|
| 58 |
+
logger.info("Starting tokenizer loading")
|
| 59 |
tokenizer = AutoTokenizer.from_pretrained(MODEL_ID)
|
| 60 |
tokenizer.pad_token = tokenizer.eos_token
|
| 61 |
+
logger.info("Tokenizer loaded successfully")
|
| 62 |
|
| 63 |
print("Loading model...")
|
| 64 |
+
logger.info("Starting model loading")
|
| 65 |
bnb_config = BitsAndBytesConfig(
|
| 66 |
load_in_4bit=True,
|
| 67 |
bnb_4bit_quant_type="nf4",
|
|
|
|
| 77 |
trust_remote_code=True
|
| 78 |
)
|
| 79 |
print("Model loaded successfully!")
|
| 80 |
+
logger.info("Model loaded successfully")
|
| 81 |
+
|
| 82 |
+
def log_interaction(input_text, temperature, output, processing_time, error=None):
|
| 83 |
+
"""Log each interaction to JSON file for analysis"""
|
| 84 |
+
log_entry = {
|
| 85 |
+
"timestamp": datetime.datetime.now().isoformat(),
|
| 86 |
+
"input_length": len(input_text) if input_text else 0,
|
| 87 |
+
"input_preview": input_text[:100] if input_text else "",
|
| 88 |
+
"temperature": temperature,
|
| 89 |
+
"output_length": len(output) if output else 0,
|
| 90 |
+
"processing_time_seconds": processing_time,
|
| 91 |
+
"has_error": error is not None,
|
| 92 |
+
"error_message": str(error) if error else None,
|
| 93 |
+
"output_preview": output[:200] if output else ""
|
| 94 |
+
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 95 |
|
| 96 |
+
# Save to JSON file
|
| 97 |
+
try:
|
| 98 |
+
with open('interaction_logs.json', 'a') as f:
|
| 99 |
+
f.write(json.dumps(log_entry) + '\n')
|
| 100 |
+
except Exception as e:
|
| 101 |
+
logger.error(f"Failed to save interaction log: {e}")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 102 |
|
| 103 |
@spaces.GPU
|
| 104 |
+
def extract_arguments(text, temperature=0.1):
|
| 105 |
+
start_time = datetime.datetime.now()
|
| 106 |
+
logger.info(f"Processing request - Input length: {len(text) if text else 0}, Temperature: {temperature}")
|
| 107 |
+
|
| 108 |
if not text or not text.strip():
|
| 109 |
+
error_msg = "Please enter some text to analyze."
|
| 110 |
+
logger.warning("Empty input received")
|
| 111 |
+
log_interaction(text, temperature, "", 0, error_msg)
|
| 112 |
+
return "", error_msg
|
| 113 |
|
| 114 |
try:
|
| 115 |
prompt = f"""<|begin_of_text|><|start_header_id|>system<|end_header_id|>
|
|
|
|
| 135 |
except:
|
| 136 |
temperature = 0.1
|
| 137 |
|
| 138 |
+
logger.info(f"Starting model generation with {input_length} input tokens")
|
| 139 |
|
| 140 |
with torch.no_grad():
|
| 141 |
outputs = model.generate(
|
|
|
|
| 151 |
generated_tokens = outputs[0][input_length:]
|
| 152 |
response = tokenizer.decode(generated_tokens, skip_special_tokens=True)
|
| 153 |
|
|
|
|
|
|
|
|
|
|
| 154 |
# Fix XML start
|
| 155 |
if not response.startswith('<argument>'):
|
| 156 |
arg_start = response.find('<argument>')
|
| 157 |
if arg_start != -1:
|
| 158 |
response = response[arg_start:]
|
|
|
|
| 159 |
|
| 160 |
+
processing_time = (datetime.datetime.now() - start_time).total_seconds()
|
| 161 |
+
logger.info(f"Processing completed in {processing_time:.2f} seconds - Output length: {len(response)}")
|
| 162 |
|
| 163 |
+
# Log successful interaction
|
| 164 |
+
log_interaction(text, temperature, response, processing_time)
|
|
|
|
| 165 |
|
| 166 |
+
return response
|
| 167 |
|
| 168 |
+
except Exception as e:
|
| 169 |
+
processing_time = (datetime.datetime.now() - start_time).total_seconds()
|
| 170 |
+
error_msg = f"Error during processing: {str(e)}"
|
| 171 |
+
logger.error(f"Processing failed after {processing_time:.2f} seconds: {e}")
|
|
|
|
|
|
|
| 172 |
|
| 173 |
+
# Log failed interaction
|
| 174 |
+
log_interaction(text, temperature, "", processing_time, e)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 175 |
|
| 176 |
+
return error_msg
|
| 177 |
+
|
| 178 |
+
def get_logs():
|
| 179 |
+
"""Function to view recent logs"""
|
| 180 |
+
try:
|
| 181 |
+
with open('interaction_logs.json', 'r') as f:
|
| 182 |
+
lines = f.readlines()
|
| 183 |
+
recent_logs = lines[-10:] # Last 10 interactions
|
| 184 |
+
|
| 185 |
+
log_summary = []
|
| 186 |
+
for line in recent_logs:
|
| 187 |
+
entry = json.loads(line)
|
| 188 |
+
confidence_info = f", Confidence: {entry['confidence_score']:.3f}" if entry.get('confidence_score') else ""
|
| 189 |
+
summary = f"[{entry['timestamp']}] Input: {entry['input_length']} chars, Output: {entry['output_length']} chars, Time: {entry['processing_time_seconds']:.2f}s{confidence_info}"
|
| 190 |
+
if entry['has_error']:
|
| 191 |
+
summary += f" ERROR: {entry['error_message']}"
|
| 192 |
+
log_summary.append(summary)
|
| 193 |
|
| 194 |
+
return "\n".join(log_summary)
|
| 195 |
except Exception as e:
|
| 196 |
+
return f"Error reading logs: {e}"
|
|
|
|
|
|
|
| 197 |
|
| 198 |
+
# Gradio interface with logging viewer
|
| 199 |
+
with gr.Blocks(title="Newspaper Argumentative Unit Extractor") as demo:
|
| 200 |
+
gr.Markdown("# Newspaper Argumentative Unit Extractor")
|
| 201 |
+
gr.Markdown("Extract argumentative units from news sources")
|
| 202 |
+
|
| 203 |
+
with gr.Tab("Extract Arguments"):
|
| 204 |
+
with gr.Row():
|
| 205 |
+
with gr.Column():
|
| 206 |
+
input_text = gr.Textbox(
|
| 207 |
+
label="Input Text",
|
| 208 |
+
placeholder="Enter newspaper text here...",
|
| 209 |
+
lines=10
|
| 210 |
+
)
|
| 211 |
+
temperature = gr.Slider(
|
| 212 |
+
minimum=0.01,
|
| 213 |
+
maximum=0.3,
|
| 214 |
+
value=0.1,
|
| 215 |
+
step=0.01,
|
| 216 |
+
label="Temperature (lower = more consistent)"
|
| 217 |
+
)
|
| 218 |
+
extract_btn = gr.Button("Extract Arguments", variant="primary")
|
| 219 |
+
|
| 220 |
+
with gr.Column():
|
| 221 |
+
output_text = gr.Textbox(
|
| 222 |
+
label="Raw XML Output",
|
| 223 |
+
lines=8
|
| 224 |
+
)
|
| 225 |
+
|
| 226 |
+
extract_btn.click(
|
| 227 |
+
fn=extract_arguments,
|
| 228 |
+
inputs=[input_text, temperature],
|
| 229 |
+
outputs=[output_text]
|
| 230 |
+
)
|
| 231 |
+
|
| 232 |
+
# Examples
|
| 233 |
+
gr.Examples(
|
| 234 |
+
examples=[
|
| 235 |
+
["Reggio, January 8. Frequent shocks of earthquake were felt here dur ing the night, accompanied at times by loud subter ranean reports. A few buildings that had not been completely destroyed were further damaged. The work of reconstructing the railway is being pushed forward energetically. News has been received from Brancaleone, Catanzaro, and Palmi of earthquakes by which the inhabitants were alarmed last night", 0.1],
|
| 236 |
+
],
|
| 237 |
+
inputs=[input_text, temperature],
|
| 238 |
+
outputs=[output_text],
|
| 239 |
+
fn=extract_arguments
|
| 240 |
+
)
|
| 241 |
+
|
| 242 |
+
with gr.Tab("Logs"):
|
| 243 |
+
gr.Markdown("## Recent Activity Logs")
|
| 244 |
+
log_display = gr.Textbox(
|
| 245 |
+
label="Recent Interactions",
|
| 246 |
+
lines=15,
|
| 247 |
+
value=get_logs()
|
| 248 |
+
)
|
| 249 |
+
refresh_btn = gr.Button("Refresh Logs")
|
| 250 |
+
refresh_btn.click(fn=get_logs, outputs=[log_display])
|
| 251 |
|
| 252 |
if __name__ == "__main__":
|
| 253 |
+
demo.launch()
|
|
|