github-actions[bot]
Sync inference Space from GitHub
42d397a
Raw
History Blame Contribute Delete
4.15 kB
import os
import torch
from fastapi import FastAPI, Header, HTTPException
from pydantic import BaseModel
from peft import PeftConfig, PeftModel
from transformers import AutoModelForCausalLM, AutoTokenizer, BitsAndBytesConfig
ADAPTER_MODEL_ID = os.environ.get(
"ADAPTER_MODEL_ID",
"maimd/Maimd-HPI-SFT-Behavioral-MedGemma-4B-v001-20260608",
).strip()
MODEL_ID = os.environ.get("MODEL_ID", "").strip()
HF_TOKEN = os.environ.get("HF_TOKEN")
REMOTE_API_KEY = os.environ.get("REMOTE_API_KEY", "")
app = FastAPI(title="Agentic Dr Inference")
model = None
tokenizer = None
resolved_model_id = None
resolved_base_model_id = None
class GenerateRequest(BaseModel):
prompt: str
max_new_tokens: int = 300
def load_model():
global model, tokenizer, resolved_model_id, resolved_base_model_id
if model is not None and tokenizer is not None:
return
device = "cuda" if torch.cuda.is_available() else "cpu"
quantization_config = None
if device == "cuda":
quantization_config = BitsAndBytesConfig(
load_in_4bit=True,
bnb_4bit_compute_dtype=torch.bfloat16,
bnb_4bit_use_double_quant=True,
bnb_4bit_quant_type="nf4",
)
target_model_id = ADAPTER_MODEL_ID or MODEL_ID
if not target_model_id:
raise RuntimeError("Either ADAPTER_MODEL_ID or MODEL_ID must be configured.")
resolved_model_id = target_model_id
adapter_model_id = None
try:
peft_config = PeftConfig.from_pretrained(
target_model_id,
token=HF_TOKEN,
)
adapter_model_id = target_model_id
resolved_base_model_id = peft_config.base_model_name_or_path
except Exception:
resolved_base_model_id = None
tokenizer_source = resolved_base_model_id or target_model_id
tokenizer = AutoTokenizer.from_pretrained(
tokenizer_source,
token=HF_TOKEN,
)
if tokenizer.pad_token is None:
tokenizer.pad_token = tokenizer.eos_token
base_model = AutoModelForCausalLM.from_pretrained(
resolved_base_model_id or target_model_id,
token=HF_TOKEN,
torch_dtype=torch.bfloat16 if device == "cuda" else torch.float32,
device_map="auto" if device == "cuda" else None,
low_cpu_mem_usage=True,
quantization_config=quantization_config,
)
if adapter_model_id:
model = PeftModel.from_pretrained(
base_model,
adapter_model_id,
token=HF_TOKEN,
).merge_and_unload()
else:
model = base_model
model.eval()
@app.get("/health")
def health():
return {
"status": "ok",
"model_id": resolved_model_id or MODEL_ID or ADAPTER_MODEL_ID,
"base_model_id": resolved_base_model_id,
}
@app.post("/generate")
def generate(request: GenerateRequest, authorization: str | None = Header(default=None)):
if REMOTE_API_KEY:
expected = f"Bearer {REMOTE_API_KEY}"
if authorization != expected:
raise HTTPException(status_code=401, detail="unauthorized")
if not request.prompt.strip():
raise HTTPException(status_code=400, detail="prompt is required")
load_model()
messages = [
{
"role": "user",
"content": request.prompt,
}
]
prompt_text = tokenizer.apply_chat_template(
messages,
add_generation_prompt=True,
tokenize=False,
)
inputs = tokenizer(prompt_text, return_tensors="pt")
model_input_device = next(model.parameters()).device
inputs = {
key: value.to(model_input_device) if isinstance(value, torch.Tensor) else value
for key, value in inputs.items()
}
input_len = inputs["input_ids"].shape[-1]
with torch.inference_mode():
outputs = model.generate(
**inputs,
max_new_tokens=request.max_new_tokens,
do_sample=False,
pad_token_id=tokenizer.eos_token_id,
)
generated_tokens = outputs[0][input_len:]
text = tokenizer.decode(generated_tokens, skip_special_tokens=True).strip()
return {"text": text}