|
| 1 | +#!/bin/bash |
| 2 | +# Debug script for testing Bedrock gateway connectivity |
| 3 | +# Tests SSL certificates, API key, and endpoint accessibility |
| 4 | + |
| 5 | +set -e |
| 6 | + |
| 7 | +# Colors for output |
| 8 | +RED='\033[0;31m' |
| 9 | +GREEN='\033[0;32m' |
| 10 | +YELLOW='\033[1;33m' |
| 11 | +BLUE='\033[0;34m' |
| 12 | +NC='\033[0m' # No Color |
| 13 | + |
| 14 | +print_header() { |
| 15 | + echo "" |
| 16 | + echo -e "${GREEN}========================================${NC}" |
| 17 | + echo -e "${GREEN}$1${NC}" |
| 18 | + echo -e "${GREEN}========================================${NC}" |
| 19 | + echo "" |
| 20 | +} |
| 21 | + |
| 22 | +print_success() { echo -e "${GREEN}✓ $1${NC}"; } |
| 23 | +print_warning() { echo -e "${YELLOW}⚠ $1${NC}"; } |
| 24 | +print_error() { echo -e "${RED}✗ $1${NC}"; } |
| 25 | +print_info() { echo -e "${BLUE}ℹ $1${NC}"; } |
| 26 | + |
| 27 | +# Helper function to list available models |
| 28 | +list_available_models() { |
| 29 | + local base_url=$1 |
| 30 | + |
| 31 | + echo "" |
| 32 | + print_info "Checking available models at: $base_url/v1/models" |
| 33 | + echo "" |
| 34 | + |
| 35 | + response=$(curl -s -w "\nHTTP_STATUS:%{http_code}" \ |
| 36 | + --cacert "$SSL_CERT_FILE" \ |
| 37 | + -X GET "$base_url/v1/models" \ |
| 38 | + -H "Authorization: Bearer $OPENAI_API_KEY" \ |
| 39 | + -H "Content-Type: application/json" \ |
| 40 | + --max-time 30 2>&1) |
| 41 | + |
| 42 | + http_status=$(echo "$response" | grep "HTTP_STATUS" | cut -d: -f2) |
| 43 | + body=$(echo "$response" | sed '/HTTP_STATUS/d') |
| 44 | + |
| 45 | + if [ "$http_status" = "200" ]; then |
| 46 | + print_success "Successfully retrieved model list (HTTP $http_status)" |
| 47 | + echo "" |
| 48 | + print_info "Available models:" |
| 49 | + echo "$body" | python3 -c " |
| 50 | +import json |
| 51 | +import sys |
| 52 | +try: |
| 53 | + data = json.load(sys.stdin) |
| 54 | + models = data.get('data', []) |
| 55 | + if models: |
| 56 | + for model in models: |
| 57 | + model_id = model.get('id', 'N/A') |
| 58 | + owned_by = model.get('owned_by', 'N/A') |
| 59 | + print(f' • {model_id} (owned by: {owned_by})') |
| 60 | + else: |
| 61 | + print(' No models found in response') |
| 62 | +except Exception as e: |
| 63 | + print(f' Error parsing model list: {e}') |
| 64 | +" 2>/dev/null || echo "$body" |
| 65 | + else |
| 66 | + print_error "Failed to retrieve model list (HTTP $http_status)" |
| 67 | + echo "$body" | python3 -m json.tool 2>/dev/null || echo "$body" |
| 68 | + fi |
| 69 | + echo "" |
| 70 | +} |
| 71 | + |
| 72 | +# Test 1: Check SSL Certificate |
| 73 | +print_header "Test 1: SSL Certificate Check" |
| 74 | + |
| 75 | +if [ -f "$SSL_CERT_FILE" ]; then |
| 76 | + print_success "Certificate file exists: $SSL_CERT_FILE" |
| 77 | + |
| 78 | + # Check if readable |
| 79 | + if [ -r "$SSL_CERT_FILE" ]; then |
| 80 | + print_success "Certificate file is readable" |
| 81 | + |
| 82 | + # Count certificates in bundle |
| 83 | + cert_count=$(grep -c "BEGIN CERTIFICATE" "$SSL_CERT_FILE" || echo "0") |
| 84 | + print_info "Certificate bundle contains $cert_count certificate(s)" |
| 85 | + else |
| 86 | + print_error "Certificate file is not readable" |
| 87 | + exit 1 |
| 88 | + fi |
| 89 | +else |
| 90 | + print_error "Certificate file not found: $SSL_CERT_FILE" |
| 91 | + exit 1 |
| 92 | +fi |
| 93 | + |
| 94 | +# Test 2: Check Environment Variables |
| 95 | +print_header "Test 2: Environment Variables" |
| 96 | + |
| 97 | +echo "Checking SSL certificate variables:" |
| 98 | +if [ ! -z "$SSL_CERT_FILE" ]; then |
| 99 | + print_success "SSL_CERT_FILE: $SSL_CERT_FILE" |
| 100 | +else |
| 101 | + print_warning "SSL_CERT_FILE not set" |
| 102 | + export SSL_CERT_FILE="$SSL_CERT_FILE" |
| 103 | + print_info "Set SSL_CERT_FILE=$SSL_CERT_FILE" |
| 104 | +fi |
| 105 | + |
| 106 | +if [ ! -z "$REQUESTS_CA_BUNDLE" ]; then |
| 107 | + print_success "REQUESTS_CA_BUNDLE: $REQUESTS_CA_BUNDLE" |
| 108 | +else |
| 109 | + print_warning "REQUESTS_CA_BUNDLE not set" |
| 110 | + export REQUESTS_CA_BUNDLE="$SSL_CERT_FILE" |
| 111 | + print_info "Set REQUESTS_CA_BUNDLE=$SSL_CERT_FILE" |
| 112 | +fi |
| 113 | + |
| 114 | +if [ ! -z "$CURL_CA_BUNDLE" ]; then |
| 115 | + print_success "CURL_CA_BUNDLE: $CURL_CA_BUNDLE" |
| 116 | +else |
| 117 | + print_warning "CURL_CA_BUNDLE not set" |
| 118 | + export CURL_CA_BUNDLE="$SSL_CERT_FILE" |
| 119 | + print_info "Set CURL_CA_BUNDLE=$SSL_CERT_FILE" |
| 120 | +fi |
| 121 | + |
| 122 | +echo "" |
| 123 | +echo "Checking API key:" |
| 124 | +if [ ! -z "$OPENAI_API_KEY" ]; then |
| 125 | + masked_key=$(echo $OPENAI_API_KEY | sed 's/\(.\{5\}\).*\(.\{4\}\)/\1*************\2/') |
| 126 | + print_success "OPENAI_API_KEY: $masked_key" |
| 127 | +else |
| 128 | + print_error "OPENAI_API_KEY not set" |
| 129 | + print_info "Please set: export OPENAI_API_KEY='your-key-here'" |
| 130 | + exit 1 |
| 131 | +fi |
| 132 | + |
| 133 | +# Test 3: Load Configuration |
| 134 | +print_header "Test 3: Configuration Check" |
| 135 | + |
| 136 | +CONFIG_FILE="config/providers/openai.local.yml" |
| 137 | +if [ -f "$CONFIG_FILE" ]; then |
| 138 | + print_success "Found config file: $CONFIG_FILE" |
| 139 | + |
| 140 | + # Extract base URLs |
| 141 | + llm_base_url=$(grep -A 5 "^llm:" "$CONFIG_FILE" | grep "base_url:" | head -1 | awk '{print $2}' | tr -d '"') |
| 142 | + embedder_base_url=$(grep -A 5 "^embedder:" "$CONFIG_FILE" | grep "base_url:" | head -1 | awk '{print $2}' | tr -d '"') |
| 143 | + llm_model=$(grep -A 5 "^llm:" "$CONFIG_FILE" | grep "model:" | head -1 | awk '{print $2}' | tr -d '"') |
| 144 | + embedder_model=$(grep -A 5 "^embedder:" "$CONFIG_FILE" | grep "model:" | head -1 | awk '{print $2}' | tr -d '"') |
| 145 | + |
| 146 | + print_info "LLM Base URL: $llm_base_url" |
| 147 | + print_info "LLM Model: $llm_model" |
| 148 | + print_info "Embedder Base URL: $embedder_base_url" |
| 149 | + print_info "Embedder Model: $embedder_model" |
| 150 | +else |
| 151 | + print_error "Config file not found: $CONFIG_FILE" |
| 152 | + exit 1 |
| 153 | +fi |
| 154 | + |
| 155 | +# Test 4: Test SSL Connection with curl |
| 156 | +print_header "Test 4: SSL Connection Test (curl)" |
| 157 | + |
| 158 | +GATEWAY_HOST="eng-ai-model-gateway.sfproxy.devx-preprod.aws-esvc1-useast2.aws.sfdc.cl" |
| 159 | + |
| 160 | +print_info "Testing SSL connection to: $GATEWAY_HOST" |
| 161 | +echo "" |
| 162 | + |
| 163 | +if curl --cacert "$SSL_CERT_FILE" -s -o /dev/null -w "%{http_code}" "https://$GATEWAY_HOST" > /tmp/curl_test.txt 2>&1; then |
| 164 | + status_code=$(cat /tmp/curl_test.txt) |
| 165 | + if [ "$status_code" != "000" ]; then |
| 166 | + print_success "SSL connection successful (HTTP $status_code)" |
| 167 | + else |
| 168 | + print_error "SSL connection failed" |
| 169 | + curl --cacert "$SSL_CERT_FILE" -v "https://$GATEWAY_HOST" 2>&1 | head -20 |
| 170 | + exit 1 |
| 171 | + fi |
| 172 | +else |
| 173 | + print_error "curl command failed" |
| 174 | + exit 1 |
| 175 | +fi |
| 176 | + |
| 177 | +# Test 5: Test LLM Endpoint with curl |
| 178 | +print_header "Test 5: LLM Endpoint Test" |
| 179 | + |
| 180 | +print_info "Testing: $llm_base_url/v1/chat/completions" |
| 181 | +echo "" |
| 182 | + |
| 183 | +response=$(curl -s -w "\nHTTP_STATUS:%{http_code}" \ |
| 184 | + --cacert "$SSL_CERT_FILE" \ |
| 185 | + -X POST "$llm_base_url/v1/chat/completions" \ |
| 186 | + -H "Authorization: Bearer $OPENAI_API_KEY" \ |
| 187 | + -H "Content-Type: application/json" \ |
| 188 | + --max-time 30 \ |
| 189 | + -d '{ |
| 190 | + "model": "'"$llm_model"'", |
| 191 | + "messages": [ |
| 192 | + {"role": "user", "content": "Say: Connection test successful"} |
| 193 | + ], |
| 194 | + "max_tokens": 50 |
| 195 | + }' 2>&1) |
| 196 | + |
| 197 | +http_status=$(echo "$response" | grep "HTTP_STATUS" | cut -d: -f2) |
| 198 | +body=$(echo "$response" | sed '/HTTP_STATUS/d') |
| 199 | + |
| 200 | +if [ "$http_status" = "200" ]; then |
| 201 | + print_success "LLM endpoint works! (HTTP $http_status)" |
| 202 | + # Try to extract response content |
| 203 | + content=$(echo "$body" | python3 -c "import json,sys; data=json.load(sys.stdin); print(data.get('choices', [{}])[0].get('message', {}).get('content', 'N/A'))" 2>/dev/null || echo "") |
| 204 | + if [ ! -z "$content" ] && [ "$content" != "N/A" ]; then |
| 205 | + print_info "Response: $content" |
| 206 | + fi |
| 207 | +elif [ "$http_status" = "401" ]; then |
| 208 | + print_error "Authentication failed (HTTP 401)" |
| 209 | + print_info "Your API key may not be valid for this endpoint" |
| 210 | + error_msg=$(echo "$body" | python3 -c "import json,sys; data=json.load(sys.stdin); print(data.get('error', {}).get('message', 'Unknown error'))" 2>/dev/null || echo "") |
| 211 | + if [ ! -z "$error_msg" ]; then |
| 212 | + print_info "Error: $error_msg" |
| 213 | + fi |
| 214 | +elif [ "$http_status" = "404" ]; then |
| 215 | + print_error "Endpoint not found (HTTP 404)" |
| 216 | + print_warning "The /v1/chat/completions path may not exist" |
| 217 | + print_info "Try updating base_url to include the correct path" |
| 218 | + list_available_models "$llm_base_url" |
| 219 | +else |
| 220 | + print_error "Request failed (HTTP $http_status)" |
| 221 | + echo "$body" | python3 -m json.tool 2>/dev/null || echo "$body" |
| 222 | + |
| 223 | + # Check if error message suggests checking available models |
| 224 | + if echo "$body" | grep -q "model"; then |
| 225 | + print_warning "Error mentions model - checking available models..." |
| 226 | + list_available_models "$llm_base_url" |
| 227 | + fi |
| 228 | +fi |
| 229 | + |
| 230 | +# Test 6: Test Embedder Endpoint |
| 231 | +print_header "Test 6: Embedder Endpoint Test" |
| 232 | + |
| 233 | +print_info "Testing: $embedder_base_url/embeddings" |
| 234 | +echo "" |
| 235 | + |
| 236 | +response=$(curl -s -w "\nHTTP_STATUS:%{http_code}" \ |
| 237 | + --cacert "$SSL_CERT_FILE" \ |
| 238 | + -X POST "$embedder_base_url/embeddings" \ |
| 239 | + -H "Authorization: Bearer $OPENAI_API_KEY" \ |
| 240 | + -H "Content-Type: application/json" \ |
| 241 | + --max-time 30 \ |
| 242 | + -d '{ |
| 243 | + "model": "'"$embedder_model"'", |
| 244 | + "input": "Connection test" |
| 245 | + }' 2>&1) |
| 246 | + |
| 247 | +http_status=$(echo "$response" | grep "HTTP_STATUS" | cut -d: -f2) |
| 248 | +body=$(echo "$response" | sed '/HTTP_STATUS/d') |
| 249 | + |
| 250 | +if [ "$http_status" = "200" ]; then |
| 251 | + print_success "Embedder endpoint works! (HTTP $http_status)" |
| 252 | + dimension=$(echo "$body" | python3 -c "import json,sys; data=json.load(sys.stdin); print(len(data.get('data', [{}])[0].get('embedding', [])))" 2>/dev/null || echo "N/A") |
| 253 | + if [ "$dimension" != "N/A" ]; then |
| 254 | + print_info "Embedding dimension: $dimension" |
| 255 | + fi |
| 256 | +elif [ "$http_status" = "401" ]; then |
| 257 | + print_error "Authentication failed (HTTP 401)" |
| 258 | + print_info "Your API key may not be valid for this endpoint" |
| 259 | + list_available_models "$embedder_base_url" |
| 260 | +elif [ "$http_status" = "404" ]; then |
| 261 | + print_error "Endpoint not found (HTTP 404)" |
| 262 | + print_warning "The embeddings path may not exist at this base_url" |
| 263 | + print_info "Try: $llm_base_url/v1/embeddings" |
| 264 | + list_available_models "$embedder_base_url" |
| 265 | +else |
| 266 | + print_error "Request failed (HTTP $http_status)" |
| 267 | + echo "$body" | python3 -m json.tool 2>/dev/null || echo "$body" |
| 268 | + |
| 269 | + # Check if error message suggests checking available models |
| 270 | + if echo "$body" | grep -q "model\|/v1/models"; then |
| 271 | + print_warning "Error mentions models - checking available models..." |
| 272 | + list_available_models "$embedder_base_url" |
| 273 | + fi |
| 274 | +fi |
| 275 | + |
| 276 | +# Test 7: Python SSL Test |
| 277 | +print_header "Test 7: Python SSL Verification" |
| 278 | + |
| 279 | +print_info "Testing Python SSL certificate handling..." |
| 280 | +echo "" |
| 281 | + |
| 282 | +python3 << 'EOF' |
| 283 | +import os |
| 284 | +import sys |
| 285 | +import ssl |
| 286 | +import httpx |
| 287 | +
|
| 288 | +cert_path = os.path.expanduser("$SSL_CERT_FILE") |
| 289 | +gateway_host = "eng-ai-model-gateway.sfproxy.devx-preprod.aws-esvc1-useast2.aws.sfdc.cl" |
| 290 | +
|
| 291 | +print(f"Certificate path: {cert_path}") |
| 292 | +print(f"File exists: {os.path.exists(cert_path)}") |
| 293 | +print("") |
| 294 | +
|
| 295 | +# Test 1: SSL context |
| 296 | +try: |
| 297 | + ssl_context = ssl.create_default_context(cafile=cert_path) |
| 298 | + print("✓ SSL context created successfully") |
| 299 | +except Exception as e: |
| 300 | + print(f"✗ Failed to create SSL context: {e}") |
| 301 | + sys.exit(1) |
| 302 | +
|
| 303 | +# Test 2: httpx with certificate |
| 304 | +print("\nTesting httpx with certificate...") |
| 305 | +try: |
| 306 | + with httpx.Client(verify=cert_path, timeout=10.0) as client: |
| 307 | + response = client.get(f"https://{gateway_host}") |
| 308 | + print(f"✓ Connection successful (HTTP {response.status_code})") |
| 309 | +except httpx.ConnectError as e: |
| 310 | + print(f"✗ Connection failed: {e}") |
| 311 | + sys.exit(1) |
| 312 | +except Exception as e: |
| 313 | + print(f"⚠ Request completed but with error: {e}") |
| 314 | +
|
| 315 | +print("\n✓ Python SSL verification works!") |
| 316 | +EOF |
| 317 | + |
| 318 | +if [ $? -eq 0 ]; then |
| 319 | + print_success "Python can use SSL certificates correctly" |
| 320 | +else |
| 321 | + print_error "Python SSL verification failed" |
| 322 | + exit 1 |
| 323 | +fi |
| 324 | + |
| 325 | +# Summary |
| 326 | +print_header "Debug Summary" |
| 327 | + |
| 328 | +print_success "All connection tests completed!" |
| 329 | +echo "" |
| 330 | +print_info "Next steps:" |
| 331 | +echo " 1. If LLM endpoint failed: Check the base_url path in config" |
| 332 | +echo " 2. If embedder endpoint failed: Check the embedder base_url path" |
| 333 | +echo " 3. If authentication failed: Verify your OPENAI_API_KEY is correct" |
| 334 | +echo " 4. If model errors occurred: Check the available models list shown above" |
| 335 | +echo " 5. Run the actual tests: scripts/test_bedrock_endpoint.sh --connection-only" |
| 336 | +echo "" |
0 commit comments