Spaces:
Sleeping
Sleeping
mystic_CBK commited on
Commit ·
f71b308
1
Parent(s): a76f477
Fix ECG-FM deployment: Update Dockerfile, server.py, requirements.txt with fairseq-signals integration
Browse files- FAIRSEQ_SIGNALS_DEPLOYMENT.md +1 -0
- app.py +4 -4
- create_compatible_env.py +0 -230
- create_compatible_env_py313.py +0 -296
- deploy_hf_spaces_final.ps1 +0 -185
- deploy_hf_spaces_final.sh +1 -0
- deploy_simple.ps1 +0 -73
- requirements.txt +5 -11
- requirements_hf_spaces.txt +0 -23
- requirements_hf_spaces_py39.txt +0 -25
- server.py +42 -71
- test_dependencies_lightweight.py +0 -213
- test_env_simple.py +0 -102
- test_fairseq_signals.py +1 -0
- test_local_docker.py +0 -180
- test_omegaconf_fix.py +0 -126
- verify_versions.py +0 -154
FAIRSEQ_SIGNALS_DEPLOYMENT.md
CHANGED
|
@@ -239,3 +239,4 @@ RUN pip install --no-cache-dir torch==1.13.1+cpu torchvision==0.14.1+cpu torchau
|
|
| 239 |
✅ **Complete model architecture** with interpretation heads
|
| 240 |
|
| 241 |
**This is a game-changer** - from 25% to 80%+ clinical accuracy!
|
|
|
|
|
|
| 239 |
✅ **Complete model architecture** with interpretation heads
|
| 240 |
|
| 241 |
**This is a game-changer** - from 25% to 80%+ clinical accuracy!
|
| 242 |
+
|
app.py
CHANGED
|
@@ -4,12 +4,12 @@ Hugging Face Spaces Entry Point for ECG-FM API
|
|
| 4 |
This file serves as the main entry point for HF Spaces deployment
|
| 5 |
"""
|
| 6 |
|
| 7 |
-
#
|
| 8 |
-
# This file is specifically for Hugging Face Spaces deployment
|
| 9 |
-
|
| 10 |
-
import uvicorn
|
| 11 |
from server import app
|
| 12 |
|
|
|
|
|
|
|
| 13 |
if __name__ == "__main__":
|
|
|
|
| 14 |
uvicorn.run(app, host="0.0.0.0", port=7860)
|
| 15 |
|
|
|
|
| 4 |
This file serves as the main entry point for HF Spaces deployment
|
| 5 |
"""
|
| 6 |
|
| 7 |
+
# Import our main server application
|
|
|
|
|
|
|
|
|
|
| 8 |
from server import app
|
| 9 |
|
| 10 |
+
# HF Spaces will automatically detect this as a FastAPI app
|
| 11 |
+
# and serve it on the configured port
|
| 12 |
if __name__ == "__main__":
|
| 13 |
+
import uvicorn
|
| 14 |
uvicorn.run(app, host="0.0.0.0", port=7860)
|
| 15 |
|
create_compatible_env.py
DELETED
|
@@ -1,230 +0,0 @@
|
|
| 1 |
-
#!/usr/bin/env python3
|
| 2 |
-
"""
|
| 3 |
-
Create Compatible Environment Script
|
| 4 |
-
===================================
|
| 5 |
-
|
| 6 |
-
Creates a compatible Python environment for ECG-FM testing.
|
| 7 |
-
This addresses the version incompatibilities found in the lightweight test.
|
| 8 |
-
"""
|
| 9 |
-
|
| 10 |
-
import subprocess
|
| 11 |
-
import sys
|
| 12 |
-
import os
|
| 13 |
-
|
| 14 |
-
def check_python_version():
|
| 15 |
-
"""Check current Python version"""
|
| 16 |
-
version = sys.version_info
|
| 17 |
-
print(f"🐍 Current Python: {version.major}.{version.minor}.{version.micro}")
|
| 18 |
-
|
| 19 |
-
if version.major == 3 and 8 <= version.minor <= 11:
|
| 20 |
-
print("✅ Python version is compatible with HF Spaces")
|
| 21 |
-
return True
|
| 22 |
-
else:
|
| 23 |
-
print("❌ Python version NOT compatible with HF Spaces")
|
| 24 |
-
print(" Need Python 3.8-3.11, have 3.13.3")
|
| 25 |
-
return False
|
| 26 |
-
|
| 27 |
-
def create_virtual_env():
|
| 28 |
-
"""Create a virtual environment with compatible Python"""
|
| 29 |
-
print("\n🔧 Creating Virtual Environment")
|
| 30 |
-
print("=" * 40)
|
| 31 |
-
|
| 32 |
-
# Check if virtualenv is available
|
| 33 |
-
try:
|
| 34 |
-
result = subprocess.run([sys.executable, '-m', 'pip', 'install', 'virtualenv'],
|
| 35 |
-
capture_output=True, text=True)
|
| 36 |
-
if result.returncode == 0:
|
| 37 |
-
print("✅ virtualenv installed")
|
| 38 |
-
else:
|
| 39 |
-
print("❌ Failed to install virtualenv")
|
| 40 |
-
return False
|
| 41 |
-
except Exception as e:
|
| 42 |
-
print(f"❌ Error installing virtualenv: {e}")
|
| 43 |
-
return False
|
| 44 |
-
|
| 45 |
-
# Create virtual environment
|
| 46 |
-
env_name = "ecg_fm_env"
|
| 47 |
-
try:
|
| 48 |
-
cmd = [sys.executable, '-m', 'virtualenv', env_name]
|
| 49 |
-
print(f"Creating environment: {env_name}")
|
| 50 |
-
result = subprocess.run(cmd, capture_output=True, text=True)
|
| 51 |
-
|
| 52 |
-
if result.returncode == 0:
|
| 53 |
-
print(f"✅ Virtual environment created: {env_name}")
|
| 54 |
-
return True
|
| 55 |
-
else:
|
| 56 |
-
print("❌ Failed to create virtual environment")
|
| 57 |
-
print(result.stderr)
|
| 58 |
-
return False
|
| 59 |
-
except Exception as e:
|
| 60 |
-
print(f"❌ Error creating environment: {e}")
|
| 61 |
-
return False
|
| 62 |
-
|
| 63 |
-
def install_compatible_packages():
|
| 64 |
-
"""Install compatible package versions"""
|
| 65 |
-
print("\n📦 Installing Compatible Packages")
|
| 66 |
-
print("=" * 40)
|
| 67 |
-
|
| 68 |
-
# These versions are compatible with each other
|
| 69 |
-
packages = [
|
| 70 |
-
"numpy==1.24.3",
|
| 71 |
-
"torch==1.13.1",
|
| 72 |
-
"torchvision==0.14.1",
|
| 73 |
-
"torchaudio==0.13.1",
|
| 74 |
-
"omegaconf==2.3.0",
|
| 75 |
-
"fastapi==0.104.1",
|
| 76 |
-
"uvicorn[standard]==0.24.0",
|
| 77 |
-
"huggingface-hub==0.19.4",
|
| 78 |
-
"transformers==4.21.0",
|
| 79 |
-
"einops==0.7.0",
|
| 80 |
-
"pyyaml==6.0.1"
|
| 81 |
-
]
|
| 82 |
-
|
| 83 |
-
# Get the virtual environment Python
|
| 84 |
-
if os.name == 'nt': # Windows
|
| 85 |
-
python_path = os.path.join("ecg_fm_env", "Scripts", "python.exe")
|
| 86 |
-
else: # Unix/Linux
|
| 87 |
-
python_path = os.path.join("ecg_fm_env", "bin", "python")
|
| 88 |
-
|
| 89 |
-
if not os.path.exists(python_path):
|
| 90 |
-
print("❌ Virtual environment Python not found")
|
| 91 |
-
return False
|
| 92 |
-
|
| 93 |
-
print(f"Using Python: {python_path}")
|
| 94 |
-
|
| 95 |
-
all_good = True
|
| 96 |
-
|
| 97 |
-
for package in packages:
|
| 98 |
-
print(f"Installing {package}...")
|
| 99 |
-
try:
|
| 100 |
-
cmd = [python_path, '-m', 'pip', 'install', package]
|
| 101 |
-
result = subprocess.run(cmd, capture_output=True, text=True, timeout=120)
|
| 102 |
-
|
| 103 |
-
if result.returncode == 0:
|
| 104 |
-
print(f" ✅ {package} installed")
|
| 105 |
-
else:
|
| 106 |
-
print(f" ❌ {package} failed")
|
| 107 |
-
print(f" Error: {result.stderr}")
|
| 108 |
-
all_good = False
|
| 109 |
-
|
| 110 |
-
except subprocess.TimeoutExpired:
|
| 111 |
-
print(f" ⚠️ {package} timed out")
|
| 112 |
-
all_good = False
|
| 113 |
-
except Exception as e:
|
| 114 |
-
print(f" ❌ {package} error: {e}")
|
| 115 |
-
all_good = False
|
| 116 |
-
|
| 117 |
-
return all_good
|
| 118 |
-
|
| 119 |
-
def test_compatible_environment():
|
| 120 |
-
"""Test the compatible environment"""
|
| 121 |
-
print("\n🧪 Testing Compatible Environment")
|
| 122 |
-
print("=" * 40)
|
| 123 |
-
|
| 124 |
-
if os.name == 'nt': # Windows
|
| 125 |
-
python_path = os.path.join("ecg_fm_env", "Scripts", "python.exe")
|
| 126 |
-
else: # Unix/Linux
|
| 127 |
-
python_path = os.path.join("ecg_fm_env", "bin", "python")
|
| 128 |
-
|
| 129 |
-
if not os.path.exists(python_path):
|
| 130 |
-
print("❌ Virtual environment not found")
|
| 131 |
-
return False
|
| 132 |
-
|
| 133 |
-
# Test imports
|
| 134 |
-
test_script = f"""
|
| 135 |
-
import sys
|
| 136 |
-
print(f"Python: {{sys.version}}")
|
| 137 |
-
|
| 138 |
-
try:
|
| 139 |
-
import numpy
|
| 140 |
-
print(f"NumPy: {{numpy.__version__}}")
|
| 141 |
-
except ImportError as e:
|
| 142 |
-
print(f"NumPy: ❌ {{e}}")
|
| 143 |
-
|
| 144 |
-
try:
|
| 145 |
-
import torch
|
| 146 |
-
print(f"PyTorch: {{torch.__version__}}")
|
| 147 |
-
except ImportError as e:
|
| 148 |
-
print(f"PyTorch: ❌ {{e}}")
|
| 149 |
-
|
| 150 |
-
try:
|
| 151 |
-
import omegaconf
|
| 152 |
-
print(f"OmegaConf: {{omegaconf.__version__}}")
|
| 153 |
-
except ImportError as e:
|
| 154 |
-
print(f"OmegaConf: ❌ {{e}}")
|
| 155 |
-
|
| 156 |
-
try:
|
| 157 |
-
import fastapi
|
| 158 |
-
print(f"FastAPI: {{fastapi.__version__}}")
|
| 159 |
-
except ImportError as e:
|
| 160 |
-
print(f"FastAPI: ❌ {{e}}")
|
| 161 |
-
|
| 162 |
-
try:
|
| 163 |
-
import transformers
|
| 164 |
-
print(f"Transformers: {{transformers.__version__}}")
|
| 165 |
-
except ImportError as e:
|
| 166 |
-
print(f"Transformers: ❌ {{e}}")
|
| 167 |
-
"""
|
| 168 |
-
|
| 169 |
-
try:
|
| 170 |
-
result = subprocess.run([python_path, '-c', test_script],
|
| 171 |
-
capture_output=True, text=True)
|
| 172 |
-
|
| 173 |
-
if result.returncode == 0:
|
| 174 |
-
print("✅ Environment test successful!")
|
| 175 |
-
print("\n📋 Package Versions:")
|
| 176 |
-
print(result.stdout)
|
| 177 |
-
return True
|
| 178 |
-
else:
|
| 179 |
-
print("❌ Environment test failed!")
|
| 180 |
-
print(result.stderr)
|
| 181 |
-
return False
|
| 182 |
-
|
| 183 |
-
except Exception as e:
|
| 184 |
-
print(f"❌ Test error: {e}")
|
| 185 |
-
return False
|
| 186 |
-
|
| 187 |
-
def main():
|
| 188 |
-
"""Main function"""
|
| 189 |
-
print("🚀 Creating Compatible Environment for ECG-FM")
|
| 190 |
-
print("=" * 60)
|
| 191 |
-
print("This will create a virtual environment with compatible versions")
|
| 192 |
-
print()
|
| 193 |
-
|
| 194 |
-
# Step 1: Check Python version
|
| 195 |
-
if not check_python_version():
|
| 196 |
-
print("\n⚠️ Warning: Python 3.13.3 may cause issues")
|
| 197 |
-
print(" Virtual environment will help isolate dependencies")
|
| 198 |
-
|
| 199 |
-
# Step 2: Create virtual environment
|
| 200 |
-
if not create_virtual_env():
|
| 201 |
-
print("\n❌ Failed to create virtual environment")
|
| 202 |
-
return 1
|
| 203 |
-
|
| 204 |
-
# Step 3: Install compatible packages
|
| 205 |
-
if not install_compatible_packages():
|
| 206 |
-
print("\n❌ Failed to install compatible packages")
|
| 207 |
-
return 1
|
| 208 |
-
|
| 209 |
-
# Step 4: Test environment
|
| 210 |
-
if not test_compatible_environment():
|
| 211 |
-
print("\n❌ Environment test failed")
|
| 212 |
-
return 1
|
| 213 |
-
|
| 214 |
-
print("\n🎉 COMPATIBLE ENVIRONMENT CREATED!")
|
| 215 |
-
print("✅ All packages installed with compatible versions")
|
| 216 |
-
print("🚀 Ready for local testing before HF upload")
|
| 217 |
-
|
| 218 |
-
# Instructions
|
| 219 |
-
print("\n📋 How to use:")
|
| 220 |
-
if os.name == 'nt': # Windows
|
| 221 |
-
print(" Activate: ecg_fm_env\\Scripts\\activate")
|
| 222 |
-
print(" Python: ecg_fm_env\\Scripts\\python.exe")
|
| 223 |
-
else: # Unix/Linux
|
| 224 |
-
print(" Activate: source ecg_fm_env/bin/activate")
|
| 225 |
-
print(" Python: ecg_fm_env/bin/python")
|
| 226 |
-
|
| 227 |
-
return 0
|
| 228 |
-
|
| 229 |
-
if __name__ == "__main__":
|
| 230 |
-
sys.exit(main())
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
create_compatible_env_py313.py
DELETED
|
@@ -1,296 +0,0 @@
|
|
| 1 |
-
#!/usr/bin/env python3
|
| 2 |
-
"""
|
| 3 |
-
Create Compatible Environment for Python 3.13
|
| 4 |
-
============================================
|
| 5 |
-
|
| 6 |
-
Creates a compatible Python environment for ECG-FM testing on Python 3.13.
|
| 7 |
-
Uses newer package versions that are compatible with Python 3.13.
|
| 8 |
-
"""
|
| 9 |
-
|
| 10 |
-
import subprocess
|
| 11 |
-
import sys
|
| 12 |
-
import os
|
| 13 |
-
|
| 14 |
-
def check_python_version():
|
| 15 |
-
"""Check current Python version"""
|
| 16 |
-
version = sys.version_info
|
| 17 |
-
print(f"🐍 Current Python: {version.major}.{version.minor}.{version.micro}")
|
| 18 |
-
|
| 19 |
-
if version.major == 3 and version.minor >= 13:
|
| 20 |
-
print("⚠️ Python 3.13+ detected - Using newer package versions")
|
| 21 |
-
print(" Note: HF Spaces supports Python 3.8-3.11")
|
| 22 |
-
return "py313"
|
| 23 |
-
elif version.major == 3 and 8 <= version.minor <= 11:
|
| 24 |
-
print("✅ Python version is compatible with HF Spaces")
|
| 25 |
-
return "py311"
|
| 26 |
-
else:
|
| 27 |
-
print("❌ Python version not supported")
|
| 28 |
-
return None
|
| 29 |
-
|
| 30 |
-
def create_virtual_env():
|
| 31 |
-
"""Create a virtual environment"""
|
| 32 |
-
print("\n🔧 Creating Virtual Environment")
|
| 33 |
-
print("=" * 40)
|
| 34 |
-
|
| 35 |
-
# Check if virtualenv is available
|
| 36 |
-
try:
|
| 37 |
-
result = subprocess.run([sys.executable, '-m', 'pip', 'install', 'virtualenv'],
|
| 38 |
-
capture_output=True, text=True)
|
| 39 |
-
if result.returncode == 0:
|
| 40 |
-
print("✅ virtualenv installed")
|
| 41 |
-
else:
|
| 42 |
-
print("❌ Failed to install virtualenv")
|
| 43 |
-
return False
|
| 44 |
-
except Exception as e:
|
| 45 |
-
print(f"❌ Error installing virtualenv: {e}")
|
| 46 |
-
return False
|
| 47 |
-
|
| 48 |
-
# Create virtual environment
|
| 49 |
-
env_name = "ecg_fm_env_py313"
|
| 50 |
-
try:
|
| 51 |
-
cmd = [sys.executable, '-m', 'virtualenv', env_name]
|
| 52 |
-
print(f"Creating environment: {env_name}")
|
| 53 |
-
result = subprocess.run(cmd, capture_output=True, text=True)
|
| 54 |
-
|
| 55 |
-
if result.returncode == 0:
|
| 56 |
-
print(f"✅ Virtual environment created: {env_name}")
|
| 57 |
-
return True
|
| 58 |
-
else:
|
| 59 |
-
print("❌ Failed to create virtual environment")
|
| 60 |
-
print(result.stderr)
|
| 61 |
-
return False
|
| 62 |
-
except Exception as e:
|
| 63 |
-
print(f"❌ Error creating environment: {e}")
|
| 64 |
-
return False
|
| 65 |
-
|
| 66 |
-
def install_compatible_packages_py313():
|
| 67 |
-
"""Install Python 3.13 compatible package versions"""
|
| 68 |
-
print("\n📦 Installing Python 3.13 Compatible Packages")
|
| 69 |
-
print("=" * 50)
|
| 70 |
-
|
| 71 |
-
# Python 3.13 compatible versions
|
| 72 |
-
packages = [
|
| 73 |
-
"numpy>=2.0.0", # Python 3.13 compatible
|
| 74 |
-
"torch>=2.6.0", # Latest PyTorch for Python 3.13
|
| 75 |
-
"torchvision>=0.21.0",
|
| 76 |
-
"torchaudio>=2.6.0",
|
| 77 |
-
"omegaconf>=2.3.0", # Has is_primitive_type
|
| 78 |
-
"fastapi>=0.104.1",
|
| 79 |
-
"uvicorn[standard]>=0.24.0",
|
| 80 |
-
"huggingface-hub>=0.19.4",
|
| 81 |
-
"transformers>=4.21.0",
|
| 82 |
-
"einops>=0.7.0",
|
| 83 |
-
"pyyaml>=6.0.1"
|
| 84 |
-
]
|
| 85 |
-
|
| 86 |
-
# Get the virtual environment Python
|
| 87 |
-
if os.name == 'nt': # Windows
|
| 88 |
-
python_path = os.path.join("ecg_fm_env_py313", "Scripts", "python.exe")
|
| 89 |
-
else: # Unix/Linux
|
| 90 |
-
python_path = os.path.join("ecg_fm_env_py313", "bin", "python")
|
| 91 |
-
|
| 92 |
-
if not os.path.exists(python_path):
|
| 93 |
-
print("❌ Virtual environment Python not found")
|
| 94 |
-
return False
|
| 95 |
-
|
| 96 |
-
print(f"Using Python: {python_path}")
|
| 97 |
-
|
| 98 |
-
all_good = True
|
| 99 |
-
|
| 100 |
-
for package in packages:
|
| 101 |
-
print(f"Installing {package}...")
|
| 102 |
-
try:
|
| 103 |
-
cmd = [python_path, '-m', 'pip', 'install', package]
|
| 104 |
-
result = subprocess.run(cmd, capture_output=True, text=True, timeout=180)
|
| 105 |
-
|
| 106 |
-
if result.returncode == 0:
|
| 107 |
-
print(f" ✅ {package} installed")
|
| 108 |
-
else:
|
| 109 |
-
print(f" ❌ {package} failed")
|
| 110 |
-
print(f" Error: {result.stderr}")
|
| 111 |
-
all_good = False
|
| 112 |
-
|
| 113 |
-
except subprocess.TimeoutExpired:
|
| 114 |
-
print(f" ⚠️ {package} timed out")
|
| 115 |
-
all_good = False
|
| 116 |
-
except Exception as e:
|
| 117 |
-
print(f" ❌ {package} error: {e}")
|
| 118 |
-
all_good = False
|
| 119 |
-
|
| 120 |
-
return all_good
|
| 121 |
-
|
| 122 |
-
def test_compatible_environment():
|
| 123 |
-
"""Test the compatible environment"""
|
| 124 |
-
print("\n🧪 Testing Compatible Environment")
|
| 125 |
-
print("=" * 40)
|
| 126 |
-
|
| 127 |
-
if os.name == 'nt': # Windows
|
| 128 |
-
python_path = os.path.join("ecg_fm_env_py313", "Scripts", "python.exe")
|
| 129 |
-
else: # Unix/Linux
|
| 130 |
-
python_path = os.path.join("ecg_fm_env_py313", "bin", "python")
|
| 131 |
-
|
| 132 |
-
if not os.path.exists(python_path):
|
| 133 |
-
print("❌ Virtual environment not found")
|
| 134 |
-
return False
|
| 135 |
-
|
| 136 |
-
# Test imports
|
| 137 |
-
test_script = """
|
| 138 |
-
import sys
|
| 139 |
-
print(f"Python: {sys.version}")
|
| 140 |
-
|
| 141 |
-
try:
|
| 142 |
-
import numpy
|
| 143 |
-
print(f"NumPy: {numpy.__version__}")
|
| 144 |
-
except ImportError as e:
|
| 145 |
-
print(f"NumPy: ❌ {e}")
|
| 146 |
-
|
| 147 |
-
try:
|
| 148 |
-
import torch
|
| 149 |
-
print(f"PyTorch: {torch.__version__}")
|
| 150 |
-
except ImportError as e:
|
| 151 |
-
print(f"PyTorch: ❌ {e}")
|
| 152 |
-
|
| 153 |
-
try:
|
| 154 |
-
import omegaconf
|
| 155 |
-
print(f"OmegaConf: {omegaconf.__version__}")
|
| 156 |
-
# Test if is_primitive_type exists
|
| 157 |
-
if hasattr(omegaconf._utils, 'is_primitive_type'):
|
| 158 |
-
print("✅ omegaconf._utils.is_primitive_type available")
|
| 159 |
-
else:
|
| 160 |
-
print("❌ omegaconf._utils.is_primitive_type NOT available")
|
| 161 |
-
except ImportError as e:
|
| 162 |
-
print(f"OmegaConf: ❌ {e}")
|
| 163 |
-
|
| 164 |
-
try:
|
| 165 |
-
import fastapi
|
| 166 |
-
print(f"FastAPI: {fastapi.__version__}")
|
| 167 |
-
except ImportError as e:
|
| 168 |
-
print(f"FastAPI: ❌ {e}")
|
| 169 |
-
|
| 170 |
-
try:
|
| 171 |
-
import transformers
|
| 172 |
-
print(f"Transformers: {transformers.__version__}")
|
| 173 |
-
except ImportError as e:
|
| 174 |
-
print(f"Transformers: ❌ {e}")
|
| 175 |
-
|
| 176 |
-
try:
|
| 177 |
-
import huggingface_hub
|
| 178 |
-
print(f"HuggingFace Hub: {huggingface_hub.__version__}")
|
| 179 |
-
except ImportError as e:
|
| 180 |
-
print(f"HuggingFace Hub: ❌ {e}")
|
| 181 |
-
"""
|
| 182 |
-
|
| 183 |
-
try:
|
| 184 |
-
result = subprocess.run([python_path, '-c', test_script],
|
| 185 |
-
capture_output=True, text=True)
|
| 186 |
-
|
| 187 |
-
if result.returncode == 0:
|
| 188 |
-
print("✅ Environment test successful!")
|
| 189 |
-
print("\n📋 Package Versions:")
|
| 190 |
-
print(result.stdout)
|
| 191 |
-
return True
|
| 192 |
-
else:
|
| 193 |
-
print("❌ Environment test failed!")
|
| 194 |
-
print(result.stderr)
|
| 195 |
-
return False
|
| 196 |
-
|
| 197 |
-
except Exception as e:
|
| 198 |
-
print(f"❌ Test error: {e}")
|
| 199 |
-
return False
|
| 200 |
-
|
| 201 |
-
def create_hf_compatible_requirements():
|
| 202 |
-
"""Create HF Spaces compatible requirements file"""
|
| 203 |
-
print("\n📝 Creating HF Spaces Compatible Requirements")
|
| 204 |
-
print("=" * 50)
|
| 205 |
-
|
| 206 |
-
# Create requirements file for HF Spaces (Python 3.9)
|
| 207 |
-
hf_requirements = """# HF Spaces Requirements - Python 3.9 Compatible
|
| 208 |
-
# These versions work on HF Spaces with Python 3.9
|
| 209 |
-
|
| 210 |
-
# Core dependencies
|
| 211 |
-
fastapi==0.104.1
|
| 212 |
-
uvicorn[standard]==0.24.0
|
| 213 |
-
huggingface-hub==0.19.4
|
| 214 |
-
pyyaml==6.0.1
|
| 215 |
-
einops==0.7.0
|
| 216 |
-
|
| 217 |
-
# PyTorch 1.13.x - Compatible with Python 3.9 and NumPy 1.24
|
| 218 |
-
torch==1.13.1
|
| 219 |
-
torchvision==0.14.1
|
| 220 |
-
torchaudio==0.13.1
|
| 221 |
-
|
| 222 |
-
# NumPy 1.24.x - Compatible with PyTorch 1.13.x
|
| 223 |
-
numpy==1.24.3
|
| 224 |
-
|
| 225 |
-
# OmegaConf - Version that has is_primitive_type
|
| 226 |
-
omegaconf==2.3.0
|
| 227 |
-
|
| 228 |
-
# Transformers - Compatible version
|
| 229 |
-
transformers==4.21.0
|
| 230 |
-
|
| 231 |
-
# fairseq-signals will be installed from source in Dockerfile
|
| 232 |
-
"""
|
| 233 |
-
|
| 234 |
-
try:
|
| 235 |
-
with open("requirements_hf_spaces_py39.txt", "w") as f:
|
| 236 |
-
f.write(hf_requirements)
|
| 237 |
-
print("✅ Created requirements_hf_spaces_py39.txt")
|
| 238 |
-
print(" This file is compatible with HF Spaces Python 3.9")
|
| 239 |
-
return True
|
| 240 |
-
except Exception as e:
|
| 241 |
-
print(f"❌ Failed to create requirements file: {e}")
|
| 242 |
-
return False
|
| 243 |
-
|
| 244 |
-
def main():
|
| 245 |
-
"""Main function"""
|
| 246 |
-
print("🚀 Creating Python 3.13 Compatible Environment for ECG-FM")
|
| 247 |
-
print("=" * 70)
|
| 248 |
-
print("This will create a virtual environment with Python 3.13 compatible versions")
|
| 249 |
-
print()
|
| 250 |
-
|
| 251 |
-
# Step 1: Check Python version
|
| 252 |
-
py_version = check_python_version()
|
| 253 |
-
if not py_version:
|
| 254 |
-
return 1
|
| 255 |
-
|
| 256 |
-
# Step 2: Create virtual environment
|
| 257 |
-
if not create_virtual_env():
|
| 258 |
-
print("\n❌ Failed to create virtual environment")
|
| 259 |
-
return 1
|
| 260 |
-
|
| 261 |
-
# Step 3: Install compatible packages
|
| 262 |
-
if py_version == "py313":
|
| 263 |
-
if not install_compatible_packages_py313():
|
| 264 |
-
print("\n❌ Failed to install compatible packages")
|
| 265 |
-
return 1
|
| 266 |
-
|
| 267 |
-
# Step 4: Test environment
|
| 268 |
-
if not test_compatible_environment():
|
| 269 |
-
print("\n❌ Environment test failed")
|
| 270 |
-
return 1
|
| 271 |
-
|
| 272 |
-
# Step 5: Create HF compatible requirements
|
| 273 |
-
create_hf_compatible_requirements()
|
| 274 |
-
|
| 275 |
-
print("\n🎉 COMPATIBLE ENVIRONMENT CREATED!")
|
| 276 |
-
print("✅ All packages installed with compatible versions")
|
| 277 |
-
print("🚀 Ready for local testing before HF upload")
|
| 278 |
-
|
| 279 |
-
# Instructions
|
| 280 |
-
print("\n📋 How to use:")
|
| 281 |
-
if os.name == 'nt': # Windows
|
| 282 |
-
print(" Activate: ecg_fm_env_py313\\Scripts\\activate")
|
| 283 |
-
print(" Python: ecg_fm_env_py313\\Scripts\\python.exe")
|
| 284 |
-
else: # Unix/Linux
|
| 285 |
-
print(" Activate: source ecg_fm_env_py313/bin/activate")
|
| 286 |
-
print(" Python: ecg_fm_env_py313/bin/python")
|
| 287 |
-
|
| 288 |
-
print("\n⚠️ IMPORTANT NOTES:")
|
| 289 |
-
print(" - Local environment uses Python 3.13 compatible versions")
|
| 290 |
-
print(" - HF Spaces will use Python 3.9 with requirements_hf_spaces_py39.txt")
|
| 291 |
-
print(" - Test locally first, then upload to HF Spaces")
|
| 292 |
-
|
| 293 |
-
return 0
|
| 294 |
-
|
| 295 |
-
if __name__ == "__main__":
|
| 296 |
-
sys.exit(main())
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
deploy_hf_spaces_final.ps1
DELETED
|
@@ -1,185 +0,0 @@
|
|
| 1 |
-
# 🚀 HF Spaces Deployment Script for ECG-FM with fairseq-signals
|
| 2 |
-
# PowerShell version for Windows
|
| 3 |
-
|
| 4 |
-
param(
|
| 5 |
-
[string]$HFUsername = "your_username"
|
| 6 |
-
)
|
| 7 |
-
|
| 8 |
-
Write-Host "🚀 ECG-FM HF Spaces Deployment with fairseq-signals" -ForegroundColor Green
|
| 9 |
-
Write-Host "==================================================" -ForegroundColor Green
|
| 10 |
-
|
| 11 |
-
# Configuration
|
| 12 |
-
$SpaceName = "ecg-fm-fairseq-signals"
|
| 13 |
-
$RepoUrl = "https://huggingface.co/spaces/$HFUsername/$SpaceName"
|
| 14 |
-
|
| 15 |
-
Write-Host "📋 Configuration:" -ForegroundColor Blue
|
| 16 |
-
Write-Host " Space Name: $SpaceName" -ForegroundColor White
|
| 17 |
-
Write-Host " HF Username: $HFUsername" -ForegroundColor White
|
| 18 |
-
Write-Host " Repository: $RepoUrl" -ForegroundColor White
|
| 19 |
-
Write-Host ""
|
| 20 |
-
|
| 21 |
-
# Check if HF CLI is installed
|
| 22 |
-
try {
|
| 23 |
-
$null = Get-Command huggingface-cli -ErrorAction Stop
|
| 24 |
-
Write-Host "✅ HF CLI check passed" -ForegroundColor Green
|
| 25 |
-
} catch {
|
| 26 |
-
Write-Host "❌ Hugging Face CLI not found" -ForegroundColor Red
|
| 27 |
-
Write-Host "Please install it with: pip install huggingface_hub" -ForegroundColor Yellow
|
| 28 |
-
exit 1
|
| 29 |
-
}
|
| 30 |
-
|
| 31 |
-
# Check if logged in to HF
|
| 32 |
-
try {
|
| 33 |
-
$whoami = huggingface-cli whoami 2>$null
|
| 34 |
-
if ($LASTEXITCODE -eq 0) {
|
| 35 |
-
Write-Host "✅ Logged in to HF as: $whoami" -ForegroundColor Green
|
| 36 |
-
} else {
|
| 37 |
-
throw "Not logged in"
|
| 38 |
-
}
|
| 39 |
-
} catch {
|
| 40 |
-
Write-Host "❌ Not logged in to Hugging Face" -ForegroundColor Red
|
| 41 |
-
Write-Host "Please login with: huggingface-cli login" -ForegroundColor Yellow
|
| 42 |
-
exit 1
|
| 43 |
-
}
|
| 44 |
-
|
| 45 |
-
# Create or clone the space
|
| 46 |
-
if (Test-Path $SpaceName) {
|
| 47 |
-
Write-Host "📁 Space directory exists, updating..." -ForegroundColor Yellow
|
| 48 |
-
Set-Location $SpaceName
|
| 49 |
-
git pull origin main
|
| 50 |
-
} else {
|
| 51 |
-
Write-Host "📁 Creating new HF Space..." -ForegroundColor Blue
|
| 52 |
-
huggingface-cli repo create $SpaceName --type space --space-sdk docker
|
| 53 |
-
git clone "$RepoUrl.git" $SpaceName
|
| 54 |
-
Set-Location $SpaceName
|
| 55 |
-
}
|
| 56 |
-
|
| 57 |
-
Write-Host "✅ HF Space ready" -ForegroundColor Green
|
| 58 |
-
|
| 59 |
-
# Copy deployment files
|
| 60 |
-
Write-Host "📁 Copying deployment files..." -ForegroundColor Blue
|
| 61 |
-
|
| 62 |
-
# Copy core files
|
| 63 |
-
Copy-Item ..\server.py .
|
| 64 |
-
Copy-Item ..\requirements_hf_spaces.txt .
|
| 65 |
-
Copy-Item ..\Dockerfile .
|
| 66 |
-
Copy-Item ..\app.py .
|
| 67 |
-
|
| 68 |
-
# Copy additional files
|
| 69 |
-
Copy-Item ..\test_fairseq_signals.py .
|
| 70 |
-
Copy-Item ..\FAIRSEQ_SIGNALS_DEPLOYMENT.md .
|
| 71 |
-
|
| 72 |
-
# Create .gitattributes for HF Spaces
|
| 73 |
-
@"
|
| 74 |
-
*.md filter=lfs diff=lfs merge=lfs -text
|
| 75 |
-
*.png filter=lfs diff=lfs merge=lfs -text
|
| 76 |
-
*.jpg filter=lfs diff=lfs merge=lfs -text
|
| 77 |
-
*.jpeg filter=lfs diff=lfs merge=lfs -text
|
| 78 |
-
*.gif filter=lfs diff=lfs merge=lfs -text
|
| 79 |
-
*.pdf filter=lfs diff=lfs merge=lfs -text
|
| 80 |
-
*.zip filter=lfs diff=lfs merge=lfs -text
|
| 81 |
-
*.tar.gz filter=lfs diff=lfs merge=lfs -text
|
| 82 |
-
*.pt filter=lfs diff=lfs merge=lfs -text
|
| 83 |
-
*.pth filter=lfs diff=lfs merge=lfs -text
|
| 84 |
-
*.bin filter=lfs diff=lfs merge=lfs -text
|
| 85 |
-
*.safetensors filter=lfs diff=lfs merge=lfs -text
|
| 86 |
-
"@ | Out-File -FilePath .gitattributes -Encoding UTF8
|
| 87 |
-
|
| 88 |
-
# Create README for the space
|
| 89 |
-
@"
|
| 90 |
-
# ECG-FM API with fairseq-signals
|
| 91 |
-
|
| 92 |
-
## 🎯 Overview
|
| 93 |
-
This is the official ECG-FM implementation using fairseq-signals for full clinical interpretation capabilities.
|
| 94 |
-
|
| 95 |
-
## 🚀 Features
|
| 96 |
-
- **Full ECG-FM functionality** (not just features)
|
| 97 |
-
- **Clinical interpretation** and abnormality detection
|
| 98 |
-
- **Confidence scoring** for clinical decisions
|
| 99 |
-
- **Research-proven accuracy** (80-95%)
|
| 100 |
-
|
| 101 |
-
## 🔧 Technical Details
|
| 102 |
-
- **Model**: wanglab/ecg-fm
|
| 103 |
-
- **Implementation**: fairseq-signals (official)
|
| 104 |
-
- **Framework**: FastAPI + PyTorch
|
| 105 |
-
- **Deployment**: Docker on HF Spaces
|
| 106 |
-
|
| 107 |
-
## 📊 API Endpoints
|
| 108 |
-
- `GET /` - API information
|
| 109 |
-
- `GET /healthz` - Health check
|
| 110 |
-
- `POST /predict` - ECG analysis
|
| 111 |
-
|
| 112 |
-
## 🎉 Expected Results
|
| 113 |
-
- **Before**: 25% accuracy (features only)
|
| 114 |
-
- **After**: 80%+ accuracy (full clinical interpretation)
|
| 115 |
-
- **Improvement**: 3x better clinical value
|
| 116 |
-
|
| 117 |
-
## 🚨 Important Notes
|
| 118 |
-
- This is for research/development purposes
|
| 119 |
-
- Clinical validation required for medical use
|
| 120 |
-
- Always consult healthcare professionals
|
| 121 |
-
"@ | Out-File -FilePath README.md -Encoding UTF8
|
| 122 |
-
|
| 123 |
-
Write-Host "✅ Deployment files copied" -ForegroundColor Green
|
| 124 |
-
|
| 125 |
-
# Verify critical files
|
| 126 |
-
Write-Host "🔍 Verifying critical files..." -ForegroundColor Blue
|
| 127 |
-
if (-not (Test-Path "server.py")) {
|
| 128 |
-
Write-Host "❌ server.py not found" -ForegroundColor Red
|
| 129 |
-
exit 1
|
| 130 |
-
}
|
| 131 |
-
|
| 132 |
-
if (-not (Test-Path "requirements_hf_spaces.txt")) {
|
| 133 |
-
Write-Host "❌ requirements_hf_spaces.txt not found" -ForegroundColor Red
|
| 134 |
-
exit 1
|
| 135 |
-
}
|
| 136 |
-
|
| 137 |
-
if (-not (Test-Path "Dockerfile")) {
|
| 138 |
-
Write-Host "❌ Dockerfile not found" -ForegroundColor Red
|
| 139 |
-
exit 1
|
| 140 |
-
}
|
| 141 |
-
|
| 142 |
-
Write-Host "✅ All critical files verified" -ForegroundColor Green
|
| 143 |
-
|
| 144 |
-
# Check git status
|
| 145 |
-
Write-Host "📊 Git status:" -ForegroundColor Blue
|
| 146 |
-
git status
|
| 147 |
-
|
| 148 |
-
# Add all files
|
| 149 |
-
Write-Host "📝 Adding files to git..." -ForegroundColor Blue
|
| 150 |
-
git add .
|
| 151 |
-
|
| 152 |
-
# Commit changes
|
| 153 |
-
Write-Host "💾 Committing changes..." -ForegroundColor Blue
|
| 154 |
-
git commit -m "🚀 Deploy ECG-FM with fairseq-signals - Full clinical interpretation
|
| 155 |
-
|
| 156 |
-
- Updated server.py to use fairseq_signals.models
|
| 157 |
-
- Fixed version compatibility (PyTorch 1.13.1, omegaconf 1.4.1)
|
| 158 |
-
- Enhanced Dockerfile with proper fairseq-signals installation
|
| 159 |
-
- Added comprehensive error handling and monitoring
|
| 160 |
-
- Expected: 3x accuracy improvement (25% to 80%+)"
|
| 161 |
-
|
| 162 |
-
# Push to trigger build
|
| 163 |
-
Write-Host "🚀 Pushing to HF Spaces..." -ForegroundColor Blue
|
| 164 |
-
git push origin main
|
| 165 |
-
|
| 166 |
-
Write-Host ""
|
| 167 |
-
Write-Host "🎉 Deployment completed successfully!" -ForegroundColor Green
|
| 168 |
-
Write-Host ""
|
| 169 |
-
Write-Host "📋 Next steps:" -ForegroundColor Blue
|
| 170 |
-
Write-Host "1. Monitor build at: https://huggingface.co/spaces/$HFUsername/$SpaceName" -ForegroundColor White
|
| 171 |
-
Write-Host "2. Wait for build completion (10-15 minutes)" -ForegroundColor White
|
| 172 |
-
Write-Host "3. Test API endpoints" -ForegroundColor White
|
| 173 |
-
Write-Host "4. Verify fairseq_signals implementation" -ForegroundColor White
|
| 174 |
-
Write-Host ""
|
| 175 |
-
Write-Host "⚠️ Important:" -ForegroundColor Yellow
|
| 176 |
-
Write-Host "- Build may take 10-15 minutes for first deployment" -ForegroundColor White
|
| 177 |
-
Write-Host "- Monitor build logs for any issues" -ForegroundColor White
|
| 178 |
-
Write-Host "- Verify fairseq_signals installation in build logs" -ForegroundColor White
|
| 179 |
-
Write-Host ""
|
| 180 |
-
Write-Host "🎯 Expected Results:" -ForegroundColor Green
|
| 181 |
-
Write-Host "- fairseq_signals import successful" -ForegroundColor White
|
| 182 |
-
Write-Host "- Full ECG-FM clinical interpretation" -ForegroundColor White
|
| 183 |
-
Write-Host "- 3x accuracy improvement" -ForegroundColor White
|
| 184 |
-
Write-Host ""
|
| 185 |
-
Write-Host "🔗 Space URL: https://huggingface.co/spaces/$HFUsername/$SpaceName" -ForegroundColor Blue
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
deploy_hf_spaces_final.sh
CHANGED
|
@@ -183,3 +183,4 @@ echo "- Full ECG-FM clinical interpretation"
|
|
| 183 |
echo "- 3x accuracy improvement"
|
| 184 |
echo ""
|
| 185 |
echo -e "${BLUE}🔗 Space URL:${NC} https://huggingface.co/spaces/${HF_USERNAME}/${SPACE_NAME}"
|
|
|
|
|
|
| 183 |
echo "- 3x accuracy improvement"
|
| 184 |
echo ""
|
| 185 |
echo -e "${BLUE}🔗 Space URL:${NC} https://huggingface.co/spaces/${HF_USERNAME}/${SPACE_NAME}"
|
| 186 |
+
|
deploy_simple.ps1
DELETED
|
@@ -1,73 +0,0 @@
|
|
| 1 |
-
# Simple HF Spaces Deployment Script
|
| 2 |
-
param([string]$HFUsername = "mystic-cbk")
|
| 3 |
-
|
| 4 |
-
Write-Host "🚀 Starting HF Spaces Deployment..." -ForegroundColor Green
|
| 5 |
-
|
| 6 |
-
$SpaceName = "ecg-fm-fairseq-signals"
|
| 7 |
-
$RepoUrl = "https://huggingface.co/spaces/$HFUsername/$SpaceName"
|
| 8 |
-
|
| 9 |
-
Write-Host "Space: $SpaceName" -ForegroundColor Blue
|
| 10 |
-
Write-Host "Username: $HFUsername" -ForegroundColor Blue
|
| 11 |
-
|
| 12 |
-
# Create or clone the space
|
| 13 |
-
if (Test-Path $SpaceName) {
|
| 14 |
-
Write-Host "Updating existing space..." -ForegroundColor Yellow
|
| 15 |
-
Set-Location $SpaceName
|
| 16 |
-
git pull origin main
|
| 17 |
-
} else {
|
| 18 |
-
Write-Host "Creating new HF Space..." -ForegroundColor Blue
|
| 19 |
-
|
| 20 |
-
# Use Python API to create space
|
| 21 |
-
python -c "
|
| 22 |
-
from huggingface_hub import HfApi
|
| 23 |
-
api = HfApi()
|
| 24 |
-
try:
|
| 25 |
-
api.create_repo(repo_id='$HFUsername/$SpaceName', repo_type='space', space_sdk='docker')
|
| 26 |
-
print('Space created successfully!')
|
| 27 |
-
except Exception as e:
|
| 28 |
-
print(f'Error creating space: {e}')
|
| 29 |
-
"
|
| 30 |
-
|
| 31 |
-
git clone "$RepoUrl.git" $SpaceName
|
| 32 |
-
Set-Location $SpaceName
|
| 33 |
-
}
|
| 34 |
-
|
| 35 |
-
Write-Host "Space ready!" -ForegroundColor Green
|
| 36 |
-
|
| 37 |
-
# Copy files
|
| 38 |
-
Write-Host "Copying deployment files..." -ForegroundColor Blue
|
| 39 |
-
Copy-Item ..\server.py .
|
| 40 |
-
Copy-Item ..\requirements_hf_spaces.txt .
|
| 41 |
-
Copy-Item ..\Dockerfile .
|
| 42 |
-
Copy-Item ..\app.py .
|
| 43 |
-
|
| 44 |
-
# Create README
|
| 45 |
-
@"
|
| 46 |
-
# ECG-FM API with fairseq-signals
|
| 47 |
-
|
| 48 |
-
Full clinical interpretation capabilities using official fairseq-signals implementation.
|
| 49 |
-
|
| 50 |
-
## Features
|
| 51 |
-
- Full ECG-FM functionality (not just features)
|
| 52 |
-
- Clinical interpretation and abnormality detection
|
| 53 |
-
- Confidence scoring for clinical decisions
|
| 54 |
-
- Research-proven accuracy (80-95%)
|
| 55 |
-
|
| 56 |
-
## Expected Results
|
| 57 |
-
- Before: 25% accuracy (features only)
|
| 58 |
-
- After: 80%+ accuracy (full clinical interpretation)
|
| 59 |
-
- Improvement: 3x better clinical value
|
| 60 |
-
"@ | Out-File -FilePath README.md -Encoding UTF8
|
| 61 |
-
|
| 62 |
-
Write-Host "Files copied!" -ForegroundColor Green
|
| 63 |
-
|
| 64 |
-
# Git operations
|
| 65 |
-
Write-Host "Git operations..." -ForegroundColor Blue
|
| 66 |
-
git add .
|
| 67 |
-
git commit -m "Deploy ECG-FM with fairseq-signals - Full clinical interpretation"
|
| 68 |
-
git push origin main
|
| 69 |
-
|
| 70 |
-
Write-Host ""
|
| 71 |
-
Write-Host "🎉 Deployment completed!" -ForegroundColor Green
|
| 72 |
-
Write-Host "Monitor build at: https://huggingface.co/spaces/$HFUsername/$SpaceName" -ForegroundColor Blue
|
| 73 |
-
Write-Host "Expected: 3x accuracy improvement (25% to 80%+)" -ForegroundColor Yellow
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
requirements.txt
CHANGED
|
@@ -1,5 +1,3 @@
|
|
| 1 |
-
# ECG-FM API Requirements - Aligned with Dockerfile
|
| 2 |
-
# Core dependencies
|
| 3 |
fastapi
|
| 4 |
uvicorn[standard]
|
| 5 |
numpy<2.0
|
|
@@ -7,12 +5,8 @@ huggingface-hub
|
|
| 7 |
pyyaml
|
| 8 |
einops
|
| 9 |
transformers==4.21.0
|
| 10 |
-
|
| 11 |
-
#
|
| 12 |
-
|
| 13 |
-
|
| 14 |
-
|
| 15 |
-
torchaudio>=0.13.0,<0.15.0
|
| 16 |
-
|
| 17 |
-
# fairseq-signals will be installed in Docker with specific version constraints
|
| 18 |
-
# This ensures we get the official ECG-FM implementation
|
|
|
|
|
|
|
|
|
|
| 1 |
fastapi
|
| 2 |
uvicorn[standard]
|
| 3 |
numpy<2.0
|
|
|
|
| 5 |
pyyaml
|
| 6 |
einops
|
| 7 |
transformers==4.21.0
|
| 8 |
+
# fairseq will be installed in Docker with specific version constraints
|
| 9 |
+
# Alternative: torch and torchaudio for model loading fallback
|
| 10 |
+
torch>=2.0.0
|
| 11 |
+
torchaudio>=2.0.0
|
| 12 |
+
# PyTorch will be installed separately in Dockerfile for Python 3.9 compatibility
|
|
|
|
|
|
|
|
|
|
|
|
requirements_hf_spaces.txt
DELETED
|
@@ -1,23 +0,0 @@
|
|
| 1 |
-
# HF Spaces Requirements - SYSTEMATIC FIX
|
| 2 |
-
# Based on official PyTorch compatibility matrix
|
| 3 |
-
|
| 4 |
-
# Core dependencies - Exact versions
|
| 5 |
-
fastapi==0.104.1
|
| 6 |
-
uvicorn[standard]==0.24.0
|
| 7 |
-
huggingface-hub==0.19.4
|
| 8 |
-
pyyaml==6.0.1
|
| 9 |
-
einops==0.7.0
|
| 10 |
-
transformers==4.21.0
|
| 11 |
-
|
| 12 |
-
# CRITICAL: NumPy 1.24.3 for PyTorch 1.13.x compatibility
|
| 13 |
-
numpy==1.24.3
|
| 14 |
-
|
| 15 |
-
# PyTorch 1.13.x - Compatible with NumPy 1.21-1.24
|
| 16 |
-
torch==1.13.1
|
| 17 |
-
torchvision==0.14.1
|
| 18 |
-
torchaudio==0.13.1
|
| 19 |
-
|
| 20 |
-
# omegaconf - Version that has is_primitive_type (CRITICAL!)
|
| 21 |
-
omegaconf==2.0.0
|
| 22 |
-
|
| 23 |
-
# fairseq-signals will be installed from source in Dockerfile
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
requirements_hf_spaces_py39.txt
DELETED
|
@@ -1,25 +0,0 @@
|
|
| 1 |
-
# HF Spaces Requirements - Python 3.9 Compatible
|
| 2 |
-
# These versions work on HF Spaces with Python 3.9
|
| 3 |
-
|
| 4 |
-
# Core dependencies
|
| 5 |
-
fastapi==0.104.1
|
| 6 |
-
uvicorn[standard]==0.24.0
|
| 7 |
-
huggingface-hub==0.19.4
|
| 8 |
-
pyyaml==6.0.1
|
| 9 |
-
einops==0.7.0
|
| 10 |
-
|
| 11 |
-
# PyTorch 1.13.x - Compatible with Python 3.9 and NumPy 1.24
|
| 12 |
-
torch==1.13.1
|
| 13 |
-
torchvision==0.14.1
|
| 14 |
-
torchaudio==0.13.1
|
| 15 |
-
|
| 16 |
-
# NumPy 1.24.x - Compatible with PyTorch 1.13.x
|
| 17 |
-
numpy==1.24.3
|
| 18 |
-
|
| 19 |
-
# OmegaConf - Version that has is_primitive_type (CRITICAL!)
|
| 20 |
-
omegaconf==2.0.0
|
| 21 |
-
|
| 22 |
-
# Transformers - Compatible version
|
| 23 |
-
transformers==4.21.0
|
| 24 |
-
|
| 25 |
-
# fairseq-signals will be installed from source in Dockerfile
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
server.py
CHANGED
|
@@ -6,53 +6,45 @@ from pydantic import BaseModel
|
|
| 6 |
from typing import List, Optional
|
| 7 |
from huggingface_hub import hf_hub_download
|
| 8 |
|
| 9 |
-
# Import fairseq
|
| 10 |
-
|
| 11 |
build_model_from_checkpoint = None
|
| 12 |
|
| 13 |
try:
|
| 14 |
-
|
| 15 |
-
|
| 16 |
-
|
| 17 |
-
|
| 18 |
-
except ImportError as e:
|
| 19 |
-
print(f"⚠️ fairseq_signals import failed: {e}")
|
| 20 |
try:
|
| 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 |
-
else:
|
| 51 |
-
print("⚠️ Checkpoint format not recognized, returning raw checkpoint")
|
| 52 |
-
return checkpoint
|
| 53 |
-
except Exception as e:
|
| 54 |
-
print(f"❌ Failed to load checkpoint: {e}")
|
| 55 |
-
raise
|
| 56 |
|
| 57 |
# Configuration
|
| 58 |
MODEL_REPO = os.getenv("MODEL_REPO", "wanglab/ecg-fm")
|
|
@@ -70,7 +62,7 @@ model_loaded = False
|
|
| 70 |
|
| 71 |
def load_model():
|
| 72 |
print(f"🔄 Loading model from {MODEL_REPO}...")
|
| 73 |
-
print(f"📦
|
| 74 |
|
| 75 |
try:
|
| 76 |
# Only download the checkpoint - config is embedded inside
|
|
@@ -83,10 +75,6 @@ def load_model():
|
|
| 83 |
if hasattr(m, 'eval'):
|
| 84 |
m.eval()
|
| 85 |
print("✅ Model loaded successfully and set to eval mode!")
|
| 86 |
-
if fairseq_signals_available:
|
| 87 |
-
print("🎉 Using OFFICIAL fairseq-signals implementation - Full ECG-FM functionality!")
|
| 88 |
-
else:
|
| 89 |
-
print("⚠️ Using fallback implementation - Limited functionality")
|
| 90 |
else:
|
| 91 |
print("⚠️ Model loaded but no eval() method - may be raw checkpoint")
|
| 92 |
|
|
@@ -114,8 +102,7 @@ def root():
|
|
| 114 |
"message": "ECG-FM API is running",
|
| 115 |
"model": MODEL_REPO,
|
| 116 |
"status": "Model loaded" if model_loaded else "Model not loaded",
|
| 117 |
-
"
|
| 118 |
-
"implementation": "Official fairseq-signals" if fairseq_signals_available else "Fallback implementation"
|
| 119 |
}
|
| 120 |
|
| 121 |
@app.get("/healthz")
|
|
@@ -123,8 +110,7 @@ def healthz():
|
|
| 123 |
return {
|
| 124 |
"status": "ok",
|
| 125 |
"model_loaded": model_loaded,
|
| 126 |
-
"
|
| 127 |
-
"implementation": "Official fairseq-signals" if fairseq_signals_available else "Fallback implementation"
|
| 128 |
}
|
| 129 |
|
| 130 |
@app.post("/predict")
|
|
@@ -133,8 +119,7 @@ def predict(p: ECGPayload):
|
|
| 133 |
return {
|
| 134 |
"error": "Model not loaded",
|
| 135 |
"status": "unavailable",
|
| 136 |
-
"
|
| 137 |
-
"implementation": "Official fairseq-signals" if fairseq_signals_available else "Fallback implementation"
|
| 138 |
}
|
| 139 |
|
| 140 |
try:
|
|
@@ -157,33 +142,19 @@ def predict(p: ECGPayload):
|
|
| 157 |
y = y.detach()
|
| 158 |
|
| 159 |
out = torch.as_tensor(y).cpu().numpy().tolist()
|
| 160 |
-
|
| 161 |
-
# Enhanced response with implementation details
|
| 162 |
-
response = {
|
| 163 |
"output": out,
|
| 164 |
"input_shape": x.shape,
|
| 165 |
"output_shape": [len(out)],
|
| 166 |
-
"
|
| 167 |
-
"implementation": "Official fairseq-signals" if fairseq_signals_available else "Fallback implementation"
|
| 168 |
}
|
| 169 |
|
| 170 |
-
# Add clinical interpretation if using fairseq-signals
|
| 171 |
-
if fairseq_signals_available:
|
| 172 |
-
response["note"] = "Full ECG-FM clinical interpretation available"
|
| 173 |
-
response["capabilities"] = ["Feature extraction", "Clinical interpretation", "Abnormality detection", "Confidence scoring"]
|
| 174 |
-
else:
|
| 175 |
-
response["note"] = "Limited to feature extraction only - using fallback implementation"
|
| 176 |
-
response["capabilities"] = ["Feature extraction only"]
|
| 177 |
-
|
| 178 |
-
return response
|
| 179 |
-
|
| 180 |
except Exception as e:
|
| 181 |
print(f"❌ Prediction error: {e}")
|
| 182 |
return {
|
| 183 |
"error": str(e),
|
| 184 |
"status": "prediction_failed",
|
| 185 |
-
"
|
| 186 |
-
"implementation": "Official fairseq-signals" if fairseq_signals_available else "Fallback implementation"
|
| 187 |
}
|
| 188 |
|
| 189 |
if __name__ == "__main__":
|
|
|
|
| 6 |
from typing import List, Optional
|
| 7 |
from huggingface_hub import hf_hub_download
|
| 8 |
|
| 9 |
+
# Import fairseq with robust fallback logic
|
| 10 |
+
fairseq_available = False
|
| 11 |
build_model_from_checkpoint = None
|
| 12 |
|
| 13 |
try:
|
| 14 |
+
from fairseq.models import build_model_from_checkpoint
|
| 15 |
+
print("✅ Successfully imported build_model_from_checkpoint from fairseq.models")
|
| 16 |
+
fairseq_available = True
|
| 17 |
+
except ImportError:
|
|
|
|
|
|
|
| 18 |
try:
|
| 19 |
+
from fairseq import checkpoint_utils
|
| 20 |
+
print("⚠️ Using fairseq.checkpoint_utils as fallback")
|
| 21 |
+
# Create a wrapper function for compatibility
|
| 22 |
+
def build_model_from_checkpoint(ckpt):
|
| 23 |
+
models, args, task = checkpoint_utils.load_model_ensemble_and_task([ckpt])
|
| 24 |
+
return models[0]
|
| 25 |
+
fairseq_available = True
|
| 26 |
+
except ImportError as e:
|
| 27 |
+
print(f"❌ Could not import fairseq: {e}")
|
| 28 |
+
print("🔄 Running in fallback mode - will use alternative model loading")
|
| 29 |
+
|
| 30 |
+
# Alternative model loading approach
|
| 31 |
+
def build_model_from_checkpoint(ckpt):
|
| 32 |
+
print(f"🔄 Attempting to load checkpoint: {ckpt}")
|
| 33 |
+
try:
|
| 34 |
+
# Try to load as PyTorch checkpoint
|
| 35 |
+
checkpoint = torch.load(ckpt, map_location='cpu')
|
| 36 |
+
if 'model' in checkpoint:
|
| 37 |
+
print("✅ Loaded PyTorch checkpoint with 'model' key")
|
| 38 |
+
return checkpoint['model']
|
| 39 |
+
elif 'state_dict' in checkpoint:
|
| 40 |
+
print("✅ Loaded PyTorch checkpoint with 'state_dict' key")
|
| 41 |
+
return checkpoint['state_dict']
|
| 42 |
+
else:
|
| 43 |
+
print("⚠️ Checkpoint format not recognized, returning raw checkpoint")
|
| 44 |
+
return checkpoint
|
| 45 |
+
except Exception as e:
|
| 46 |
+
print(f"❌ Failed to load checkpoint: {e}")
|
| 47 |
+
raise
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 48 |
|
| 49 |
# Configuration
|
| 50 |
MODEL_REPO = os.getenv("MODEL_REPO", "wanglab/ecg-fm")
|
|
|
|
| 62 |
|
| 63 |
def load_model():
|
| 64 |
print(f"🔄 Loading model from {MODEL_REPO}...")
|
| 65 |
+
print(f"📦 fairseq available: {fairseq_available}")
|
| 66 |
|
| 67 |
try:
|
| 68 |
# Only download the checkpoint - config is embedded inside
|
|
|
|
| 75 |
if hasattr(m, 'eval'):
|
| 76 |
m.eval()
|
| 77 |
print("✅ Model loaded successfully and set to eval mode!")
|
|
|
|
|
|
|
|
|
|
|
|
|
| 78 |
else:
|
| 79 |
print("⚠️ Model loaded but no eval() method - may be raw checkpoint")
|
| 80 |
|
|
|
|
| 102 |
"message": "ECG-FM API is running",
|
| 103 |
"model": MODEL_REPO,
|
| 104 |
"status": "Model loaded" if model_loaded else "Model not loaded",
|
| 105 |
+
"fairseq_available": fairseq_available
|
|
|
|
| 106 |
}
|
| 107 |
|
| 108 |
@app.get("/healthz")
|
|
|
|
| 110 |
return {
|
| 111 |
"status": "ok",
|
| 112 |
"model_loaded": model_loaded,
|
| 113 |
+
"fairseq_available": fairseq_available
|
|
|
|
| 114 |
}
|
| 115 |
|
| 116 |
@app.post("/predict")
|
|
|
|
| 119 |
return {
|
| 120 |
"error": "Model not loaded",
|
| 121 |
"status": "unavailable",
|
| 122 |
+
"fairseq_available": fairseq_available
|
|
|
|
| 123 |
}
|
| 124 |
|
| 125 |
try:
|
|
|
|
| 142 |
y = y.detach()
|
| 143 |
|
| 144 |
out = torch.as_tensor(y).cpu().numpy().tolist()
|
| 145 |
+
return {
|
|
|
|
|
|
|
| 146 |
"output": out,
|
| 147 |
"input_shape": x.shape,
|
| 148 |
"output_shape": [len(out)],
|
| 149 |
+
"fairseq_available": fairseq_available
|
|
|
|
| 150 |
}
|
| 151 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 152 |
except Exception as e:
|
| 153 |
print(f"❌ Prediction error: {e}")
|
| 154 |
return {
|
| 155 |
"error": str(e),
|
| 156 |
"status": "prediction_failed",
|
| 157 |
+
"fairseq_available": fairseq_available
|
|
|
|
| 158 |
}
|
| 159 |
|
| 160 |
if __name__ == "__main__":
|
test_dependencies_lightweight.py
DELETED
|
@@ -1,213 +0,0 @@
|
|
| 1 |
-
#!/usr/bin/env python3
|
| 2 |
-
"""
|
| 3 |
-
Lightweight Dependency Testing
|
| 4 |
-
=============================
|
| 5 |
-
|
| 6 |
-
Test dependencies locally without Docker for systems with limited resources.
|
| 7 |
-
This catches most dependency issues before HF upload.
|
| 8 |
-
"""
|
| 9 |
-
|
| 10 |
-
import sys
|
| 11 |
-
import subprocess
|
| 12 |
-
import importlib
|
| 13 |
-
|
| 14 |
-
def test_python_version():
|
| 15 |
-
"""Test Python version compatibility"""
|
| 16 |
-
print("🐍 Testing Python Version")
|
| 17 |
-
print("=" * 40)
|
| 18 |
-
|
| 19 |
-
version = sys.version_info
|
| 20 |
-
print(f"Current: {version.major}.{version.minor}.{version.micro}")
|
| 21 |
-
|
| 22 |
-
if version.major == 3 and 8 <= version.minor <= 11:
|
| 23 |
-
print("✅ Python version compatible with HF Spaces")
|
| 24 |
-
return True
|
| 25 |
-
else:
|
| 26 |
-
print("❌ Python version not compatible with HF Spaces")
|
| 27 |
-
print(" HF Spaces supports Python 3.8-3.11")
|
| 28 |
-
return False
|
| 29 |
-
|
| 30 |
-
def test_package_installation():
|
| 31 |
-
"""Test if packages can be installed"""
|
| 32 |
-
print("\n📦 Testing Package Installation")
|
| 33 |
-
print("=" * 40)
|
| 34 |
-
|
| 35 |
-
packages = [
|
| 36 |
-
('numpy', '1.24.3'),
|
| 37 |
-
('torch', '1.13.1'),
|
| 38 |
-
('omegaconf', '2.3.0'),
|
| 39 |
-
('fastapi', '0.104.1'),
|
| 40 |
-
('uvicorn', '0.24.0'),
|
| 41 |
-
('huggingface-hub', '0.19.4'),
|
| 42 |
-
('transformers', '4.21.0')
|
| 43 |
-
]
|
| 44 |
-
|
| 45 |
-
all_good = True
|
| 46 |
-
|
| 47 |
-
for package, version in packages:
|
| 48 |
-
print(f"Testing {package}=={version}...")
|
| 49 |
-
|
| 50 |
-
try:
|
| 51 |
-
# Try to install the specific version
|
| 52 |
-
cmd = [sys.executable, '-m', 'pip', 'install', f'{package}=={version}']
|
| 53 |
-
result = subprocess.run(cmd, capture_output=True, text=True, timeout=60)
|
| 54 |
-
|
| 55 |
-
if result.returncode == 0:
|
| 56 |
-
print(f" ✅ {package}=={version} installed successfully")
|
| 57 |
-
else:
|
| 58 |
-
print(f" ❌ {package}=={version} failed to install")
|
| 59 |
-
print(f" Error: {result.stderr}")
|
| 60 |
-
all_good = False
|
| 61 |
-
|
| 62 |
-
except subprocess.TimeoutExpired:
|
| 63 |
-
print(f" ⚠️ {package}=={version} installation timed out")
|
| 64 |
-
all_good = False
|
| 65 |
-
except Exception as e:
|
| 66 |
-
print(f" ❌ {package}=={version} error: {e}")
|
| 67 |
-
all_good = False
|
| 68 |
-
|
| 69 |
-
return all_good
|
| 70 |
-
|
| 71 |
-
def test_imports():
|
| 72 |
-
"""Test if packages can be imported"""
|
| 73 |
-
print("\n🔍 Testing Package Imports")
|
| 74 |
-
print("=" * 40)
|
| 75 |
-
|
| 76 |
-
packages = [
|
| 77 |
-
'numpy',
|
| 78 |
-
'torch',
|
| 79 |
-
'omegaconf',
|
| 80 |
-
'fastapi',
|
| 81 |
-
'uvicorn',
|
| 82 |
-
'huggingface_hub',
|
| 83 |
-
'transformers'
|
| 84 |
-
]
|
| 85 |
-
|
| 86 |
-
all_good = True
|
| 87 |
-
|
| 88 |
-
for package in packages:
|
| 89 |
-
try:
|
| 90 |
-
module = importlib.import_module(package)
|
| 91 |
-
version = getattr(module, '__version__', 'Unknown')
|
| 92 |
-
print(f"✅ {package}: {version}")
|
| 93 |
-
except ImportError as e:
|
| 94 |
-
print(f"❌ {package}: Import failed - {e}")
|
| 95 |
-
all_good = False
|
| 96 |
-
except Exception as e:
|
| 97 |
-
print(f"⚠️ {package}: Error - {e}")
|
| 98 |
-
all_good = False
|
| 99 |
-
|
| 100 |
-
return all_good
|
| 101 |
-
|
| 102 |
-
def test_version_compatibility():
|
| 103 |
-
"""Test version compatibility"""
|
| 104 |
-
print("\n🔧 Testing Version Compatibility")
|
| 105 |
-
print("=" * 40)
|
| 106 |
-
|
| 107 |
-
try:
|
| 108 |
-
import numpy
|
| 109 |
-
import torch
|
| 110 |
-
|
| 111 |
-
numpy_version = numpy.__version__
|
| 112 |
-
torch_version = torch.__version__
|
| 113 |
-
|
| 114 |
-
print(f"NumPy: {numpy_version}")
|
| 115 |
-
print(f"PyTorch: {torch_version}")
|
| 116 |
-
|
| 117 |
-
# Check NumPy compatibility
|
| 118 |
-
if numpy_version.startswith('1.24'):
|
| 119 |
-
print("✅ NumPy 1.24.x - Compatible with PyTorch 1.13.x")
|
| 120 |
-
elif numpy_version.startswith('2.'):
|
| 121 |
-
print("❌ NumPy 2.x - NOT compatible with PyTorch 1.13.x")
|
| 122 |
-
return False
|
| 123 |
-
else:
|
| 124 |
-
print(f"⚠️ NumPy {numpy_version} - Check compatibility")
|
| 125 |
-
|
| 126 |
-
# Check PyTorch compatibility
|
| 127 |
-
if torch_version.startswith('1.13'):
|
| 128 |
-
print("✅ PyTorch 1.13.x - Compatible with NumPy 1.24.x")
|
| 129 |
-
else:
|
| 130 |
-
print(f"⚠️ PyTorch {torch_version} - Check compatibility")
|
| 131 |
-
|
| 132 |
-
return True
|
| 133 |
-
|
| 134 |
-
except ImportError as e:
|
| 135 |
-
print(f"❌ Cannot test compatibility: {e}")
|
| 136 |
-
return False
|
| 137 |
-
|
| 138 |
-
def test_fairseq_signals_import():
|
| 139 |
-
"""Test fairseq-signals import (if available)"""
|
| 140 |
-
print("\n🧬 Testing fairseq-signals Import")
|
| 141 |
-
print("=" * 40)
|
| 142 |
-
|
| 143 |
-
try:
|
| 144 |
-
# This will fail locally but shows the import path
|
| 145 |
-
import fairseq_signals
|
| 146 |
-
print("✅ fairseq_signals available locally")
|
| 147 |
-
return True
|
| 148 |
-
except ImportError:
|
| 149 |
-
print("⚠️ fairseq_signals not available locally (expected)")
|
| 150 |
-
print(" This will be installed from source in Docker")
|
| 151 |
-
return True
|
| 152 |
-
except Exception as e:
|
| 153 |
-
print(f"❌ fairseq_signals error: {e}")
|
| 154 |
-
return False
|
| 155 |
-
|
| 156 |
-
def main():
|
| 157 |
-
"""Main testing function"""
|
| 158 |
-
print("🚀 Lightweight Dependency Testing for ECG-FM")
|
| 159 |
-
print("=" * 60)
|
| 160 |
-
print("This tests dependencies without Docker for limited systems")
|
| 161 |
-
print()
|
| 162 |
-
|
| 163 |
-
tests = [
|
| 164 |
-
("Python Version", test_python_version),
|
| 165 |
-
("Package Installation", test_package_installation),
|
| 166 |
-
("Package Imports", test_imports),
|
| 167 |
-
("Version Compatibility", test_version_compatibility),
|
| 168 |
-
("fairseq-signals Import", test_fairseq_signals_import)
|
| 169 |
-
]
|
| 170 |
-
|
| 171 |
-
results = []
|
| 172 |
-
|
| 173 |
-
for test_name, test_func in tests:
|
| 174 |
-
print(f"\n{'='*60}")
|
| 175 |
-
print(f"🧪 {test_name}")
|
| 176 |
-
print(f"{'='*60}")
|
| 177 |
-
|
| 178 |
-
try:
|
| 179 |
-
result = test_func()
|
| 180 |
-
results.append((test_name, result))
|
| 181 |
-
except Exception as e:
|
| 182 |
-
print(f"❌ Test failed with error: {e}")
|
| 183 |
-
results.append((test_name, False))
|
| 184 |
-
|
| 185 |
-
# Summary
|
| 186 |
-
print(f"\n{'='*60}")
|
| 187 |
-
print("📊 TEST SUMMARY")
|
| 188 |
-
print(f"{'='*60}")
|
| 189 |
-
|
| 190 |
-
passed = 0
|
| 191 |
-
total = len(results)
|
| 192 |
-
|
| 193 |
-
for test_name, result in results:
|
| 194 |
-
status = "✅ PASS" if result else "❌ FAIL"
|
| 195 |
-
print(f"{test_name}: {status}")
|
| 196 |
-
if result:
|
| 197 |
-
passed += 1
|
| 198 |
-
|
| 199 |
-
print(f"\nResults: {passed}/{total} tests passed")
|
| 200 |
-
|
| 201 |
-
if passed == total:
|
| 202 |
-
print("\n🎉 ALL TESTS PASSED!")
|
| 203 |
-
print("✅ Dependencies look good for HF upload")
|
| 204 |
-
print("💡 Still recommend Docker test if possible")
|
| 205 |
-
else:
|
| 206 |
-
print(f"\n❌ {total-passed} tests failed!")
|
| 207 |
-
print("⚠️ Fix dependency issues before HF upload")
|
| 208 |
-
print("💡 Check the output above for specific problems")
|
| 209 |
-
|
| 210 |
-
return 0 if passed == total else 1
|
| 211 |
-
|
| 212 |
-
if __name__ == "__main__":
|
| 213 |
-
sys.exit(main())
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
test_env_simple.py
DELETED
|
@@ -1,102 +0,0 @@
|
|
| 1 |
-
#!/usr/bin/env python3
|
| 2 |
-
"""
|
| 3 |
-
Simple Environment Test Script
|
| 4 |
-
=============================
|
| 5 |
-
|
| 6 |
-
Tests the Python 3.13 compatible environment without Unicode issues.
|
| 7 |
-
"""
|
| 8 |
-
|
| 9 |
-
import sys
|
| 10 |
-
import os
|
| 11 |
-
|
| 12 |
-
def test_environment():
|
| 13 |
-
"""Test the environment"""
|
| 14 |
-
print("Testing Python 3.13 Compatible Environment")
|
| 15 |
-
print("=" * 50)
|
| 16 |
-
|
| 17 |
-
# Get the virtual environment Python
|
| 18 |
-
if os.name == 'nt': # Windows
|
| 19 |
-
python_path = os.path.join("ecg_fm_env_py313", "Scripts", "python.exe")
|
| 20 |
-
else: # Unix/Linux
|
| 21 |
-
python_path = os.path.join("ecg_fm_env_py313", "bin", "python")
|
| 22 |
-
|
| 23 |
-
if not os.path.exists(python_path):
|
| 24 |
-
print("Virtual environment not found")
|
| 25 |
-
return False
|
| 26 |
-
|
| 27 |
-
print(f"Using Python: {python_path}")
|
| 28 |
-
|
| 29 |
-
# Test imports
|
| 30 |
-
test_script = """
|
| 31 |
-
import sys
|
| 32 |
-
print(f"Python: {sys.version}")
|
| 33 |
-
|
| 34 |
-
try:
|
| 35 |
-
import numpy
|
| 36 |
-
print(f"NumPy: {numpy.__version__}")
|
| 37 |
-
except ImportError as e:
|
| 38 |
-
print(f"NumPy: FAILED - {e}")
|
| 39 |
-
|
| 40 |
-
try:
|
| 41 |
-
import torch
|
| 42 |
-
print(f"PyTorch: {torch.__version__}")
|
| 43 |
-
except ImportError as e:
|
| 44 |
-
print(f"PyTorch: FAILED - {e}")
|
| 45 |
-
|
| 46 |
-
try:
|
| 47 |
-
import omegaconf
|
| 48 |
-
print(f"OmegaConf: {omegaconf.__version__}")
|
| 49 |
-
# Test if is_primitive_type exists
|
| 50 |
-
if hasattr(omegaconf._utils, 'is_primitive_type'):
|
| 51 |
-
print("OmegaConf: is_primitive_type AVAILABLE")
|
| 52 |
-
else:
|
| 53 |
-
print("OmegaConf: is_primitive_type NOT AVAILABLE")
|
| 54 |
-
except ImportError as e:
|
| 55 |
-
print(f"OmegaConf: FAILED - {e}")
|
| 56 |
-
|
| 57 |
-
try:
|
| 58 |
-
import fastapi
|
| 59 |
-
print(f"FastAPI: {fastapi.__version__}")
|
| 60 |
-
except ImportError as e:
|
| 61 |
-
print(f"FastAPI: FAILED - {e}")
|
| 62 |
-
|
| 63 |
-
try:
|
| 64 |
-
import transformers
|
| 65 |
-
print(f"Transformers: {transformers.__version__}")
|
| 66 |
-
except ImportError as e:
|
| 67 |
-
print(f"Transformers: FAILED - {e}")
|
| 68 |
-
|
| 69 |
-
try:
|
| 70 |
-
import huggingface_hub
|
| 71 |
-
print(f"HuggingFace Hub: {huggingface_hub.__version__}")
|
| 72 |
-
except ImportError as e:
|
| 73 |
-
print(f"HuggingFace Hub: FAILED - {e}")
|
| 74 |
-
|
| 75 |
-
try:
|
| 76 |
-
import uvicorn
|
| 77 |
-
print(f"Uvicorn: {uvicorn.__version__}")
|
| 78 |
-
except ImportError as e:
|
| 79 |
-
print(f"Uvicorn: FAILED - {e}")
|
| 80 |
-
"""
|
| 81 |
-
|
| 82 |
-
try:
|
| 83 |
-
result = subprocess.run([python_path, '-c', test_script],
|
| 84 |
-
capture_output=True, text=True)
|
| 85 |
-
|
| 86 |
-
if result.returncode == 0:
|
| 87 |
-
print("Environment test successful!")
|
| 88 |
-
print("\nPackage Versions:")
|
| 89 |
-
print(result.stdout)
|
| 90 |
-
return True
|
| 91 |
-
else:
|
| 92 |
-
print("Environment test failed!")
|
| 93 |
-
print(result.stderr)
|
| 94 |
-
return False
|
| 95 |
-
|
| 96 |
-
except Exception as e:
|
| 97 |
-
print(f"Test error: {e}")
|
| 98 |
-
return False
|
| 99 |
-
|
| 100 |
-
if __name__ == "__main__":
|
| 101 |
-
import subprocess
|
| 102 |
-
test_environment()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
test_fairseq_signals.py
CHANGED
|
@@ -137,3 +137,4 @@ def main():
|
|
| 137 |
|
| 138 |
if __name__ == "__main__":
|
| 139 |
sys.exit(main())
|
|
|
|
|
|
| 137 |
|
| 138 |
if __name__ == "__main__":
|
| 139 |
sys.exit(main())
|
| 140 |
+
|
test_local_docker.py
DELETED
|
@@ -1,180 +0,0 @@
|
|
| 1 |
-
#!/usr/bin/env python3
|
| 2 |
-
"""
|
| 3 |
-
Local Docker Testing Script
|
| 4 |
-
==========================
|
| 5 |
-
|
| 6 |
-
Test the exact same Docker environment locally before uploading to HF Spaces.
|
| 7 |
-
This prevents wasting time on failed builds.
|
| 8 |
-
"""
|
| 9 |
-
|
| 10 |
-
import subprocess
|
| 11 |
-
import sys
|
| 12 |
-
import os
|
| 13 |
-
import time
|
| 14 |
-
|
| 15 |
-
def test_docker_build():
|
| 16 |
-
"""Test Docker build locally"""
|
| 17 |
-
print("🐳 Testing Docker Build Locally")
|
| 18 |
-
print("=" * 50)
|
| 19 |
-
|
| 20 |
-
# Check if Docker is available
|
| 21 |
-
try:
|
| 22 |
-
result = subprocess.run(['docker', '--version'], capture_output=True, text=True)
|
| 23 |
-
if result.returncode == 0:
|
| 24 |
-
print(f"✅ Docker available: {result.stdout.strip()}")
|
| 25 |
-
else:
|
| 26 |
-
print("❌ Docker not available")
|
| 27 |
-
return False
|
| 28 |
-
except FileNotFoundError:
|
| 29 |
-
print("❌ Docker not installed")
|
| 30 |
-
return False
|
| 31 |
-
|
| 32 |
-
# Build the Docker image
|
| 33 |
-
print("\n🔧 Building Docker image...")
|
| 34 |
-
try:
|
| 35 |
-
build_cmd = [
|
| 36 |
-
'docker', 'build',
|
| 37 |
-
'-t', 'ecg-fm-test',
|
| 38 |
-
'--no-cache', # Force fresh build
|
| 39 |
-
'.'
|
| 40 |
-
]
|
| 41 |
-
|
| 42 |
-
print(f"Command: {' '.join(build_cmd)}")
|
| 43 |
-
result = subprocess.run(build_cmd, capture_output=True, text=True)
|
| 44 |
-
|
| 45 |
-
if result.returncode == 0:
|
| 46 |
-
print("✅ Docker build successful!")
|
| 47 |
-
return True
|
| 48 |
-
else:
|
| 49 |
-
print("❌ Docker build failed!")
|
| 50 |
-
print("\n📋 Build Output:")
|
| 51 |
-
print(result.stdout)
|
| 52 |
-
print("\n❌ Error Output:")
|
| 53 |
-
print(result.stderr)
|
| 54 |
-
return False
|
| 55 |
-
|
| 56 |
-
except Exception as e:
|
| 57 |
-
print(f"❌ Build error: {e}")
|
| 58 |
-
return False
|
| 59 |
-
|
| 60 |
-
def test_docker_run():
|
| 61 |
-
"""Test running the container locally"""
|
| 62 |
-
print("\n🚀 Testing Docker Container Run")
|
| 63 |
-
print("=" * 50)
|
| 64 |
-
|
| 65 |
-
try:
|
| 66 |
-
# Run container in background
|
| 67 |
-
run_cmd = [
|
| 68 |
-
'docker', 'run',
|
| 69 |
-
'-d', # Detached mode
|
| 70 |
-
'-p', '7860:7860', # Port mapping
|
| 71 |
-
'--name', 'ecg-fm-test-container',
|
| 72 |
-
'ecg-fm-test'
|
| 73 |
-
]
|
| 74 |
-
|
| 75 |
-
print(f"Command: {' '.join(run_cmd)}")
|
| 76 |
-
result = subprocess.run(run_cmd, capture_output=True, text=True)
|
| 77 |
-
|
| 78 |
-
if result.returncode == 0:
|
| 79 |
-
print("✅ Container started successfully!")
|
| 80 |
-
|
| 81 |
-
# Wait for startup
|
| 82 |
-
print("⏳ Waiting for container startup...")
|
| 83 |
-
time.sleep(10)
|
| 84 |
-
|
| 85 |
-
# Test API endpoints
|
| 86 |
-
test_api_endpoints()
|
| 87 |
-
|
| 88 |
-
# Cleanup
|
| 89 |
-
cleanup_container()
|
| 90 |
-
return True
|
| 91 |
-
else:
|
| 92 |
-
print("❌ Container start failed!")
|
| 93 |
-
print(result.stderr)
|
| 94 |
-
return False
|
| 95 |
-
|
| 96 |
-
except Exception as e:
|
| 97 |
-
print(f"❌ Run error: {e}")
|
| 98 |
-
return False
|
| 99 |
-
|
| 100 |
-
def test_api_endpoints():
|
| 101 |
-
"""Test API endpoints"""
|
| 102 |
-
print("\n🌐 Testing API Endpoints")
|
| 103 |
-
print("=" * 50)
|
| 104 |
-
|
| 105 |
-
import requests
|
| 106 |
-
|
| 107 |
-
base_url = "http://localhost:7860"
|
| 108 |
-
|
| 109 |
-
# Test health endpoint
|
| 110 |
-
try:
|
| 111 |
-
response = requests.get(f"{base_url}/healthz", timeout=10)
|
| 112 |
-
if response.status_code == 200:
|
| 113 |
-
print("✅ Health endpoint working")
|
| 114 |
-
print(f"Response: {response.json()}")
|
| 115 |
-
else:
|
| 116 |
-
print(f"❌ Health endpoint failed: {response.status_code}")
|
| 117 |
-
except Exception as e:
|
| 118 |
-
print(f"❌ Health endpoint error: {e}")
|
| 119 |
-
|
| 120 |
-
# Test root endpoint
|
| 121 |
-
try:
|
| 122 |
-
response = requests.get(f"{base_url}/", timeout=10)
|
| 123 |
-
if response.status_code == 200:
|
| 124 |
-
print("✅ Root endpoint working")
|
| 125 |
-
print(f"Response: {response.json()}")
|
| 126 |
-
else:
|
| 127 |
-
print(f"❌ Root endpoint failed: {response.status_code}")
|
| 128 |
-
except Exception as e:
|
| 129 |
-
print(f"❌ Root endpoint error: {e}")
|
| 130 |
-
|
| 131 |
-
def cleanup_container():
|
| 132 |
-
"""Clean up test container"""
|
| 133 |
-
print("\n🧹 Cleaning up test container...")
|
| 134 |
-
|
| 135 |
-
try:
|
| 136 |
-
# Stop container
|
| 137 |
-
subprocess.run(['docker', 'stop', 'ecg-fm-test-container'],
|
| 138 |
-
capture_output=True, text=True)
|
| 139 |
-
|
| 140 |
-
# Remove container
|
| 141 |
-
subprocess.run(['docker', 'rm', 'ecg-fm-test-container'],
|
| 142 |
-
capture_output=True, text=True)
|
| 143 |
-
|
| 144 |
-
# Remove image
|
| 145 |
-
subprocess.run(['docker', 'rmi', 'ecg-fm-test'],
|
| 146 |
-
capture_output=True, text=True)
|
| 147 |
-
|
| 148 |
-
print("✅ Cleanup completed")
|
| 149 |
-
except Exception as e:
|
| 150 |
-
print(f"⚠️ Cleanup warning: {e}")
|
| 151 |
-
|
| 152 |
-
def main():
|
| 153 |
-
"""Main testing function"""
|
| 154 |
-
print("🚀 Local Docker Testing for ECG-FM")
|
| 155 |
-
print("=" * 60)
|
| 156 |
-
print("This will test the exact same environment locally before HF upload")
|
| 157 |
-
print()
|
| 158 |
-
|
| 159 |
-
# Test 1: Docker build
|
| 160 |
-
build_success = test_docker_build()
|
| 161 |
-
|
| 162 |
-
if not build_success:
|
| 163 |
-
print("\n❌ Docker build failed! Fix issues before uploading to HF.")
|
| 164 |
-
return 1
|
| 165 |
-
|
| 166 |
-
# Test 2: Docker run
|
| 167 |
-
run_success = test_docker_run()
|
| 168 |
-
|
| 169 |
-
if not run_success:
|
| 170 |
-
print("\n❌ Docker run failed! Fix issues before uploading to HF.")
|
| 171 |
-
return 1
|
| 172 |
-
|
| 173 |
-
print("\n🎉 ALL TESTS PASSED!")
|
| 174 |
-
print("✅ Ready to upload to HF Spaces!")
|
| 175 |
-
print("🚀 The build should succeed on HF.")
|
| 176 |
-
|
| 177 |
-
return 0
|
| 178 |
-
|
| 179 |
-
if __name__ == "__main__":
|
| 180 |
-
sys.exit(main())
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
test_omegaconf_fix.py
DELETED
|
@@ -1,126 +0,0 @@
|
|
| 1 |
-
#!/usr/bin/env python3
|
| 2 |
-
"""
|
| 3 |
-
Test OmegaConf Fix Locally
|
| 4 |
-
==========================
|
| 5 |
-
|
| 6 |
-
Tests if the OmegaConf fix works before deploying to HF Spaces.
|
| 7 |
-
"""
|
| 8 |
-
|
| 9 |
-
import subprocess
|
| 10 |
-
import sys
|
| 11 |
-
import os
|
| 12 |
-
|
| 13 |
-
def test_omegaconf_override():
|
| 14 |
-
"""Test if we can override OmegaConf version"""
|
| 15 |
-
print("🧪 Testing OmegaConf Version Override")
|
| 16 |
-
print("=" * 50)
|
| 17 |
-
|
| 18 |
-
# Get the virtual environment Python
|
| 19 |
-
if os.name == 'nt': # Windows
|
| 20 |
-
python_path = os.path.join("ecg_fm_env_py313", "Scripts", "python.exe")
|
| 21 |
-
else: # Unix/Linux
|
| 22 |
-
python_path = os.path.join("ecg_fm_env_py313", "bin", "python")
|
| 23 |
-
|
| 24 |
-
if not os.path.exists(python_path):
|
| 25 |
-
print("❌ Virtual environment not found")
|
| 26 |
-
return False
|
| 27 |
-
|
| 28 |
-
print(f"Using Python: {python_path}")
|
| 29 |
-
|
| 30 |
-
# Test 1: Check current OmegaConf version
|
| 31 |
-
print("\n📋 Current OmegaConf version:")
|
| 32 |
-
try:
|
| 33 |
-
result = subprocess.run([python_path, '-c',
|
| 34 |
-
"import omegaconf; print(f'Version: {omegaconf.__version__}'); print(f'is_primitive_type: {hasattr(omegaconf._utils, \"is_primitive_type\")}')"],
|
| 35 |
-
capture_output=True, text=True)
|
| 36 |
-
if result.returncode == 0:
|
| 37 |
-
print(result.stdout)
|
| 38 |
-
else:
|
| 39 |
-
print(f"❌ Error: {result.stderr}")
|
| 40 |
-
return False
|
| 41 |
-
except Exception as e:
|
| 42 |
-
print(f"❌ Test error: {e}")
|
| 43 |
-
return False
|
| 44 |
-
|
| 45 |
-
# Test 2: Simulate fairseq-signals installation (install a package that might override OmegaConf)
|
| 46 |
-
print("\n🔧 Simulating fairseq-signals installation...")
|
| 47 |
-
try:
|
| 48 |
-
# Install a package that might have conflicting dependencies
|
| 49 |
-
result = subprocess.run([python_path, '-m', 'pip', 'install', '--upgrade', 'omegaconf'],
|
| 50 |
-
capture_output=True, text=True)
|
| 51 |
-
if result.returncode == 0:
|
| 52 |
-
print("✅ Upgraded OmegaConf")
|
| 53 |
-
else:
|
| 54 |
-
print(f"⚠️ Upgrade warning: {result.stderr}")
|
| 55 |
-
except Exception as e:
|
| 56 |
-
print(f"⚠️ Upgrade warning: {e}")
|
| 57 |
-
|
| 58 |
-
# Test 3: Check OmegaConf version after upgrade
|
| 59 |
-
print("\n📋 OmegaConf version after upgrade:")
|
| 60 |
-
try:
|
| 61 |
-
result = subprocess.run([python_path, '-c',
|
| 62 |
-
"import omegaconf; print(f'Version: {omegaconf.__version__}'); print(f'is_primitive_type: {hasattr(omegaconf._utils, \"is_primitive_type\")}')"],
|
| 63 |
-
capture_output=True, text=True)
|
| 64 |
-
if result.returncode == 0:
|
| 65 |
-
print(result.stdout)
|
| 66 |
-
else:
|
| 67 |
-
print(f"❌ Error: {result.stderr}")
|
| 68 |
-
return False
|
| 69 |
-
except Exception as e:
|
| 70 |
-
print(f"❌ Test error: {e}")
|
| 71 |
-
return False
|
| 72 |
-
|
| 73 |
-
# Test 4: Force reinstall correct version
|
| 74 |
-
print("\n🔧 Forcing correct OmegaConf version...")
|
| 75 |
-
try:
|
| 76 |
-
result = subprocess.run([python_path, '-m', 'pip', 'install', '--force-reinstall', '--no-deps', 'omegaconf==2.0.0'],
|
| 77 |
-
capture_output=True, text=True)
|
| 78 |
-
if result.returncode == 0:
|
| 79 |
-
print("✅ Forced reinstall of OmegaConf 2.0.0")
|
| 80 |
-
else:
|
| 81 |
-
print(f"❌ Reinstall failed: {result.stderr}")
|
| 82 |
-
return False
|
| 83 |
-
except Exception as e:
|
| 84 |
-
print(f"❌ Reinstall error: {e}")
|
| 85 |
-
return False
|
| 86 |
-
|
| 87 |
-
# Test 5: Verify final version
|
| 88 |
-
print("\n📋 Final OmegaConf version:")
|
| 89 |
-
try:
|
| 90 |
-
result = subprocess.run([python_path, '-c',
|
| 91 |
-
"import omegaconf; print(f'Version: {omegaconf.__version__}'); print(f'is_primitive_type: {hasattr(omegaconf._utils, \"is_primitive_type\")}')"],
|
| 92 |
-
capture_output=True, text=True)
|
| 93 |
-
if result.returncode == 0:
|
| 94 |
-
print(result.stdout)
|
| 95 |
-
if "2.0.0" in result.stdout and "True" in result.stdout:
|
| 96 |
-
print("✅ SUCCESS: OmegaConf 2.0.0 with is_primitive_type!")
|
| 97 |
-
return True
|
| 98 |
-
else:
|
| 99 |
-
print("❌ FAILED: Wrong version or missing is_primitive_type")
|
| 100 |
-
return False
|
| 101 |
-
else:
|
| 102 |
-
print(f"❌ Error: {result.stderr}")
|
| 103 |
-
return False
|
| 104 |
-
except Exception as e:
|
| 105 |
-
print(f"❌ Test error: {e}")
|
| 106 |
-
return False
|
| 107 |
-
|
| 108 |
-
def main():
|
| 109 |
-
"""Main function"""
|
| 110 |
-
print("🚀 Testing OmegaConf Fix Locally")
|
| 111 |
-
print("=" * 60)
|
| 112 |
-
print("This tests if our Dockerfile fix will work")
|
| 113 |
-
print()
|
| 114 |
-
|
| 115 |
-
if test_omegaconf_override():
|
| 116 |
-
print("\n🎉 LOCAL TEST PASSED!")
|
| 117 |
-
print("✅ OmegaConf fix should work on HF Spaces")
|
| 118 |
-
print("🚀 Ready to deploy with fixed Dockerfile")
|
| 119 |
-
return 0
|
| 120 |
-
else:
|
| 121 |
-
print("\n❌ LOCAL TEST FAILED!")
|
| 122 |
-
print("⚠️ Need to fix the approach")
|
| 123 |
-
return 1
|
| 124 |
-
|
| 125 |
-
if __name__ == "__main__":
|
| 126 |
-
sys.exit(main())
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
verify_versions.py
DELETED
|
@@ -1,154 +0,0 @@
|
|
| 1 |
-
#!/usr/bin/env python3
|
| 2 |
-
"""
|
| 3 |
-
Version Compatibility Verification
|
| 4 |
-
================================
|
| 5 |
-
|
| 6 |
-
This script verifies that all package versions are compatible
|
| 7 |
-
before deploying to HF Spaces
|
| 8 |
-
"""
|
| 9 |
-
|
| 10 |
-
import sys
|
| 11 |
-
import subprocess
|
| 12 |
-
import pkg_resources
|
| 13 |
-
|
| 14 |
-
def get_installed_version(package_name):
|
| 15 |
-
"""Get installed version of a package"""
|
| 16 |
-
try:
|
| 17 |
-
return pkg_resources.get_distribution(package_name).version
|
| 18 |
-
except pkg_resources.DistributionNotFound:
|
| 19 |
-
return None
|
| 20 |
-
|
| 21 |
-
def check_version_compatibility():
|
| 22 |
-
"""Check if installed versions are compatible"""
|
| 23 |
-
print("🔍 Version Compatibility Check for HF Spaces")
|
| 24 |
-
print("=" * 60)
|
| 25 |
-
|
| 26 |
-
# Required versions for HF Spaces
|
| 27 |
-
required_versions = {
|
| 28 |
-
'torch': '1.13.1',
|
| 29 |
-
'torchvision': '0.14.1',
|
| 30 |
-
'torchaudio': '0.13.1',
|
| 31 |
-
'omegaconf': '1.4.1',
|
| 32 |
-
'numpy': '1.24.3',
|
| 33 |
-
'fastapi': '0.104.1',
|
| 34 |
-
'uvicorn': '0.24.0',
|
| 35 |
-
'transformers': '4.21.0',
|
| 36 |
-
'huggingface-hub': '0.19.4',
|
| 37 |
-
'pyyaml': '6.0.1',
|
| 38 |
-
'einops': '0.7.0'
|
| 39 |
-
}
|
| 40 |
-
|
| 41 |
-
print("📋 Required versions for HF Spaces compatibility:")
|
| 42 |
-
for package, version in required_versions.items():
|
| 43 |
-
print(f" {package}: {version}")
|
| 44 |
-
|
| 45 |
-
print("\n🔍 Checking installed versions...")
|
| 46 |
-
|
| 47 |
-
all_compatible = True
|
| 48 |
-
compatibility_issues = []
|
| 49 |
-
|
| 50 |
-
for package, required_version in required_versions.items():
|
| 51 |
-
installed_version = get_installed_version(package)
|
| 52 |
-
|
| 53 |
-
if installed_version is None:
|
| 54 |
-
print(f"❌ {package}: Not installed")
|
| 55 |
-
all_compatible = False
|
| 56 |
-
compatibility_issues.append(f"{package}: Not installed")
|
| 57 |
-
else:
|
| 58 |
-
print(f"✅ {package}: {installed_version}")
|
| 59 |
-
|
| 60 |
-
# Check if versions match (allowing for minor variations)
|
| 61 |
-
if package.startswith('torch'):
|
| 62 |
-
# PyTorch packages should match exactly
|
| 63 |
-
if installed_version != required_version:
|
| 64 |
-
print(f" ⚠️ Version mismatch: {installed_version} != {required_version}")
|
| 65 |
-
all_compatible = False
|
| 66 |
-
compatibility_issues.append(f"{package}: {installed_version} != {required_version}")
|
| 67 |
-
else:
|
| 68 |
-
# Other packages can have compatible versions
|
| 69 |
-
print(f" ✅ Version compatible")
|
| 70 |
-
|
| 71 |
-
print("\n📊 Compatibility Summary:")
|
| 72 |
-
if all_compatible:
|
| 73 |
-
print("🎉 ALL VERSIONS ARE COMPATIBLE!")
|
| 74 |
-
print("✅ Ready for HF Spaces deployment")
|
| 75 |
-
return True
|
| 76 |
-
else:
|
| 77 |
-
print("❌ VERSION COMPATIBILITY ISSUES FOUND:")
|
| 78 |
-
for issue in compatibility_issues:
|
| 79 |
-
print(f" - {issue}")
|
| 80 |
-
print("\n⚠️ Please fix version conflicts before deployment")
|
| 81 |
-
return False
|
| 82 |
-
|
| 83 |
-
def check_python_version():
|
| 84 |
-
"""Check Python version compatibility"""
|
| 85 |
-
print("\n🐍 Python Version Check:")
|
| 86 |
-
python_version = sys.version_info
|
| 87 |
-
|
| 88 |
-
print(f" Current: {python_version.major}.{python_version.minor}.{python_version.micro}")
|
| 89 |
-
|
| 90 |
-
# HF Spaces supports Python 3.8-3.11
|
| 91 |
-
if python_version.major == 3 and 8 <= python_version.minor <= 11:
|
| 92 |
-
print(" ✅ Python version compatible with HF Spaces")
|
| 93 |
-
return True
|
| 94 |
-
else:
|
| 95 |
-
print(" ❌ Python version not compatible with HF Spaces")
|
| 96 |
-
print(" ⚠️ HF Spaces supports Python 3.8-3.11")
|
| 97 |
-
return False
|
| 98 |
-
|
| 99 |
-
def check_system_requirements():
|
| 100 |
-
"""Check system requirements"""
|
| 101 |
-
print("\n💻 System Requirements Check:")
|
| 102 |
-
|
| 103 |
-
# Check if we're on a compatible system
|
| 104 |
-
import platform
|
| 105 |
-
system = platform.system()
|
| 106 |
-
print(f" OS: {system}")
|
| 107 |
-
|
| 108 |
-
if system in ['Linux', 'Darwin']:
|
| 109 |
-
print(" ✅ OS compatible with Docker deployment")
|
| 110 |
-
return True
|
| 111 |
-
elif system == 'Windows':
|
| 112 |
-
print(" ⚠️ Windows detected - Docker deployment may have issues")
|
| 113 |
-
print(" 💡 Consider using WSL2 or Linux environment")
|
| 114 |
-
return False
|
| 115 |
-
else:
|
| 116 |
-
print(" ❌ Unknown OS - compatibility uncertain")
|
| 117 |
-
return False
|
| 118 |
-
|
| 119 |
-
def main():
|
| 120 |
-
"""Main verification function"""
|
| 121 |
-
print("🚀 HF Spaces Version Compatibility Verification")
|
| 122 |
-
print("=" * 80)
|
| 123 |
-
|
| 124 |
-
# Check Python version
|
| 125 |
-
python_ok = check_python_version()
|
| 126 |
-
|
| 127 |
-
# Check system requirements
|
| 128 |
-
system_ok = check_system_requirements()
|
| 129 |
-
|
| 130 |
-
# Check package versions
|
| 131 |
-
packages_ok = check_version_compatibility()
|
| 132 |
-
|
| 133 |
-
# Final summary
|
| 134 |
-
print("\n" + "=" * 80)
|
| 135 |
-
print("📊 FINAL VERIFICATION SUMMARY:")
|
| 136 |
-
print("=" * 80)
|
| 137 |
-
|
| 138 |
-
print(f"Python Version: {'✅ PASS' if python_ok else '❌ FAIL'}")
|
| 139 |
-
print(f"System Requirements: {'✅ PASS' if system_ok else '❌ FAIL'}")
|
| 140 |
-
print(f"Package Versions: {'✅ PASS' if packages_ok else '❌ FAIL'}")
|
| 141 |
-
|
| 142 |
-
if python_ok and system_ok and packages_ok:
|
| 143 |
-
print("\n🎉 ALL CHECKS PASSED!")
|
| 144 |
-
print("✅ Ready for HF Spaces deployment with fairseq-signals")
|
| 145 |
-
print("🚀 Expected: 3x accuracy improvement (25% → 80%+)")
|
| 146 |
-
return 0
|
| 147 |
-
else:
|
| 148 |
-
print("\n❌ SOME CHECKS FAILED!")
|
| 149 |
-
print("⚠️ Please resolve issues before deployment")
|
| 150 |
-
print("💡 Check the output above for specific problems")
|
| 151 |
-
return 1
|
| 152 |
-
|
| 153 |
-
if __name__ == "__main__":
|
| 154 |
-
sys.exit(main())
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|