Spaces:
Sleeping
Sleeping
Update main.py
Browse files
main.py
CHANGED
|
@@ -1,4 +1,4 @@
|
|
| 1 |
-
from fastapi import FastAPI, File, UploadFile, HTTPException, Request
|
| 2 |
from fastapi.middleware.cors import CORSMiddleware
|
| 3 |
from pydantic import BaseModel
|
| 4 |
import pandas as pd
|
|
@@ -9,23 +9,21 @@ from transformers import AutoTokenizer, AutoModelForCausalLM
|
|
| 9 |
import uvicorn
|
| 10 |
import nest_asyncio
|
| 11 |
import os
|
|
|
|
| 12 |
|
| 13 |
-
# Allow uvicorn to run inside Colab
|
| 14 |
nest_asyncio.apply()
|
|
|
|
| 15 |
# ========== Environment Setup ==========
|
| 16 |
device = "cpu"
|
| 17 |
-
|
| 18 |
-
# Set offload folder path
|
| 19 |
offload_dir = "/data/model_offload"
|
| 20 |
os.makedirs(offload_dir, exist_ok=True)
|
| 21 |
|
| 22 |
adapter_path = "./phi3-ds-expert-lora"
|
| 23 |
base_model_id = "microsoft/Phi-3-mini-4k-instruct"
|
| 24 |
|
| 25 |
-
# Load LoRA config
|
| 26 |
peft_config = PeftConfig.from_pretrained(adapter_path)
|
| 27 |
|
| 28 |
-
# Load base model
|
| 29 |
base_model = AutoModelForCausalLM.from_pretrained(
|
| 30 |
base_model_id,
|
| 31 |
torch_dtype="auto",
|
|
@@ -36,7 +34,6 @@ base_model = AutoModelForCausalLM.from_pretrained(
|
|
| 36 |
tokenizer = AutoTokenizer.from_pretrained(base_model_id)
|
| 37 |
tokenizer.pad_token = tokenizer.eos_token
|
| 38 |
|
| 39 |
-
# Load LoRA adapter with explicit offload_dir
|
| 40 |
model = PeftModel.from_pretrained(
|
| 41 |
base_model,
|
| 42 |
adapter_path,
|
|
@@ -46,7 +43,6 @@ model = PeftModel.from_pretrained(
|
|
| 46 |
model.to(device)
|
| 47 |
model.eval()
|
| 48 |
|
| 49 |
-
|
| 50 |
# ========== FastAPI App ==========
|
| 51 |
app = FastAPI()
|
| 52 |
|
|
@@ -62,6 +58,9 @@ app.add_middleware(
|
|
| 62 |
# Global dataset storage
|
| 63 |
uploaded_df = None
|
| 64 |
|
|
|
|
|
|
|
|
|
|
| 65 |
# ========== Pydantic Request Schema ==========
|
| 66 |
class PromptRequest(BaseModel):
|
| 67 |
prompt: str
|
|
@@ -85,18 +84,22 @@ def generate_prompt(prompt_text, df=None):
|
|
| 85 |
with torch.no_grad():
|
| 86 |
outputs = model.generate(
|
| 87 |
**inputs,
|
| 88 |
-
max_new_tokens=128,
|
| 89 |
-
do_sample=False,
|
| 90 |
eos_token_id=tokenizer.eos_token_id,
|
| 91 |
pad_token_id=tokenizer.eos_token_id
|
| 92 |
)
|
| 93 |
|
| 94 |
-
# Decode only the new generated tokens (ignoring prompt)
|
| 95 |
generated_tokens = outputs[0][input_length:]
|
| 96 |
response = tokenizer.decode(generated_tokens, skip_special_tokens=True).strip()
|
| 97 |
|
| 98 |
return response
|
| 99 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 100 |
# ========== API Endpoints ==========
|
| 101 |
|
| 102 |
@app.get("/")
|
|
@@ -104,24 +107,37 @@ def read_root():
|
|
| 104 |
return {"message": "🚀 API is live and running!"}
|
| 105 |
|
| 106 |
@app.post("/generate")
|
| 107 |
-
async def generate(request: PromptRequest):
|
| 108 |
if not request.prompt.strip():
|
| 109 |
raise HTTPException(status_code=400, detail="Prompt cannot be empty.")
|
| 110 |
-
|
| 111 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 112 |
|
| 113 |
MAX_FILE_SIZE = 15 * 1024 * 1024 # 15 MB
|
| 114 |
|
| 115 |
@app.post("/upload")
|
| 116 |
async def upload_file(request: Request, file: UploadFile = File(...)):
|
| 117 |
-
|
| 118 |
content_length = request.headers.get("content-length")
|
| 119 |
if content_length and int(content_length) > MAX_FILE_SIZE:
|
| 120 |
raise HTTPException(status_code=413, detail="File too large. Max size is 15 MB.")
|
| 121 |
|
| 122 |
content = await file.read()
|
| 123 |
|
| 124 |
-
# Double-check actual content size after reading
|
| 125 |
if len(content) > MAX_FILE_SIZE:
|
| 126 |
raise HTTPException(status_code=413, detail="File too large. Max size is 15 MB.")
|
| 127 |
|
|
@@ -132,9 +148,6 @@ async def upload_file(request: Request, file: UploadFile = File(...)):
|
|
| 132 |
uploaded_df = pd.read_excel(io.BytesIO(content))
|
| 133 |
else:
|
| 134 |
raise HTTPException(status_code=400, detail="Only CSV and Excel files are supported.")
|
| 135 |
-
|
| 136 |
-
|
| 137 |
-
|
| 138 |
except Exception as e:
|
| 139 |
raise HTTPException(status_code=500, detail=f"Error while uploading file: {str(e)}")
|
| 140 |
|
|
|
|
| 1 |
+
from fastapi import FastAPI, File, UploadFile, HTTPException, Request, BackgroundTasks
|
| 2 |
from fastapi.middleware.cors import CORSMiddleware
|
| 3 |
from pydantic import BaseModel
|
| 4 |
import pandas as pd
|
|
|
|
| 9 |
import uvicorn
|
| 10 |
import nest_asyncio
|
| 11 |
import os
|
| 12 |
+
import uuid
|
| 13 |
|
| 14 |
+
# Allow uvicorn to run inside Colab / Spaces
|
| 15 |
nest_asyncio.apply()
|
| 16 |
+
|
| 17 |
# ========== Environment Setup ==========
|
| 18 |
device = "cpu"
|
|
|
|
|
|
|
| 19 |
offload_dir = "/data/model_offload"
|
| 20 |
os.makedirs(offload_dir, exist_ok=True)
|
| 21 |
|
| 22 |
adapter_path = "./phi3-ds-expert-lora"
|
| 23 |
base_model_id = "microsoft/Phi-3-mini-4k-instruct"
|
| 24 |
|
|
|
|
| 25 |
peft_config = PeftConfig.from_pretrained(adapter_path)
|
| 26 |
|
|
|
|
| 27 |
base_model = AutoModelForCausalLM.from_pretrained(
|
| 28 |
base_model_id,
|
| 29 |
torch_dtype="auto",
|
|
|
|
| 34 |
tokenizer = AutoTokenizer.from_pretrained(base_model_id)
|
| 35 |
tokenizer.pad_token = tokenizer.eos_token
|
| 36 |
|
|
|
|
| 37 |
model = PeftModel.from_pretrained(
|
| 38 |
base_model,
|
| 39 |
adapter_path,
|
|
|
|
| 43 |
model.to(device)
|
| 44 |
model.eval()
|
| 45 |
|
|
|
|
| 46 |
# ========== FastAPI App ==========
|
| 47 |
app = FastAPI()
|
| 48 |
|
|
|
|
| 58 |
# Global dataset storage
|
| 59 |
uploaded_df = None
|
| 60 |
|
| 61 |
+
# In-memory job results store
|
| 62 |
+
job_results = {}
|
| 63 |
+
|
| 64 |
# ========== Pydantic Request Schema ==========
|
| 65 |
class PromptRequest(BaseModel):
|
| 66 |
prompt: str
|
|
|
|
| 84 |
with torch.no_grad():
|
| 85 |
outputs = model.generate(
|
| 86 |
**inputs,
|
| 87 |
+
max_new_tokens=128,
|
| 88 |
+
do_sample=False,
|
| 89 |
eos_token_id=tokenizer.eos_token_id,
|
| 90 |
pad_token_id=tokenizer.eos_token_id
|
| 91 |
)
|
| 92 |
|
|
|
|
| 93 |
generated_tokens = outputs[0][input_length:]
|
| 94 |
response = tokenizer.decode(generated_tokens, skip_special_tokens=True).strip()
|
| 95 |
|
| 96 |
return response
|
| 97 |
|
| 98 |
+
# ========== Background Inference Task ==========
|
| 99 |
+
def run_inference(prompt_text, job_id, df=None):
|
| 100 |
+
result = generate_prompt(prompt_text, df)
|
| 101 |
+
job_results[job_id] = result
|
| 102 |
+
|
| 103 |
# ========== API Endpoints ==========
|
| 104 |
|
| 105 |
@app.get("/")
|
|
|
|
| 107 |
return {"message": "🚀 API is live and running!"}
|
| 108 |
|
| 109 |
@app.post("/generate")
|
| 110 |
+
async def generate(request: PromptRequest, background_tasks: BackgroundTasks):
|
| 111 |
if not request.prompt.strip():
|
| 112 |
raise HTTPException(status_code=400, detail="Prompt cannot be empty.")
|
| 113 |
+
|
| 114 |
+
job_id = str(uuid.uuid4())
|
| 115 |
+
job_results[job_id] = "processing"
|
| 116 |
+
background_tasks.add_task(run_inference, request.prompt, job_id, uploaded_df)
|
| 117 |
+
return {"job_id": job_id}
|
| 118 |
+
|
| 119 |
+
@app.get("/status/{job_id}")
|
| 120 |
+
async def job_status(job_id: str):
|
| 121 |
+
if job_id not in job_results:
|
| 122 |
+
raise HTTPException(status_code=404, detail="Job ID not found")
|
| 123 |
+
|
| 124 |
+
result = job_results[job_id]
|
| 125 |
+
if result == "processing":
|
| 126 |
+
return {"status": "processing"}
|
| 127 |
+
else:
|
| 128 |
+
return {"status": "complete", "result": result}
|
| 129 |
|
| 130 |
MAX_FILE_SIZE = 15 * 1024 * 1024 # 15 MB
|
| 131 |
|
| 132 |
@app.post("/upload")
|
| 133 |
async def upload_file(request: Request, file: UploadFile = File(...)):
|
| 134 |
+
global uploaded_df
|
| 135 |
content_length = request.headers.get("content-length")
|
| 136 |
if content_length and int(content_length) > MAX_FILE_SIZE:
|
| 137 |
raise HTTPException(status_code=413, detail="File too large. Max size is 15 MB.")
|
| 138 |
|
| 139 |
content = await file.read()
|
| 140 |
|
|
|
|
| 141 |
if len(content) > MAX_FILE_SIZE:
|
| 142 |
raise HTTPException(status_code=413, detail="File too large. Max size is 15 MB.")
|
| 143 |
|
|
|
|
| 148 |
uploaded_df = pd.read_excel(io.BytesIO(content))
|
| 149 |
else:
|
| 150 |
raise HTTPException(status_code=400, detail="Only CSV and Excel files are supported.")
|
|
|
|
|
|
|
|
|
|
| 151 |
except Exception as e:
|
| 152 |
raise HTTPException(status_code=500, detail=f"Error while uploading file: {str(e)}")
|
| 153 |
|