milwright commited on
Commit
aaf6589
Β·
1 Parent(s): 18fef2d

Enhance deployment package with robust API key validation and error handling

Browse files

Features 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

Files changed (1) hide show
  1. app.py +157 -8
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
- return "Please set your {api_key_var} in the Space settings."
 
 
 
 
 
 
 
 
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
- return response.json()['choices'][0]['message']['content']
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
327
  else:
328
- return f"Error: {{response.status_code}} - {{response.text}}"
 
 
 
 
 
 
 
329
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
330
  except Exception as e:
331
- return f"Error: {{str(e)}}"
 
 
 
 
 
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")