Spaces:
Running
Running
Enhance deployment package with robust API key validation and error handling
Browse filesFeatures added:
- API key whitespace trimming and validation
- Startup logging with configuration status display
- Enhanced error messages with step-by-step troubleshooting
- Specific error handling for 401, 429, 400, timeout, and connection errors
- Configuration status accordion that opens when API key not configured
- Better logging for debugging API issues
- HTTP headers for better provider compatibility
app.py
CHANGED
@@ -144,8 +144,38 @@ ENABLE_DYNAMIC_URLS = {enable_dynamic_urls}
|
|
144 |
ENABLE_VECTOR_RAG = {enable_vector_rag}
|
145 |
RAG_DATA = {rag_data_json}
|
146 |
|
147 |
-
# Get API key from environment - customizable variable name
|
148 |
API_KEY = os.environ.get("{api_key_var}")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
149 |
|
150 |
def fetch_url_content(url):
|
151 |
"""Fetch and extract text content from a URL using requests and BeautifulSoup"""
|
@@ -261,8 +291,17 @@ else:
|
|
261 |
def generate_response(message, history):
|
262 |
"""Generate response using OpenRouter API"""
|
263 |
|
|
|
264 |
if not API_KEY:
|
265 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
266 |
|
267 |
# Get grounding context
|
268 |
grounding_context = get_grounding_context()
|
@@ -306,29 +345,105 @@ def generate_response(message, history):
|
|
306 |
# Add current message
|
307 |
messages.append({{"role": "user", "content": message}})
|
308 |
|
309 |
-
# Make API request
|
310 |
try:
|
|
|
|
|
|
|
|
|
311 |
response = requests.post(
|
312 |
url="https://openrouter.ai/api/v1/chat/completions",
|
313 |
headers={{
|
314 |
"Authorization": f"Bearer {{API_KEY}}",
|
315 |
-
"Content-Type": "application/json"
|
|
|
|
|
316 |
}},
|
317 |
json={{
|
318 |
"model": MODEL,
|
319 |
"messages": messages,
|
320 |
"temperature": {temperature},
|
321 |
"max_tokens": {max_tokens}
|
322 |
-
}}
|
|
|
323 |
)
|
324 |
|
|
|
|
|
325 |
if response.status_code == 200:
|
326 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
327 |
else:
|
328 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
329 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
330 |
except Exception as e:
|
331 |
-
|
|
|
|
|
|
|
|
|
|
|
332 |
|
333 |
# Access code verification
|
334 |
access_granted = gr.State(False)
|
@@ -369,11 +484,45 @@ def export_conversation(history):
|
|
369 |
|
370 |
return gr.update(value=temp_file, visible=True)
|
371 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
372 |
# Create interface with access code protection
|
373 |
with gr.Blocks(title=SPACE_NAME) as demo:
|
374 |
gr.Markdown(f"# {{SPACE_NAME}}")
|
375 |
gr.Markdown(SPACE_DESCRIPTION)
|
376 |
|
|
|
|
|
|
|
|
|
377 |
# Access code section (shown only if ACCESS_CODE is set)
|
378 |
with gr.Column(visible=bool(ACCESS_CODE)) as access_section:
|
379 |
gr.Markdown("### π Access Required")
|
|
|
144 |
ENABLE_VECTOR_RAG = {enable_vector_rag}
|
145 |
RAG_DATA = {rag_data_json}
|
146 |
|
147 |
+
# Get API key from environment - customizable variable name with validation
|
148 |
API_KEY = os.environ.get("{api_key_var}")
|
149 |
+
if API_KEY:
|
150 |
+
API_KEY = API_KEY.strip() # Remove any whitespace
|
151 |
+
if not API_KEY: # Check if empty after stripping
|
152 |
+
API_KEY = None
|
153 |
+
|
154 |
+
# API Key validation and logging
|
155 |
+
def validate_api_key():
|
156 |
+
"""Validate API key configuration with detailed logging"""
|
157 |
+
if not API_KEY:
|
158 |
+
print(f"β οΈ API KEY CONFIGURATION ERROR:")
|
159 |
+
print(f" Variable name: {api_key_var}")
|
160 |
+
print(f" Status: Not set or empty")
|
161 |
+
print(f" Action needed: Set '{api_key_var}' in HuggingFace Space secrets")
|
162 |
+
print(f" Expected format: sk-or-xxxxxxxxxx")
|
163 |
+
return False
|
164 |
+
elif not API_KEY.startswith('sk-or-'):
|
165 |
+
print(f"β οΈ API KEY FORMAT WARNING:")
|
166 |
+
print(f" Variable name: {api_key_var}")
|
167 |
+
print(f" Current value: {API_KEY[:10]}..." if len(API_KEY) > 10 else API_KEY)
|
168 |
+
print(f" Expected format: sk-or-xxxxxxxxxx")
|
169 |
+
print(f" Note: OpenRouter keys should start with 'sk-or-'")
|
170 |
+
return True # Still try to use it
|
171 |
+
else:
|
172 |
+
print(f"β
API Key configured successfully")
|
173 |
+
print(f" Variable: {api_key_var}")
|
174 |
+
print(f" Format: Valid OpenRouter key")
|
175 |
+
return True
|
176 |
+
|
177 |
+
# Validate on startup
|
178 |
+
API_KEY_VALID = validate_api_key()
|
179 |
|
180 |
def fetch_url_content(url):
|
181 |
"""Fetch and extract text content from a URL using requests and BeautifulSoup"""
|
|
|
291 |
def generate_response(message, history):
|
292 |
"""Generate response using OpenRouter API"""
|
293 |
|
294 |
+
# Enhanced API key validation with helpful messages
|
295 |
if not API_KEY:
|
296 |
+
error_msg = f"π **API Key Required**\\n\\n"
|
297 |
+
error_msg += f"Please configure your OpenRouter API key:\\n"
|
298 |
+
error_msg += f"1. Go to Settings (βοΈ) in your HuggingFace Space\\n"
|
299 |
+
error_msg += f"2. Click 'Variables and secrets'\\n"
|
300 |
+
error_msg += f"3. Add secret: **{api_key_var}**\\n"
|
301 |
+
error_msg += f"4. Value: Your OpenRouter API key (starts with `sk-or-`)\\n\\n"
|
302 |
+
error_msg += f"Get your API key at: https://openrouter.ai/keys"
|
303 |
+
print(f"β API request failed: No API key configured for {api_key_var}")
|
304 |
+
return error_msg
|
305 |
|
306 |
# Get grounding context
|
307 |
grounding_context = get_grounding_context()
|
|
|
345 |
# Add current message
|
346 |
messages.append({{"role": "user", "content": message}})
|
347 |
|
348 |
+
# Make API request with enhanced error handling
|
349 |
try:
|
350 |
+
print(f"π Making API request to OpenRouter...")
|
351 |
+
print(f" Model: {{MODEL}}")
|
352 |
+
print(f" Messages: {{len(messages)}} in conversation")
|
353 |
+
|
354 |
response = requests.post(
|
355 |
url="https://openrouter.ai/api/v1/chat/completions",
|
356 |
headers={{
|
357 |
"Authorization": f"Bearer {{API_KEY}}",
|
358 |
+
"Content-Type": "application/json",
|
359 |
+
"HTTP-Referer": "https://huggingface.co", # Required by some providers
|
360 |
+
"X-Title": "HuggingFace Space" # Helpful for tracking
|
361 |
}},
|
362 |
json={{
|
363 |
"model": MODEL,
|
364 |
"messages": messages,
|
365 |
"temperature": {temperature},
|
366 |
"max_tokens": {max_tokens}
|
367 |
+
}},
|
368 |
+
timeout=30
|
369 |
)
|
370 |
|
371 |
+
print(f"π‘ API Response: {{response.status_code}}")
|
372 |
+
|
373 |
if response.status_code == 200:
|
374 |
+
result = response.json()
|
375 |
+
content = result['choices'][0]['message']['content']
|
376 |
+
print(f"β
API request successful")
|
377 |
+
return content
|
378 |
+
elif response.status_code == 401:
|
379 |
+
error_msg = f"π **Authentication Error**\\n\\n"
|
380 |
+
error_msg += f"Your API key appears to be invalid or expired.\\n\\n"
|
381 |
+
error_msg += f"**Troubleshooting:**\\n"
|
382 |
+
error_msg += f"1. Check that your **{api_key_var}** secret is set correctly\\n"
|
383 |
+
error_msg += f"2. Verify your API key at: https://openrouter.ai/keys\\n"
|
384 |
+
error_msg += f"3. Ensure your key starts with `sk-or-`\\n"
|
385 |
+
error_msg += f"4. Check that you have credits on your OpenRouter account"
|
386 |
+
print(f"β API authentication failed: {{response.status_code}} - {{response.text[:200]}}")
|
387 |
+
return error_msg
|
388 |
+
elif response.status_code == 429:
|
389 |
+
error_msg = f"β±οΈ **Rate Limit Exceeded**\\n\\n"
|
390 |
+
error_msg += f"Too many requests. Please wait a moment and try again.\\n\\n"
|
391 |
+
error_msg += f"**Troubleshooting:**\\n"
|
392 |
+
error_msg += f"1. Wait 30-60 seconds before trying again\\n"
|
393 |
+
error_msg += f"2. Check your OpenRouter usage limits\\n"
|
394 |
+
error_msg += f"3. Consider upgrading your OpenRouter plan"
|
395 |
+
print(f"β Rate limit exceeded: {{response.status_code}}")
|
396 |
+
return error_msg
|
397 |
+
elif response.status_code == 400:
|
398 |
+
try:
|
399 |
+
error_data = response.json()
|
400 |
+
error_message = error_data.get('error', {{}}).get('message', 'Unknown error')
|
401 |
+
except:
|
402 |
+
error_message = response.text
|
403 |
+
|
404 |
+
error_msg = f"β οΈ **Request Error**\\n\\n"
|
405 |
+
error_msg += f"The API request was invalid:\\n"
|
406 |
+
error_msg += f"`{{error_message}}`\\n\\n"
|
407 |
+
if "model" in error_message.lower():
|
408 |
+
error_msg += f"**Model Issue:** The model `{{MODEL}}` may not be available.\\n"
|
409 |
+
error_msg += f"Try switching to a different model in your Space configuration."
|
410 |
+
print(f"β Bad request: {{response.status_code}} - {{error_message}}")
|
411 |
+
return error_msg
|
412 |
else:
|
413 |
+
error_msg = f"π« **API Error {{response.status_code}}**\\n\\n"
|
414 |
+
error_msg += f"An unexpected error occurred. Please try again.\\n\\n"
|
415 |
+
error_msg += f"If this persists, check:\\n"
|
416 |
+
error_msg += f"1. OpenRouter service status\\n"
|
417 |
+
error_msg += f"2. Your API key and credits\\n"
|
418 |
+
error_msg += f"3. The model availability"
|
419 |
+
print(f"β API error: {{response.status_code}} - {{response.text[:200]}}")
|
420 |
+
return error_msg
|
421 |
|
422 |
+
except requests.exceptions.Timeout:
|
423 |
+
error_msg = f"β° **Request Timeout**\\n\\n"
|
424 |
+
error_msg += f"The API request took too long (30s limit).\\n\\n"
|
425 |
+
error_msg += f"**Troubleshooting:**\\n"
|
426 |
+
error_msg += f"1. Try again with a shorter message\\n"
|
427 |
+
error_msg += f"2. Check your internet connection\\n"
|
428 |
+
error_msg += f"3. Try a different model"
|
429 |
+
print(f"β Request timeout after 30 seconds")
|
430 |
+
return error_msg
|
431 |
+
except requests.exceptions.ConnectionError:
|
432 |
+
error_msg = f"π **Connection Error**\\n\\n"
|
433 |
+
error_msg += f"Could not connect to OpenRouter API.\\n\\n"
|
434 |
+
error_msg += f"**Troubleshooting:**\\n"
|
435 |
+
error_msg += f"1. Check your internet connection\\n"
|
436 |
+
error_msg += f"2. Check OpenRouter service status\\n"
|
437 |
+
error_msg += f"3. Try again in a few moments"
|
438 |
+
print(f"β Connection error to OpenRouter API")
|
439 |
+
return error_msg
|
440 |
except Exception as e:
|
441 |
+
error_msg = f"β **Unexpected Error**\\n\\n"
|
442 |
+
error_msg += f"An unexpected error occurred:\\n"
|
443 |
+
error_msg += f"`{{str(e)}}`\\n\\n"
|
444 |
+
error_msg += f"Please try again or contact support if this persists."
|
445 |
+
print(f"β Unexpected error: {{str(e)}}")
|
446 |
+
return error_msg
|
447 |
|
448 |
# Access code verification
|
449 |
access_granted = gr.State(False)
|
|
|
484 |
|
485 |
return gr.update(value=temp_file, visible=True)
|
486 |
|
487 |
+
# Configuration status display
|
488 |
+
def get_configuration_status():
|
489 |
+
\"\"\"Generate a configuration status message for display\"\"\"
|
490 |
+
status_parts = []
|
491 |
+
|
492 |
+
if API_KEY_VALID:
|
493 |
+
status_parts.append("β
**API Key:** Configured and valid")
|
494 |
+
else:
|
495 |
+
status_parts.append("β **API Key:** Not configured - Set `{api_key_var}` in Space secrets")
|
496 |
+
|
497 |
+
status_parts.append(f"π€ **Model:** {{MODEL}}")
|
498 |
+
status_parts.append(f"π‘οΈ **Temperature:** {temperature}")
|
499 |
+
status_parts.append(f"π **Max Tokens:** {max_tokens}")
|
500 |
+
|
501 |
+
if GROUNDING_URLS:
|
502 |
+
status_parts.append(f"π **URL Grounding:** {{len(GROUNDING_URLS)}} URLs configured")
|
503 |
+
|
504 |
+
if ENABLE_DYNAMIC_URLS:
|
505 |
+
status_parts.append("π **Dynamic URLs:** Enabled")
|
506 |
+
|
507 |
+
if ENABLE_VECTOR_RAG:
|
508 |
+
status_parts.append("π **Document RAG:** Enabled")
|
509 |
+
|
510 |
+
if ACCESS_CODE:
|
511 |
+
status_parts.append("π **Access Control:** Enabled")
|
512 |
+
else:
|
513 |
+
status_parts.append("π **Access:** Public")
|
514 |
+
|
515 |
+
return "\\n".join(status_parts)
|
516 |
+
|
517 |
# Create interface with access code protection
|
518 |
with gr.Blocks(title=SPACE_NAME) as demo:
|
519 |
gr.Markdown(f"# {{SPACE_NAME}}")
|
520 |
gr.Markdown(SPACE_DESCRIPTION)
|
521 |
|
522 |
+
# Configuration status (always visible)
|
523 |
+
with gr.Accordion("π Configuration Status", open=not API_KEY_VALID):
|
524 |
+
gr.Markdown(get_configuration_status())
|
525 |
+
|
526 |
# Access code section (shown only if ACCESS_CODE is set)
|
527 |
with gr.Column(visible=bool(ACCESS_CODE)) as access_section:
|
528 |
gr.Markdown("### π Access Required")
|