finoptix14b / app.py
ccortezb's picture
feat: migrate to gr.Server with custom FinOps Command Center UI
828e540 verified
Raw
History Blame Contribute Delete
2.84 kB
import os
import torch
import threading
from peft import PeftModel
from transformers import AutoModelForCausalLM, AutoTokenizer, BitsAndBytesConfig
from gradio import Server
from gradio.data_classes import FileData
from fastapi.responses import HTMLResponse
MODEL_ID = "Qwen/Qwen2.5-14B-Instruct"
ADAPTER_ID = "ccortezb/FinOptix-14B"
SYSTEM_PROMPT = """You are FinOptix-14B, an autonomous Principal Cloud Architect and FinOps Specialist.
Your purpose: analyze cloud infrastructure, audit governance compliance for AWS, and optimize configurations for extreme cost efficiency.
Rules: Valid code only, precise financials (USD, 2 decimals), structured Markdown reports, actionable recommendations aligned to FinOps Framework (Inform/Optimize/Operate). No hallucination. Professional tone."""
model = None
tokenizer = None
_load_lock = threading.Lock()
def load_model():
global model, tokenizer
if model is not None:
return
with _load_lock:
if model is not None:
return
quantization_config = BitsAndBytesConfig(
load_in_4bit=True,
bnb_4bit_compute_dtype=torch.float16,
bnb_4bit_quant_type="nf4",
bnb_4bit_use_double_quant=True,
)
tokenizer = AutoTokenizer.from_pretrained(ADAPTER_ID, trust_remote_code=True)
base_model = AutoModelForCausalLM.from_pretrained(
MODEL_ID,
quantization_config=quantization_config,
device_map="auto",
trust_remote_code=True,
low_cpu_mem_usage=True,
)
model = PeftModel.from_pretrained(base_model, ADAPTER_ID)
model.eval()
app = Server()
@app.api()
def generate(instruction: str, context: str = "", max_tokens: int = 1024) -> str:
"""Run FinOptix-14B inference."""
load_model()
prompt = instruction
if context:
prompt = f"{instruction}\n\n```\n{context}\n```"
messages = [
{"role": "system", "content": SYSTEM_PROMPT},
{"role": "user", "content": prompt},
]
text = tokenizer.apply_chat_template(messages, tokenize=False, add_generation_prompt=True)
inputs = tokenizer(text, return_tensors="pt").to(model.device)
with torch.no_grad():
outputs = model.generate(
**inputs,
max_new_tokens=max_tokens,
temperature=0.7,
top_p=0.9,
do_sample=True,
pad_token_id=tokenizer.eos_token_id,
)
response = tokenizer.decode(outputs[0][inputs["input_ids"].shape[1]:], skip_special_tokens=True)
return response
@app.get("/")
async def homepage():
html_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), "index.html")
with open(html_path, "r", encoding="utf-8") as f:
return HTMLResponse(content=f.read())
app.launch(show_error=True)