Kalpana-Plain-Models-API / model_service.py
MaduRox's picture
Add rich Swagger API documentation
b7a913f
Raw
History Blame Contribute Delete
4.63 kB
import os
import gc
import time
import torch
import config
from huggingface_hub import hf_hub_download
from transformers import AutoModelForCausalLM, AutoTokenizer, BitsAndBytesConfig
from model_registry import PLAIN_MODELS, CPU_GGUF_MODELS, validate_model_id
# Global state for loaded model & tokenizer
active_model = None
active_tokenizer = None
active_model_id = None
model_load_time_ms = 0.0
loaded_model_hash = ""
loaded_model_revision = "main"
def get_context_limit(model_id: str) -> int:
if model_id == "plain-llama-3-8b":
return config.MODEL_CONTEXT_LIMIT_LLAMA_3_8B
elif model_id == "plain-llama-3.2-3b":
return config.MODEL_CONTEXT_LIMIT_LLAMA_3_2_3B
elif model_id == "plain-qwen-0.5b":
return config.MODEL_CONTEXT_LIMIT_QWEN_0_5B
return 2048
def unload_all_models():
global active_model, active_tokenizer, active_model_id, loaded_model_hash, loaded_model_revision
if active_model is not None:
# If it is a GGUF model, we may want to call its destructor if needed (llama_cpp cleans up automatically on delete)
del active_model
active_model = None
if active_tokenizer is not None:
del active_tokenizer
active_tokenizer = None
active_model_id = None
loaded_model_hash = ""
loaded_model_revision = "main"
# Run garbage collection
gc.collect()
# Clear PyTorch CUDA Cache if GPU is active
if torch.cuda.is_available():
torch.cuda.empty_cache()
def load_tokenizer_only(model_id: str):
validate_model_id(model_id)
repo_id = PLAIN_MODELS[model_id]
# Always load Hugging Face tokenizer for chat template rendering and token counting
tokenizer = AutoTokenizer.from_pretrained(
repo_id,
token=config.HF_TOKEN if config.HF_TOKEN else None,
trust_remote_code=True
)
if tokenizer.pad_token is None:
tokenizer.pad_token = tokenizer.eos_token
return tokenizer
def load_model_and_tokenizer(model_id: str):
global active_model, active_tokenizer, active_model_id, model_load_time_ms, loaded_model_hash, loaded_model_revision
validate_model_id(model_id)
if active_model_id == model_id:
return active_model, active_tokenizer
unload_all_models()
start_time = time.time()
# Load Hugging Face tokenizer first (used for both GPU & CPU profiles)
tokenizer = load_tokenizer_only(model_id)
ctx_limit = get_context_limit(model_id)
if config.INFERENCE_PROFILE == "gpu-transformers":
# Load Hugging Face Model with bitsandbytes 4-bit quantization
repo_id = PLAIN_MODELS[model_id]
bnb_config = BitsAndBytesConfig(
load_in_4bit=True,
bnb_4bit_quant_type="nf4",
bnb_4bit_use_double_quant=True,
bnb_4bit_compute_dtype=torch.float16
)
model = AutoModelForCausalLM.from_pretrained(
repo_id,
quantization_config=bnb_config,
device_map="auto",
token=config.HF_TOKEN if config.HF_TOKEN else None,
trust_remote_code=True
)
loaded_model_revision = "main"
loaded_model_hash = "gpu-transformers-hash-4bit"
elif config.INFERENCE_PROFILE == "cpu-gguf":
# Load GGUF model via llama-cpp-python
import llama_cpp
gguf_cfg = CPU_GGUF_MODELS[model_id]
repo_id = gguf_cfg["repo_id"]
filename = gguf_cfg["filename"]
# Download file using huggingface_hub
model_path = hf_hub_download(
repo_id=repo_id,
filename=filename,
token=config.HF_TOKEN if config.HF_TOKEN else None
)
# Initialize llama.cpp model
model = llama_cpp.Llama(
model_path=model_path,
n_ctx=ctx_limit,
n_threads=config.MODEL_THREADS,
n_batch=config.MODEL_BATCH_SIZE,
verbose=False
)
loaded_model_revision = "main"
# Extract file size/hash metadata
import hashlib
h = hashlib.sha256()
with open(model_path, "rb") as f:
# Read first 1MB for hashing to avoid blocking
h.update(f.read(1024 * 1024))
loaded_model_hash = h.hexdigest()
else:
raise ValueError(f"Unknown INFERENCE_PROFILE: {config.INFERENCE_PROFILE}")
active_model = model
active_tokenizer = tokenizer
active_model_id = model_id
model_load_time_ms = (time.time() - start_time) * 1000.0
return active_model, active_tokenizer