TajooM2003's picture
Upload 4 files
f4f1a3f verified
Raw
History Blame Contribute Delete
3.41 kB
from fastapi import FastAPI, Request, Header
from fastapi.responses import JSONResponse
import uvicorn, os, time, json, torch
from transformers import AutoTokenizer, AutoModelForCausalLM
# ==== CONFIG ====
MODEL_REPO = os.getenv("MODEL_REPO", "retaj/my-qwen14b-finetune") # your PRIVATE model
HF_TOKEN = os.getenv("HF_TOKEN") # REQUIRED (read token) because repo is private
SERVER_API_KEY = os.getenv("SERVER_API_KEY") # optional: require Bearer auth from clients
MAX_NEW_TOKENS = int(os.getenv("MAX_NEW_TOKENS", 256))
TEMPERATURE = float(os.getenv("TEMPERATURE", 0.7))
TOP_P = float(os.getenv("TOP_P", 0.95))
PORT = int(os.getenv("PORT", "7860")) # HF Spaces uses this
device = torch.device("cpu") # free tier is CPU-only
# ==== LOAD MODEL ====
def load_model():
tok = AutoTokenizer.from_pretrained(
MODEL_REPO, token=HF_TOKEN, use_fast=True, trust_remote_code=True
)
mdl = AutoModelForCausalLM.from_pretrained(
MODEL_REPO,
torch_dtype=torch.float32,
device_map=None,
token=HF_TOKEN,
trust_remote_code=True,
).to(device)
mdl.eval()
return tok, mdl
tokenizer, model = load_model()
# ==== Qwen chat template ====
IM_START = "<|im_start|>"
IM_END = "<|im_end|>"
def build_prompt(messages):
parts = []
for m in messages:
role = m.get("role", "user")
content = m.get("content", "")
parts.append(f"{IM_START}{role}\n{content}{IM_END}\n")
parts.append(f"{IM_START}assistant\n")
return "".join(parts)
def check_auth(header: str | None):
if SERVER_API_KEY is None:
return True
return header and header.startswith("Bearer ") and header.split(" ",1)[1] == SERVER_API_KEY
# ==== FastAPI app ====
app = FastAPI()
@app.get("/")
def root():
return {"status": "ok", "model": MODEL_REPO}
@app.post("/v1/chat/completions")
async def chat_completions(request: Request, authorization: str | None = Header(default=None)):
if not check_auth(authorization):
return JSONResponse({"error": {"message": "Unauthorized"}}, status_code=401)
body = await request.json()
messages = body.get("messages", [])
temperature = float(body.get("temperature", TEMPERATURE))
top_p = float(body.get("top_p", TOP_P))
max_tokens = int(body.get("max_tokens", MAX_NEW_TOKENS))
if not messages:
return JSONResponse({"error": {"message": "messages required"}}, status_code=400)
prompt = build_prompt(messages)
inputs = tokenizer(prompt, return_tensors="pt").to(device)
with torch.no_grad():
output_ids = model.generate(
**inputs,
max_new_tokens=max_tokens,
temperature=temperature,
top_p=top_p,
do_sample=True,
eos_token_id=tokenizer.eos_token_id,
)
decoded = tokenizer.decode(output_ids[0], skip_special_tokens=False)
marker = f"{IM_START}assistant\n"
resp = decoded.split(marker)[-1].split(IM_END)[0].strip()
now = int(time.time())
return {
"id": f"chatcmpl-{now}",
"object": "chat.completion",
"created": now,
"model": MODEL_REPO,
"choices": [
{"index": 0, "message": {"role": "assistant", "content": resp}, "finish_reason": "stop"}
],
"usage": None,
}
if __name__ == "__main__":
uvicorn.run("main:app", host="0.0.0.0", port=PORT)