File size: 6,033 Bytes
461adca 3e63a13 461adca 3e63a13 b79a815 461adca a09e8cb 461adca 3e63a13 461adca 381cd06 461adca 381cd06 3e63a13 381cd06 461adca ca73321 461adca a09e8cb 461adca a09e8cb | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 | #!/usr/bin/env python3
"""
Hugging Face Spaces entry point for Legal Position AI Analyzer
"""
import os
import sys
import warnings
import logging
from pathlib import Path
# Suppress asyncio event loop __del__ warnings (known Python 3.11 issue in threaded envs)
warnings.filterwarnings("ignore", message=".*Invalid file descriptor.*")
logging.getLogger("asyncio").setLevel(logging.CRITICAL)
# Suppress "Exception ignored in: BaseEventLoop.__del__" on HF Spaces shutdown
# warnings.filterwarnings does NOT catch these — they go via sys.unraisablehook
_original_unraisablehook = sys.unraisablehook
def _suppress_asyncio_fd_errors(unraisable):
if (unraisable.exc_type is ValueError and
"Invalid file descriptor" in str(unraisable.exc_value)):
return # silently ignore
_original_unraisablehook(unraisable)
sys.unraisablehook = _suppress_asyncio_fd_errors
# Set environment for Hugging Face Spaces
os.environ['GRADIO_SERVER_NAME'] = '0.0.0.0'
os.environ['GRADIO_SERVER_PORT'] = '7860'
# Avoid uvloop shutdown warnings on HF Spaces
os.environ.setdefault('UVICORN_LOOP', 'asyncio')
# Apply nest_asyncio only if needed (some Python versions have conflicts)
# try:
# import nest_asyncio
# nest_asyncio.apply()
# except Exception as e:
# print(f"[WARNING] Could not apply nest_asyncio: {e}")
# Add project root to Python path
project_root = Path(__file__).parent
sys.path.insert(0, str(project_root))
# ============ Network Diagnostics ============
def run_network_diagnostics():
"""Check outbound network connectivity from HF Spaces container."""
import urllib.request
import socket
print("=" * 50)
print("🔍 NETWORK DIAGNOSTICS")
print("=" * 50)
# Check proxy env vars
proxy_vars = ['HTTP_PROXY', 'HTTPS_PROXY', 'http_proxy', 'https_proxy', 'NO_PROXY', 'no_proxy', 'ALL_PROXY']
print("\n📡 Proxy environment variables:")
for var in proxy_vars:
val = os.environ.get(var)
if val:
print(f" {var} = {val}")
if not any(os.environ.get(v) for v in proxy_vars):
print(" (none set)")
# Check DNS resolution
hosts = ['api.anthropic.com', 'api.openai.com', 'generativelanguage.googleapis.com']
print("\n🌐 DNS resolution:")
for host in hosts:
try:
ip = socket.gethostbyname(host)
print(f" ✅ {host} -> {ip}")
except socket.gaierror as e:
print(f" ❌ {host} -> DNS FAILED: {e}")
# Check actual HTTP(S) connectivity
print("\n🔌 HTTPS connectivity:")
test_urls = [
('https://api.anthropic.com', 'Anthropic API'),
('https://api.openai.com', 'OpenAI API'),
('https://httpbin.org/get', 'httpbin (general internet)'),
]
for url, name in test_urls:
try:
req = urllib.request.Request(url, method='HEAD')
req.add_header('User-Agent', 'connectivity-test/1.0')
resp = urllib.request.urlopen(req, timeout=10)
print(f" ✅ {name} ({url}) -> HTTP {resp.status}")
except urllib.error.HTTPError as e:
# HTTP error means we CAN connect (just got an error response)
print(f" ✅ {name} ({url}) -> HTTP {e.code} (connection OK, auth expected)")
except Exception as e:
print(f" ❌ {name} ({url}) -> {type(e).__name__}: {e}")
# Check httpx (used by anthropic/openai SDKs)
print("\n🔧 httpx connectivity test:")
try:
import httpx
print(f" httpx version: {httpx.__version__}")
# Test with default settings
with httpx.Client(timeout=10) as client:
resp = client.get("https://api.anthropic.com")
print(f" ✅ httpx (default) -> Anthropic HTTP {resp.status_code}")
# Test OpenAI with HTTP/2 disabled (avoids 421 Misdirected Request)
with httpx.Client(timeout=10, http2=False) as client:
resp = client.get("https://api.openai.com")
print(f" ✅ httpx (http1.1) -> OpenAI HTTP {resp.status_code}")
# Also test with default HTTP/2 to compare
try:
with httpx.Client(timeout=10) as client:
resp = client.get("https://api.openai.com")
print(f" ✅ httpx (default) -> OpenAI HTTP {resp.status_code}")
except Exception as e2:
print(f" ⚠️ httpx (default/http2) -> OpenAI FAILED: {type(e2).__name__}: {e2}")
except Exception as e:
print(f" ❌ httpx -> {type(e).__name__}: {e}")
print("=" * 50)
# ============ End Diagnostics ============
# Import and launch interface
from interface import create_gradio_interface
from main import initialize_components
# Initialize search components at module level (required for HF Spaces)
# On HF Spaces, __main__ block never runs, so this must be called here.
print("Initializing search components...")
_init_ok = initialize_components()
if _init_ok:
print("Search components initialized successfully!")
else:
print("[WARNING] Search components initialization failed. Search functionality will be limited.")
# Create Gradio interface (at module level for HF Spaces)
demo = create_gradio_interface()
if __name__ == "__main__":
# Run diagnostics only when executed directly
run_network_diagnostics()
print("🚀 Starting Legal Position AI Analyzer...")
# Detect if running on HF Spaces or locally
is_hf_space = os.environ.get('SPACE_ID') is not None
# Must call launch() explicitly — Gradio 6 does not auto-launch.
# ssr_mode=False avoids the "shareable link" error on HF Spaces containers.
if is_hf_space:
# On HF Spaces, use fixed port 7860
demo.launch(
server_name="0.0.0.0",
server_port=7860,
share=False,
show_error=True,
ssr_mode=False,
)
else:
# Locally, let Gradio find an available port
demo.launch(
share=False,
show_error=True,
)
|