Spaces:
Configuration error
Configuration error
| 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() | |
| def root(): | |
| return {"status": "ok", "model": MODEL_REPO} | |
| 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) | |