ugonna commited on
Commit
69d444b
·
verified ·
1 Parent(s): 3a5bab4

Update main.py

Browse files
Files changed (1) hide show
  1. main.py +29 -22
main.py CHANGED
@@ -4,7 +4,7 @@ import uvicorn
4
  from fastapi import FastAPI, HTTPException
5
  from fastapi.middleware.cors import CORSMiddleware
6
  from pydantic import BaseModel
7
- from transformers import AutoModelForCausalLM, AutoTokenizer
8
  import time
9
  import uuid
10
  import logging
@@ -12,7 +12,6 @@ import logging
12
  logging.basicConfig(level=logging.INFO)
13
  logger = logging.getLogger(__name__)
14
 
15
- # Request/Response Models
16
  class GenerateRequest(BaseModel):
17
  prompt: str
18
  max_tokens: int = 512
@@ -24,14 +23,12 @@ class GenerateResponse(BaseModel):
24
  text: str
25
  created: int
26
 
27
- # Model Manager
28
  class ModelManager:
29
  def __init__(self):
30
  self.model = None
31
  self.tokenizer = None
32
  self.device = None
33
  self.loading_status = "not_started"
34
- # Use a model from Hugging Face Hub
35
  self.model_id = os.environ.get("MODEL_ID", "ugonna/llama3.18B-Fine-tunedByUgo3")
36
 
37
  def setup_device(self):
@@ -40,7 +37,7 @@ class ModelManager:
40
  logger.info(f"✅ Using GPU: {torch.cuda.get_device_name(0)}")
41
  else:
42
  self.device = torch.device("cpu")
43
- logger.info("⚠️ Using CPU (slower for LLMs)")
44
 
45
  def load_model(self):
46
  try:
@@ -52,20 +49,27 @@ class ModelManager:
52
  if self.tokenizer.pad_token is None:
53
  self.tokenizer.pad_token = self.tokenizer.eos_token
54
 
55
- logger.info(f"📥 Loading model from {self.model_id}...")
 
 
 
 
 
 
56
 
57
- # Load with optimizations for CPU
58
  self.model = AutoModelForCausalLM.from_pretrained(
59
  self.model_id,
60
- torch_dtype=torch.float32, # Use float32 for CPU
 
 
61
  low_cpu_mem_usage=True,
62
  trust_remote_code=True
63
  )
64
 
65
- # Move to CPU explicitly
66
- self.model = self.model.to(self.device)
67
- self.model.eval()
68
 
 
69
  self.loading_status = "loaded"
70
  logger.info("✅ Model loaded successfully!")
71
  return True
@@ -73,6 +77,7 @@ class ModelManager:
73
  except Exception as e:
74
  self.loading_status = "failed"
75
  logger.error(f"❌ Failed to load model: {e}")
 
76
  return False
77
 
78
  def generate(self, prompt: str, max_tokens: int = 512, temperature: float = 0.7, top_p: float = 0.9):
@@ -80,15 +85,17 @@ class ModelManager:
80
  raise Exception("Model not loaded")
81
 
82
  start_time = time.time()
83
- inputs = self.tokenizer(prompt, return_tensors="pt", truncation=True, max_length=1024)
84
 
85
- # Move inputs to the same device as model
86
- inputs = {k: v.to(self.device) for k, v in inputs.items()}
 
 
87
 
88
  with torch.no_grad():
89
  outputs = self.model.generate(
90
  **inputs,
91
- max_new_tokens=max_tokens,
92
  temperature=temperature,
93
  top_p=top_p,
94
  do_sample=True,
@@ -104,13 +111,10 @@ class ModelManager:
104
 
105
  return generated_text
106
 
107
- # Initialize model manager
108
  model_manager = ModelManager()
109
 
110
- # FastAPI app
111
  app = FastAPI(title="NAI Bot API", version="1.0.0")
112
 
113
- # CORS
114
  app.add_middleware(
115
  CORSMiddleware,
116
  allow_origins=["*"],
@@ -121,7 +125,6 @@ app.add_middleware(
121
 
122
  @app.on_event("startup")
123
  async def startup_event():
124
- """Load model in background on startup"""
125
  import threading
126
  thread = threading.Thread(target=model_manager.load_model, daemon=True)
127
  thread.start()
@@ -132,13 +135,14 @@ async def root():
132
  "message": "NAI Bot API is running",
133
  "docs": "/docs",
134
  "status": model_manager.loading_status,
135
- "model": model_manager.model_id
 
136
  }
137
 
138
  @app.get("/health")
139
  async def health_check():
140
  return {
141
- "status": "healthy" if model_manager.model is not None else "loading",
142
  "model_loaded": model_manager.model is not None,
143
  "loading_status": model_manager.loading_status,
144
  "device": str(model_manager.device) if model_manager.device else "unknown",
@@ -148,7 +152,10 @@ async def health_check():
148
  @app.post("/generate", response_model=GenerateResponse)
149
  async def generate(request: GenerateRequest):
150
  if model_manager.model is None:
151
- raise HTTPException(status_code=503, detail=f"Model is still loading (status: {model_manager.loading_status}). Try again in a few seconds.")
 
 
 
152
 
153
  if not request.prompt:
154
  raise HTTPException(status_code=400, detail="Prompt cannot be empty")
 
4
  from fastapi import FastAPI, HTTPException
5
  from fastapi.middleware.cors import CORSMiddleware
6
  from pydantic import BaseModel
7
+ from transformers import AutoModelForCausalLM, AutoTokenizer, BitsAndBytesConfig
8
  import time
9
  import uuid
10
  import logging
 
12
  logging.basicConfig(level=logging.INFO)
13
  logger = logging.getLogger(__name__)
14
 
 
15
  class GenerateRequest(BaseModel):
16
  prompt: str
17
  max_tokens: int = 512
 
23
  text: str
24
  created: int
25
 
 
26
  class ModelManager:
27
  def __init__(self):
28
  self.model = None
29
  self.tokenizer = None
30
  self.device = None
31
  self.loading_status = "not_started"
 
32
  self.model_id = os.environ.get("MODEL_ID", "ugonna/llama3.18B-Fine-tunedByUgo3")
33
 
34
  def setup_device(self):
 
37
  logger.info(f"✅ Using GPU: {torch.cuda.get_device_name(0)}")
38
  else:
39
  self.device = torch.device("cpu")
40
+ logger.info("⚠️ Using CPU - this will be VERY slow for large models")
41
 
42
  def load_model(self):
43
  try:
 
49
  if self.tokenizer.pad_token is None:
50
  self.tokenizer.pad_token = self.tokenizer.eos_token
51
 
52
+ logger.info(f"📥 Loading model from {self.model_id} with quantization...")
53
+
54
+ # Use 8-bit quantization to reduce memory (requires bitsandbytes)
55
+ bnb_config = BitsAndBytesConfig(
56
+ load_in_8bit=True,
57
+ llm_int8_threshold=6.0
58
+ )
59
 
 
60
  self.model = AutoModelForCausalLM.from_pretrained(
61
  self.model_id,
62
+ quantization_config=bnb_config if torch.cuda.is_available() else None,
63
+ device_map="auto" if torch.cuda.is_available() else None,
64
+ torch_dtype=torch.float16 if torch.cuda.is_available() else torch.float32,
65
  low_cpu_mem_usage=True,
66
  trust_remote_code=True
67
  )
68
 
69
+ if not torch.cuda.is_available():
70
+ self.model = self.model.to(self.device)
 
71
 
72
+ self.model.eval()
73
  self.loading_status = "loaded"
74
  logger.info("✅ Model loaded successfully!")
75
  return True
 
77
  except Exception as e:
78
  self.loading_status = "failed"
79
  logger.error(f"❌ Failed to load model: {e}")
80
+ logger.error("This model may be too large for the free tier. Consider using a smaller model.")
81
  return False
82
 
83
  def generate(self, prompt: str, max_tokens: int = 512, temperature: float = 0.7, top_p: float = 0.9):
 
85
  raise Exception("Model not loaded")
86
 
87
  start_time = time.time()
88
+ inputs = self.tokenizer(prompt, return_tensors="pt", truncation=True, max_length=512)
89
 
90
+ if torch.cuda.is_available():
91
+ inputs = {k: v.cuda() for k, v in inputs.items()}
92
+ else:
93
+ inputs = {k: v.to(self.device) for k, v in inputs.items()}
94
 
95
  with torch.no_grad():
96
  outputs = self.model.generate(
97
  **inputs,
98
+ max_new_tokens=min(max_tokens, 200), # Limit for CPU
99
  temperature=temperature,
100
  top_p=top_p,
101
  do_sample=True,
 
111
 
112
  return generated_text
113
 
 
114
  model_manager = ModelManager()
115
 
 
116
  app = FastAPI(title="NAI Bot API", version="1.0.0")
117
 
 
118
  app.add_middleware(
119
  CORSMiddleware,
120
  allow_origins=["*"],
 
125
 
126
  @app.on_event("startup")
127
  async def startup_event():
 
128
  import threading
129
  thread = threading.Thread(target=model_manager.load_model, daemon=True)
130
  thread.start()
 
135
  "message": "NAI Bot API is running",
136
  "docs": "/docs",
137
  "status": model_manager.loading_status,
138
+ "model": model_manager.model_id,
139
+ "warning": "Large model on free tier may fail due to memory limits"
140
  }
141
 
142
  @app.get("/health")
143
  async def health_check():
144
  return {
145
+ "status": "healthy" if model_manager.model is not None else "failed",
146
  "model_loaded": model_manager.model is not None,
147
  "loading_status": model_manager.loading_status,
148
  "device": str(model_manager.device) if model_manager.device else "unknown",
 
152
  @app.post("/generate", response_model=GenerateResponse)
153
  async def generate(request: GenerateRequest):
154
  if model_manager.model is None:
155
+ raise HTTPException(
156
+ status_code=503,
157
+ detail=f"Model failed to load. Your 8B model is too large for free tier. Please switch to a smaller model like 'microsoft/DialoGPT-small'"
158
+ )
159
 
160
  if not request.prompt:
161
  raise HTTPException(status_code=400, detail="Prompt cannot be empty")