Milad Matinfar commited on
Commit
b1ed99d
·
1 Parent(s): 1df5d66

Initial FastAPI backend

Browse files
Files changed (3) hide show
  1. Dockerfile +17 -0
  2. requirements.txt +5 -0
  3. server.py +78 -0
Dockerfile ADDED
@@ -0,0 +1,17 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ FROM python:3.11-slim
2
+
3
+ ENV PIP_NO_CACHE_DIR=1 PYTHONDONTWRITEBYTECODE=1
4
+ WORKDIR /app
5
+
6
+ COPY requirements.txt .
7
+ RUN pip install --no-cache-dir -r requirements.txt
8
+
9
+ COPY server.py .
10
+
11
+ ENV MODEL_ID="milaadesign/helper-qwen-1_5b"
12
+ ENV MODEL_MAX_LEN=512
13
+ ENV TORCH_NUM_THREADS=2
14
+ ENV OMP_NUM_THREADS=2
15
+
16
+ EXPOSE 7860
17
+ CMD ["uvicorn", "server:app", "--host", "0.0.0.0", "--port", "7860"]
requirements.txt ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
 
1
+ fastapi==0.115.0
2
+ uvicorn[standard]==0.30.6
3
+ transformers==4.57.1
4
+ torch==2.2.2
5
+ safetensors==0.4.4
server.py ADDED
@@ -0,0 +1,78 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os, torch
2
+ from fastapi import FastAPI
3
+ from fastapi.middleware.cors import CORSMiddleware
4
+ from pydantic import BaseModel
5
+ from transformers import AutoTokenizer, AutoModelForCausalLM
6
+
7
+ # Hugging Face model ID you just uploaded
8
+ MODEL_ID = os.getenv("MODEL_ID", "milaadesign/helper-qwen-1_5b")
9
+ MODEL_MAX_LEN = int(os.getenv("MODEL_MAX_LEN", "512"))
10
+
11
+ app = FastAPI(title="Patient Helper API")
12
+
13
+ # CORS so your DreamHost site can call it from the browser
14
+ app.add_middleware(
15
+ CORSMiddleware,
16
+ allow_origins=["*"], # later you can restrict to your domain
17
+ allow_credentials=True,
18
+ allow_methods=["*"],
19
+ allow_headers=["*"],
20
+ )
21
+
22
+ class GenerateIn(BaseModel):
23
+ prompt: str
24
+ max_new_tokens: int = 128
25
+ temperature: float = 0.2
26
+ top_p: float = 0.9
27
+ repetition_penalty: float = 1.05
28
+
29
+ device = torch.device("cpu") # Spaces CPU Basic
30
+
31
+ torch.set_num_threads(int(os.getenv("TORCH_NUM_THREADS", "2")))
32
+ os.environ.setdefault("OMP_NUM_THREADS", "2")
33
+
34
+ print(f"Loading tokenizer from {MODEL_ID}...")
35
+ tok = AutoTokenizer.from_pretrained(MODEL_ID)
36
+ if tok.pad_token is None:
37
+ tok.pad_token = tok.eos_token
38
+ tok.truncation_side = "left"
39
+ tok.model_max_length = MODEL_MAX_LEN
40
+
41
+ print(f"Loading model from {MODEL_ID}...")
42
+ model = AutoModelForCausalLM.from_pretrained(
43
+ MODEL_ID,
44
+ torch_dtype=torch.float32,
45
+ low_cpu_mem_usage=True,
46
+ )
47
+ model.to(device)
48
+ model.eval()
49
+ print("Model loaded.")
50
+
51
+ SYSTEM = (
52
+ "You are a supportive, non-clinical assistant. "
53
+ "Offer gentle, practical tips on how to support a patient. "
54
+ "Do not diagnose or give unsafe advice. Encourage professional help when needed."
55
+ )
56
+
57
+ @app.post("/generate")
58
+ def generate(body: GenerateIn):
59
+ messages = [
60
+ {"role": "system", "content": SYSTEM},
61
+ {"role": "user", "content": body.prompt.strip()},
62
+ ]
63
+ text = tok.apply_chat_template(messages, tokenize=False, add_generation_prompt=True)
64
+ inputs = tok(text, return_tensors="pt", truncation=True).to(device)
65
+
66
+ with torch.no_grad():
67
+ out = model.generate(
68
+ **inputs,
69
+ max_new_tokens=body.max_new_tokens,
70
+ temperature=body.temperature,
71
+ top_p=body.top_p,
72
+ repetition_penalty=body.repetition_penalty,
73
+ do_sample=body.temperature > 0,
74
+ pad_token_id=tok.eos_token_id,
75
+ )
76
+
77
+ resp = tok.decode(out[0], skip_special_tokens=True)
78
+ return {"text": resp}