| import os |
| import torch |
| from fastapi import FastAPI, HTTPException |
| from pydantic import BaseModel |
| from contextlib import asynccontextmanager |
| from transformers import AutoTokenizer, AutoModelForCausalLM |
|
|
| |
| ml_models = {} |
|
|
| |
| @asynccontextmanager |
| async def lifespan(app: FastAPI): |
| print("β³ Loading text model into memory... This might take a minute.") |
| |
| |
| local_model_path = os.path.join(os.environ.get('WORKSPACE_ROOT', os.path.join(os.environ.get('WORKSPACE_ROOT', '.'), 'AgenticControl/local_qwen_model" ) |
| |
| # Added trust_remote_code=True here as well |
| tokenizer = AutoTokenizer.from_pretrained( |
| local_model_path, |
| trust_remote_code=True |
| ) |
| |
| model = AutoModelForCausalLM.from_pretrained( |
| local_model_path, |
| device_map="auto", |
| torch_dtype="auto", # Saves memory by using the optimal precision |
| trust_remote_code=True |
| ) |
| |
| ml_models["tokenizer"] = tokenizer |
| ml_models["model"] = model |
| |
| print("β
Model loaded successfully! Server is ready for text requests.") |
| yield |
| |
| # Clean up when the server shuts down |
| ml_models.clear() |
| print("π Server shutting down, memory cleared.") |
| |
| # Initialize the FastAPI app with the lifespan manager |
| app = FastAPI(lifespan=lifespan) |
| |
| # ββ 2. Define the Request Data Structure ββββββββββββββββββββββββββββββββ |
| class GenerateRequest(BaseModel): |
| system_prompt: str |
| query: str |
| max_new_tokens: int = 200 # Increased default since text answers are usually longer |
| |
| # ββ 3. Create the API Endpoint ββββββββββββββββββββββββββββββββββββββββββ |
| @app.post("/generate") |
| async def generate(request: GenerateRequest): |
| try: |
| tokenizer = ml_models["tokenizer"] |
| model = ml_models["model"] |
| |
| # Format the prompt using the standard system/user role structure |
| messages = [ |
| {"role": "system", "content": request.system_prompt}, |
| {"role": "user", "content": request.query} |
| ] |
| |
| # Generate the formatted text string first (avoids dictionary/tensor errors) |
| text = tokenizer.apply_chat_template( |
| messages, |
| tokenize=False, |
| add_generation_prompt=True |
| ) |
| |
| # Tokenize the formatted string into PyTorch tensors and send to GPU/CPU |
| model_inputs = tokenizer([text], return_tensors="pt").to(model.device) |
| |
| # Generate response |
| generated_ids = model.generate( |
| **model_inputs, |
| max_new_tokens=request.max_new_tokens |
| ) |
| |
| # Decode only the newly generated text (ignoring the input prompt tokens) |
| generated_ids = [ |
| output_ids[len(input_ids):] for input_ids, output_ids in zip(model_inputs.input_ids, generated_ids) |
| ] |
| |
| result_text = tokenizer.batch_decode(generated_ids, skip_special_tokens=True)[0] |
| |
| return {"response": result_text} |
| |
| except Exception as e: |
| raise HTTPException(status_code=500, detail=str(e)) |