myanmar-ghost / pipelines /deployment_pipeline.py
amkyawdev's picture
Add source code
cfb5e7f verified
Raw
History Blame Contribute Delete
6.51 kB
"""Model deployment pipeline for Myanmar Ghost."""
import argparse
import logging
import shutil
import sys
from pathlib import Path
from datetime import datetime
sys.path.insert(0, str(Path(__file__).parent.parent.parent))
from src.utils.logger import setup_logger
logger = setup_logger("deployment_pipeline")
def export_model(model_path: str, output_dir: str, format: str = "pytorch") -> str:
"""Export model for deployment."""
logger.info(f"Exporting model from {model_path}")
import torch
# Load model
checkpoint = torch.load(model_path, map_location="cpu")
# Create output directory
export_path = Path(output_dir) / "exported_model"
export_path.mkdir(parents=True, exist_ok=True)
if format == "pytorch":
# Save as PyTorch model
torch.save(checkpoint, export_path / "model.pt")
logger.info(f"Exported to {export_path}")
elif format == "onnx":
# Export to ONNX (requires model forward pass)
logger.warning("ONNX export not implemented yet")
elif format == "safetensors":
# Save as safetensors
try:
from safetensors.torch import save_file
if "model_state_dict" in checkpoint:
state_dict = checkpoint["model_state_dict"]
else:
state_dict = checkpoint
save_file(state_dict, export_path / "model.safetensors")
logger.info(f"Exported safetensors to {export_path}")
except ImportError:
logger.warning("safetensors not installed, using PyTorch format")
torch.save(checkpoint, export_path / "model.pt")
# Save metadata
metadata = {
"exported_at": datetime.now().isoformat(),
"format": format,
"original_path": model_path,
}
import json
with open(export_path / "metadata.json", "w") as f:
json.dump(metadata, f, indent=2)
return str(export_path)
def create_docker_image(model_path: str, output_dir: str, tag: str = "myanmar-ghost") -> str:
"""Create Docker image for deployment."""
logger.info(f"Creating Docker image: {tag}")
import subprocess
# Create deployment directory
deploy_dir = Path(output_dir) / "docker"
deploy_dir.mkdir(parents=True, exist_ok=True)
# Copy model
shutil.copytree(model_path, deploy_dir / "model", dirs_exist_ok=True)
# Create Dockerfile
dockerfile = f"""
FROM python:3.10-slim
WORKDIR /app
COPY model/ /app/model/
COPY deployment/api/ /app/api/
RUN pip install --no-cache-dir \\
torch \\
transformers \\
fastapi \\
uvicorn
EXPOSE 8000
CMD ["uvicorn", "api.app:app", "--host", "0.0.0.0", "--port", "8000"]
"""
with open(deploy_dir / "Dockerfile", "w") as f:
f.write(dockerfile)
# Build image
result = subprocess.run(
["docker", "build", "-t", tag, "."],
cwd=deploy_dir,
capture_output=True,
text=True,
)
if result.returncode != 0:
logger.error(f"Docker build failed: {result.stderr}")
raise RuntimeError("Docker build failed")
logger.info(f"Docker image created: {tag}")
return tag
def push_to_huggingface(model_path: str, repo_id: str) -> str:
"""Push model to HuggingFace Hub."""
logger.info(f"Pushing model to HuggingFace: {repo_id}")
import subprocess
try:
# Use huggingface_hub
from huggingface_hub import HfApi, create_repo
api = HfApi()
# Create repo if doesn't exist
try:
create_repo(repo_id, repo_type="model", exist_ok=True)
except Exception:
pass
# Upload folder
api.upload_folder(
folder_path=model_path,
repo_id=repo_id,
repo_type="model",
)
logger.info(f"Pushed to https://huggingface.co/{repo_id}")
return f"https://huggingface.co/{repo_id}"
except ImportError:
logger.warning("huggingface_hub not installed, using CLI")
result = subprocess.run([
"huggingface-cli", "upload",
repo_id,
model_path,
], capture_output=True, text=True)
if result.returncode != 0:
logger.error(f"HuggingFace upload failed: {result.stderr}")
raise RuntimeError("HuggingFace upload failed")
return f"https://huggingface.co/{repo_id}"
def deploy_to_render(api_token: str, service_name: str, model_path: str) -> str:
"""Deploy to Render using blueprint."""
logger.info(f"Deploying to Render: {service_name}")
import subprocess
# Create render.yaml
render_yaml = f"""
services:
- type: web
name: {service_name}
env: docker
repo: {model_path}
envVars:
- key: MODEL_PATH
value: /app/model
"""
with open("render.yaml", "w") as f:
f.write(render_yaml)
logger.info("Created render.yaml - deploy manually via Render dashboard")
return "render.yaml created"
def run_deployment_pipeline(
model_path: str,
output_dir: str = "outputs/deployment",
format: str = "safetensors",
push_hub: bool = False,
repo_id: str = None,
) -> dict:
"""Run full deployment pipeline."""
logger.info("Starting deployment pipeline...")
Path(output_dir).mkdir(parents=True, exist_ok=True)
# Export model
export_path = export_model(model_path, output_dir, format)
results = {
"export_path": export_path,
}
# Push to HuggingFace if requested
if push_hub and repo_id:
hub_url = push_to_huggingface(export_path, repo_id)
results["hub_url"] = hub_url
logger.info("Deployment pipeline complete!")
return results
if __name__ == "__main__":
parser = argparse.ArgumentParser(description="Model deployment pipeline")
parser.add_argument("--model_path", type=str, required=True)
parser.add_argument("--output_dir", type=str, default="outputs/deployment")
parser.add_argument("--format", type=str, default="safetensors")
parser.add_argument("--push_hub", action="store_true")
parser.add_argument("--repo_id", type=str, default=None)
args = parser.parse_args()
run_deployment_pipeline(
args.model_path,
args.output_dir,
args.format,
args.push_hub,
args.repo_id,
)