Spaces:
Sleeping
Sleeping
File size: 8,246 Bytes
b8c2b77 abaa9c0 b8c2b77 f2d7bb5 b0a2a9b b8c2b77 09898d7 abaa9c0 09898d7 7452378 abaa9c0 067d546 abaa9c0 09898d7 abaa9c0 09898d7 b0a2a9b 054aa6d 09898d7 054aa6d 78a8ba8 060b916 c47d2c7 b0a2a9b 060b916 c47d2c7 060b916 09898d7 5437317 09898d7 9ef6f34 09898d7 16fabb6 09898d7 adc3697 09898d7 adc3697 9ef6f34 adc3697 09898d7 9ef6f34 adc3697 09898d7 9ef6f34 09898d7 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 | import os
import shutil
import json
# Set custom cache directories to avoid permission issues. New
os.environ["HF_HOME"] = "/tmp/huggingface"
os.makedirs("/tmp/huggingface", exist_ok=True)
os.environ["XDG_CACHE_HOME"] = "/tmp/.cache"
os.makedirs("/tmp/.cache", exist_ok=True)
from fastapi import FastAPI, HTTPException
from fastapi.middleware.cors import CORSMiddleware
from pydantic import BaseModel
from typing import List, Optional
import torch
from transformers import AutoModelForCausalLM, AutoTokenizer, AutoConfig
import re
# Hugging Face model config
REPO_NAME = "jaydatech/phi3-finetuned-project"
BASE_MODEL = "microsoft/Phi-3-mini-4k-instruct"
HF_TOKEN = os.getenv("HF_TOKEN") # Load from Render environment variable
# # Optional: Cleanup if corrupted config is detected
# def check_and_cleanup_corrupt_cache(repo_name: str):
# cache_dir = os.environ["HF_HOME"]
# model_dir = os.path.join(cache_dir, f"models--{repo_name.replace('/', '--')}")
# if os.path.exists(model_dir):
# for root, dirs, files in os.walk(model_dir):
# for file in files:
# if file == "config.json":
# path = os.path.join(root, file)
# try:
# with open(path, "r") as f:
# json.load(f)
# except json.JSONDecodeError:
# print(f"Corrupted config file detected at {path}, cleaning up...")
# shutil.rmtree(model_dir, ignore_errors=True)
# return
# check_and_cleanup_corrupt_cache(REPO_NAME)
app = FastAPI()
# Enable CORS for frontend access
app.add_middleware(
CORSMiddleware,
allow_origins=["*"], # Or specify your frontend domain
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
# Hugging Face model config
# REPO_NAME = "jaydatech/phi3-finetuned-project"
# BASE_MODEL = "microsoft/Phi-3-mini-4k-instruct"
# HF_TOKEN = os.getenv("HF_TOKEN") # Load from Render environment variable
config = AutoConfig.from_pretrained(REPO_NAME, token=HF_TOKEN)
device = "cuda" if torch.cuda.is_available() else "cpu"
tokenizer = AutoTokenizer.from_pretrained(BASE_MODEL, trust_remote_code=True)
model = AutoModelForCausalLM.from_pretrained(
REPO_NAME,
config = config,
token=HF_TOKEN,
torch_dtype=torch.float16 if torch.cuda.is_available() else torch.float32,
device_map="auto"
)
# Message and request models
class ChatMessage(BaseModel):
role: str
text: str
class ChatRequest(BaseModel):
message: str
history: Optional[List[ChatMessage]] = []
def is_farewell(message: str) -> bool:
farewells = ["bye", "goodbye", "see you", "farewell", "exit", "quit", "end"]
message_lower = message.lower().strip()
return any(re.search(rf"\b{re.escape(f)}\b", message_lower) for f in farewells)
# ============================= Recognizes "attend" but with "end" in it... Fix is above ^ =============================
# farewells = ["bye", "goodbye", "see you", "farewell", "exit", "quit", "end"]
# message_lower = message.lower().strip()
# return any(farewell in message_lower for farewell in farewells)
def clean_input_text(text: str) -> str:
# Remove any "Instruction N: ..." or similar phrases
return re.sub(r"Instruction\s*\d+\s*\(.*?\):", "", text, flags=re.IGNORECASE)
@app.post("/chat")
async def chat(request: ChatRequest):
try:
history = request.history
user_message = request.message
if is_farewell(user_message):
return {
"response": "Goodbye! Feel free to chat again if you have more questions.",
"terminate": True
}
conversation = (
"<|system|>\nYou are an AI assistant for the Federal Reserve Bank of St. Louis. "
"Answer questions based ONLY on your knowledge of the Federal Reserve Bank of St. Louis. "
"If the answer is NOT in the training data, respond with: 'I don't think this information is available. Maybe rephrase for me!'. "
"Answer ONLY what the user asks. Do not volunteer information unless specifically requested. "
"Do NOT ask: 'How can I assist you today?' or 'What can I do for you today?' after every response you give. "
"Provide concise answers to the exact question asked and nothing more.\n"
)
seen_user_messages = set()
seen_model_messages = set()
for msg in history:
if msg.role == "user":
cleaned_user_text = clean_input_text(msg.text.strip())
if cleaned_user_text and cleaned_user_text not in seen_user_messages:
conversation += f"<|user|>\n{cleaned_user_text}\n"
seen_user_messages.add(cleaned_user_text)
elif msg.role == "model":
cleaned_model_text = clean_input_text(msg.text.strip())
if cleaned_model_text and cleaned_model_text not in seen_model_messages:
conversation += f"<|assistant|>\n{cleaned_model_text}\n"
seen_model_messages.add(cleaned_model_text)
# seen_messages = set()
# for msg in history:
# if msg.role == "user" and msg.text.strip() not in seen_messages:
# cleaned_user_text = clean_input_text(msg.text.strip())
# conversation += f"<|user|>\n{cleaned_user_text}\n"
# seen_messages.add(cleaned_user_text)
# elif msg.role == "model":
# cleaned_model_text = clean_input_text(msg.text.strip())
# conversation += f"<|assistant|>\n{cleaned_model_text}\n"
#conversation += f"<|user|>\n{user_message.strip()}\n<|assistant|>"
conversation += f"<|user|>\n{clean_input_text(user_message.strip())}\n<|assistant|>"
inputs = tokenizer(conversation, return_tensors="pt", padding=True, truncation=True, max_length=4096).to(device)
with torch.no_grad():
outputs = model.generate(
**inputs,
max_new_tokens=130,
do_sample=True,
temperature=0.1,
top_k=5,
pad_token_id=tokenizer.eos_token_id
)
full_response = tokenizer.decode(outputs[0], skip_special_tokens=False)
assistant_response = ""
if "<|assistant|>" in full_response:
assistant_sections = full_response.split("<|assistant|>")
for section in reversed(assistant_sections):
cleaned = section.strip()
if cleaned:
cleaned = re.split(
r"(<\|user\|>|<\|system\|>|<\|assistant\|>|\nuser[:\s]|<\|endoftext\|>)",
cleaned
)[0]
cleaned = re.sub(r"\n?(User|Assistant)\s*[::\-–]\s*.*", "", cleaned, flags=re.IGNORECASE).strip()
assistant_response = cleaned
break
if not assistant_response:
assistant_response = "⚠️ Sorry, I couldn't generate a response."
assistant_response = re.sub(r'\*\* Instruction \*\*:.*?(?=\n\n|\n$|$)', '', assistant_response, flags=re.DOTALL)
assistant_response = re.sub(r'\*\* Instruction \*\*.*?(?=\n\n|\n$|$)', '', assistant_response, flags=re.DOTALL)
assistant_response = re.sub(r'\n{3,}', '\n\n', assistant_response).strip()
assistant_response = re.sub(r"(How can I assist you today\?|What else can I help you with\?|How can I help you today\?)", "", assistant_response, flags=re.IGNORECASE).strip()
def remove_repeated_sentences(response):
sentences = response.split(". ")
seen = set()
cleaned = []
for sentence in sentences:
if sentence not in seen:
cleaned.append(sentence)
seen.add(sentence)
return ". ".join(cleaned)
assistant_response = remove_repeated_sentences(assistant_response)
return {"response": assistant_response}
except Exception as e:
raise HTTPException(status_code=500, detail=str(e))
|