Spaces:
Running
on
Zero
Running
on
Zero
better error messages
Browse files
app.py
CHANGED
@@ -36,6 +36,115 @@ MODEL_URL = "https://dl.fbaipublicfiles.com/fasttext/supervised-models/lid.176.b
|
|
36 |
# Global counter for error modals to help with debugging
|
37 |
_ERROR_MODAL_COUNTER = 0
|
38 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
39 |
def validate_english_only_windowed(text, model=None):
|
40 |
"""
|
41 |
Validates that input text is English-only using sliding window approach.
|
@@ -652,10 +761,13 @@ def reset_to_initial_state():
|
|
652 |
for i, email in enumerate(INBOX):
|
653 |
reset_emails.append(format_single_email(email, i + 1))
|
654 |
|
|
|
|
|
|
|
655 |
return (
|
656 |
"", # Clear attack subject
|
657 |
"", # Clear attack body
|
658 |
-
|
659 |
"", # Final output cleared
|
660 |
"π Interface reset to initial state", # Trace message in accordion
|
661 |
reset_emails[0], # Reset email 1
|
@@ -1112,31 +1224,33 @@ You achieve Attacker Goal 2 and user goal is also achieved and no information ab
|
|
1112 |
with gr.Group(elem_id="defense-toggle-container"):
|
1113 |
with gr.Row(elem_id="defense-toggle-row"):
|
1114 |
with gr.Column(scale=2, min_width=200):
|
|
|
|
|
1115 |
model_selector = gr.Dropdown(
|
1116 |
-
choices=
|
1117 |
-
value=
|
1118 |
label="Select Agent LLM",
|
1119 |
elem_id="model-selector"
|
1120 |
)
|
1121 |
with gr.Column(scale=2, min_width=100):
|
1122 |
defense_toggle = gr.Checkbox(label="Instruction Tagger Defense", value=True, elem_id="defense-toggle")
|
1123 |
|
1124 |
-
|
1125 |
-
|
1126 |
lines=2,
|
1127 |
-
|
1128 |
-
|
1129 |
label="System Prompt"
|
1130 |
-
|
1131 |
-
|
1132 |
|
1133 |
-
|
1134 |
-
|
1135 |
lines=1,
|
1136 |
-
|
1137 |
-
|
1138 |
label="User Query (Fixed)"
|
1139 |
-
|
1140 |
|
1141 |
# (Moved defense toggle to top; removed previous placement)
|
1142 |
|
@@ -1376,7 +1490,24 @@ You achieve Attacker Goal 2 and user goal is also achieved and no information ab
|
|
1376 |
except Exception as e:
|
1377 |
validation_errors.append(f"EMAIL BODY: Validation failed - {str(e)}")
|
1378 |
|
1379 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1380 |
if validation_errors:
|
1381 |
error_timestamp = int(time.time() * 1000)
|
1382 |
print(f"π¨ VALIDATION ERRORS FOUND: {len(validation_errors)} errors at {error_timestamp}")
|
@@ -1402,7 +1533,66 @@ You achieve Attacker Goal 2 and user goal is also achieved and no information ab
|
|
1402 |
"subject_confidence_scores": subject_confidence_scores,
|
1403 |
"body_confidence_scores": body_confidence_scores
|
1404 |
}
|
1405 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1406 |
|
1407 |
# Build a formatted results summary extracted from exec_log
|
1408 |
def build_results_html(log_text: str) -> str:
|
|
|
36 |
# Global counter for error modals to help with debugging
|
37 |
_ERROR_MODAL_COUNTER = 0
|
38 |
|
39 |
+
def get_available_api_keys():
|
40 |
+
"""
|
41 |
+
Check which API keys are available in environment variables.
|
42 |
+
|
43 |
+
Returns:
|
44 |
+
dict: Dictionary with 'openai', 'anthropic', and 'invariant' boolean flags
|
45 |
+
"""
|
46 |
+
return {
|
47 |
+
'openai': bool(os.getenv('OPENAI_API_KEY')),
|
48 |
+
'anthropic': bool(os.getenv('ANTHROPIC_API_KEY')),
|
49 |
+
'invariant': bool(os.getenv('INVARIANT_API_KEY'))
|
50 |
+
}
|
51 |
+
|
52 |
+
def get_available_models():
|
53 |
+
"""
|
54 |
+
Get list of available models based on API keys present.
|
55 |
+
|
56 |
+
Returns:
|
57 |
+
tuple: (choices_list, default_model)
|
58 |
+
"""
|
59 |
+
api_keys = get_available_api_keys()
|
60 |
+
|
61 |
+
choices = []
|
62 |
+
|
63 |
+
# OpenAI models
|
64 |
+
if api_keys['openai']:
|
65 |
+
choices.extend(["gpt-4o", "gpt-5"])
|
66 |
+
|
67 |
+
# Anthropic models
|
68 |
+
if api_keys['anthropic']:
|
69 |
+
choices.extend(["claude-3-5-haiku-20241022", "claude-3-5-sonnet-20241022"])
|
70 |
+
|
71 |
+
# Determine default model based on available keys
|
72 |
+
if api_keys['openai']:
|
73 |
+
default_model = "gpt-4o"
|
74 |
+
elif api_keys['anthropic']:
|
75 |
+
default_model = "claude-3-5-sonnet-20241022"
|
76 |
+
else:
|
77 |
+
# No API keys available - we'll handle this in submit function
|
78 |
+
choices = ["No models available"]
|
79 |
+
default_model = "No models available"
|
80 |
+
|
81 |
+
return choices, default_model
|
82 |
+
|
83 |
+
def validate_api_key_for_model(model_name):
|
84 |
+
"""
|
85 |
+
Validate that the required API key is available for the selected model.
|
86 |
+
|
87 |
+
Args:
|
88 |
+
model_name (str): Selected model name
|
89 |
+
|
90 |
+
Returns:
|
91 |
+
tuple: (is_valid, error_message)
|
92 |
+
"""
|
93 |
+
api_keys = get_available_api_keys()
|
94 |
+
|
95 |
+
if model_name.startswith("gpt"):
|
96 |
+
if not api_keys['openai']:
|
97 |
+
return False, "OpenAI API key is required for GPT models. Please add OPENAI_API_KEY to your environment variables."
|
98 |
+
|
99 |
+
elif model_name.startswith("claude"):
|
100 |
+
if not api_keys['anthropic']:
|
101 |
+
return False, "Anthropic API key is required for Claude models. Please add ANTHROPIC_API_KEY to your environment variables."
|
102 |
+
|
103 |
+
elif model_name == "No models available":
|
104 |
+
return False, "No API keys found. Please add either OPENAI_API_KEY or ANTHROPIC_API_KEY to your environment variables to use this application."
|
105 |
+
|
106 |
+
return True, ""
|
107 |
+
|
108 |
+
def validate_invariant_api_key():
|
109 |
+
"""
|
110 |
+
Validate that INVARIANT_API_KEY is available for trace collection.
|
111 |
+
|
112 |
+
Returns:
|
113 |
+
tuple: (is_valid, error_message)
|
114 |
+
"""
|
115 |
+
api_keys = get_available_api_keys()
|
116 |
+
|
117 |
+
if not api_keys['invariant']:
|
118 |
+
return False, "Invariant Labs API key is required for trace collection and analysis. Please add INVARIANT_API_KEY to your environment variables. You can get an API key from https://invariantlabs.ai/"
|
119 |
+
|
120 |
+
return True, ""
|
121 |
+
|
122 |
+
def validate_model_dependencies():
|
123 |
+
"""
|
124 |
+
Validate that critical models can be loaded.
|
125 |
+
|
126 |
+
Returns:
|
127 |
+
tuple: (is_valid, error_message)
|
128 |
+
"""
|
129 |
+
try:
|
130 |
+
# Test FastText model loading
|
131 |
+
model = load_fasttext_model()
|
132 |
+
if model is None:
|
133 |
+
return False, "FastText language detection model failed to load. This is required for input validation."
|
134 |
+
except Exception as e:
|
135 |
+
return False, f"FastText model loading error: {str(e)}. Language detection is required for the application to function."
|
136 |
+
|
137 |
+
try:
|
138 |
+
# Test instruction classifier loading (only if defense would be enabled)
|
139 |
+
from instruction_classifier import get_sanitizer
|
140 |
+
sanitizer = get_sanitizer()
|
141 |
+
if sanitizer is None:
|
142 |
+
return False, "Instruction classifier model failed to load. This is required for defense system functionality."
|
143 |
+
except Exception as e:
|
144 |
+
return False, f"Instruction classifier loading error: {str(e)}. Defense system requires this model to function properly."
|
145 |
+
|
146 |
+
return True, ""
|
147 |
+
|
148 |
def validate_english_only_windowed(text, model=None):
|
149 |
"""
|
150 |
Validates that input text is English-only using sliding window approach.
|
|
|
761 |
for i, email in enumerate(INBOX):
|
762 |
reset_emails.append(format_single_email(email, i + 1))
|
763 |
|
764 |
+
# Get current default model based on available API keys
|
765 |
+
_, default_model = get_available_models()
|
766 |
+
|
767 |
return (
|
768 |
"", # Clear attack subject
|
769 |
"", # Clear attack body
|
770 |
+
default_model, # Reset model to current default
|
771 |
"", # Final output cleared
|
772 |
"π Interface reset to initial state", # Trace message in accordion
|
773 |
reset_emails[0], # Reset email 1
|
|
|
1224 |
with gr.Group(elem_id="defense-toggle-container"):
|
1225 |
with gr.Row(elem_id="defense-toggle-row"):
|
1226 |
with gr.Column(scale=2, min_width=200):
|
1227 |
+
# Get available models based on API keys
|
1228 |
+
available_choices, default_model = get_available_models()
|
1229 |
model_selector = gr.Dropdown(
|
1230 |
+
choices=available_choices,
|
1231 |
+
value=default_model,
|
1232 |
label="Select Agent LLM",
|
1233 |
elem_id="model-selector"
|
1234 |
)
|
1235 |
with gr.Column(scale=2, min_width=100):
|
1236 |
defense_toggle = gr.Checkbox(label="Instruction Tagger Defense", value=True, elem_id="defense-toggle")
|
1237 |
|
1238 |
+
system_display = gr.Textbox(
|
1239 |
+
value=SYSTEM_PROMPT,
|
1240 |
lines=2,
|
1241 |
+
interactive=False,
|
1242 |
+
show_copy_button=True,
|
1243 |
label="System Prompt"
|
1244 |
+
)
|
1245 |
+
|
1246 |
|
1247 |
+
user_input_display = gr.Textbox(
|
1248 |
+
value=USER_INPUT,
|
1249 |
lines=1,
|
1250 |
+
interactive=False,
|
1251 |
+
show_copy_button=True,
|
1252 |
label="User Query (Fixed)"
|
1253 |
+
)
|
1254 |
|
1255 |
# (Moved defense toggle to top; removed previous placement)
|
1256 |
|
|
|
1490 |
except Exception as e:
|
1491 |
validation_errors.append(f"EMAIL BODY: Validation failed - {str(e)}")
|
1492 |
|
1493 |
+
|
1494 |
+
|
1495 |
+
# 4. Validate API key for selected model
|
1496 |
+
is_api_valid, api_error_msg = validate_api_key_for_model(model)
|
1497 |
+
if not is_api_valid:
|
1498 |
+
validation_errors.append(f"API CONFIGURATION: {api_error_msg}")
|
1499 |
+
|
1500 |
+
# 5. Validate Invariant API key for trace collection
|
1501 |
+
is_invariant_valid, invariant_error_msg = validate_invariant_api_key()
|
1502 |
+
if not is_invariant_valid:
|
1503 |
+
validation_errors.append(f"TRACE COLLECTION: {invariant_error_msg}")
|
1504 |
+
|
1505 |
+
# 6. Validate critical model dependencies
|
1506 |
+
is_models_valid, models_error_msg = validate_model_dependencies()
|
1507 |
+
if not is_models_valid:
|
1508 |
+
validation_errors.append(f"MODEL LOADING: {models_error_msg}")
|
1509 |
+
|
1510 |
+
# If there are validation errors (including API key), show them all in the popup
|
1511 |
if validation_errors:
|
1512 |
error_timestamp = int(time.time() * 1000)
|
1513 |
print(f"π¨ VALIDATION ERRORS FOUND: {len(validation_errors)} errors at {error_timestamp}")
|
|
|
1533 |
"subject_confidence_scores": subject_confidence_scores,
|
1534 |
"body_confidence_scores": body_confidence_scores
|
1535 |
}
|
1536 |
+
|
1537 |
+
try:
|
1538 |
+
exec_log, final_out = submit_attack(from_addr.strip(), subject, body, model, defense_enabled, user_info.strip(), confidence_scores)
|
1539 |
+
except Exception as e:
|
1540 |
+
# Handle any setup or execution errors with detailed messages
|
1541 |
+
error_str = str(e).lower()
|
1542 |
+
original_error = str(e)
|
1543 |
+
|
1544 |
+
# Categorize errors and provide specific guidance
|
1545 |
+
if "fasttext" in error_str or "lid.176.bin" in error_str:
|
1546 |
+
setup_error_msg = f"LANGUAGE MODEL ERROR: FastText language detection model failed to load. {original_error}"
|
1547 |
+
setup_error_msg += " This could be due to corrupted model file, insufficient memory, or missing dependencies. Try refreshing the page or contact support if the issue persists."
|
1548 |
+
|
1549 |
+
elif "instruction_classifier" in error_str or "instruction classifier" in error_str or "sanitizer" in error_str:
|
1550 |
+
setup_error_msg = f"DEFENSE MODEL ERROR: Instruction classifier model failed to load. {original_error}"
|
1551 |
+
setup_error_msg += " The defense system requires a working instruction classifier. This could be due to model file corruption, insufficient GPU memory, or missing dependencies."
|
1552 |
+
|
1553 |
+
elif "api_key" in error_str or "api key" in error_str or "authentication" in error_str or "unauthorized" in error_str:
|
1554 |
+
setup_error_msg = f"API AUTHENTICATION ERROR: {original_error}"
|
1555 |
+
setup_error_msg += " Please verify your API keys are correct and have sufficient credits/permissions."
|
1556 |
+
|
1557 |
+
elif "model" in error_str and ("not found" in error_str or "unavailable" in error_str or "invalid" in error_str):
|
1558 |
+
setup_error_msg = f"MODEL AVAILABILITY ERROR: {original_error}"
|
1559 |
+
setup_error_msg += " The selected model may be temporarily unavailable or you may not have access to it. Try a different model."
|
1560 |
+
|
1561 |
+
elif "network" in error_str or "connection" in error_str or "timeout" in error_str or "dns" in error_str:
|
1562 |
+
setup_error_msg = f"NETWORK ERROR: {original_error}"
|
1563 |
+
setup_error_msg += " Please check your internet connection and try again. If the problem persists, the service may be temporarily unavailable."
|
1564 |
+
|
1565 |
+
elif "memory" in error_str or "oom" in error_str or "cuda" in error_str or "gpu" in error_str:
|
1566 |
+
setup_error_msg = f"RESOURCE ERROR: {original_error}"
|
1567 |
+
setup_error_msg += " Insufficient system resources (memory/GPU). Try using a smaller model or refreshing the page."
|
1568 |
+
|
1569 |
+
elif "import" in error_str or "module" in error_str or "dependency" in error_str:
|
1570 |
+
setup_error_msg = f"DEPENDENCY ERROR: {original_error}"
|
1571 |
+
setup_error_msg += " Missing required dependencies. Please ensure all required packages are installed."
|
1572 |
+
|
1573 |
+
elif "permission" in error_str or "access" in error_str or "denied" in error_str:
|
1574 |
+
setup_error_msg = f"PERMISSION ERROR: {original_error}"
|
1575 |
+
setup_error_msg += " File system permission issue. Contact administrator if running on shared system."
|
1576 |
+
|
1577 |
+
else:
|
1578 |
+
# Generic catch-all with enhanced information
|
1579 |
+
setup_error_msg = f"RUNTIME ERROR: {original_error}"
|
1580 |
+
setup_error_msg += " An unexpected error occurred during execution. Please try again, and if the problem persists, check the browser console for more details or contact support."
|
1581 |
+
|
1582 |
+
error_timestamp = int(time.time() * 1000)
|
1583 |
+
print(f"π¨ RUNTIME ERROR: {original_error} at {error_timestamp}")
|
1584 |
+
print(f"π Error category: {setup_error_msg.split(':')[0]}")
|
1585 |
+
|
1586 |
+
modal_html = create_error_modal_html([setup_error_msg])
|
1587 |
+
return (
|
1588 |
+
gr.update(), # final_output_display - no change
|
1589 |
+
gr.update(), # results_display - no change
|
1590 |
+
gr.update(), # trace_display - no change
|
1591 |
+
gr.update(), # email1_display - no change
|
1592 |
+
gr.update(), # email2_display - no change
|
1593 |
+
gr.update(), # email3_display - no change
|
1594 |
+
gr.update(value=modal_html, visible=True) # error_modal_html
|
1595 |
+
)
|
1596 |
|
1597 |
# Build a formatted results summary extracted from exec_log
|
1598 |
def build_results_html(log_text: str) -> str:
|