Spaces:
Sleeping
Sleeping
| from fastapi import FastAPI, HTTPException | |
| from pydantic import BaseModel | |
| import os | |
| import torch | |
| from langchain_community.document_loaders import PyMuPDFLoader | |
| from langchain_text_splitters import RecursiveCharacterTextSplitter | |
| from langchain_community.vectorstores import FAISS | |
| from langchain_community.embeddings import HuggingFaceEmbeddings | |
| from transformers import AutoTokenizer, AutoModelForCausalLM, BitsAndBytesConfig, pipeline | |
| app = FastAPI(title="Flykite Model Serving Engine") | |
| class QueryRequest(BaseModel): | |
| question: str | |
| chunk_size: int = 1000 | |
| chunk_overlap: int = 200 | |
| k_depth: int = 3 | |
| temperature: float = 0.20 | |
| top_p: float = 0.90 | |
| # Pre-caching models on container start | |
| embeddings = HuggingFaceEmbeddings(model_name="BAAI/bge-small-en-v1.5") | |
| bnb_config = BitsAndBytesConfig( | |
| load_in_4bit=True, | |
| bnb_4bit_quant_type="nf4", | |
| bnb_4bit_use_double_quant=True, | |
| bnb_4bit_compute_dtype=torch.float16 | |
| ) | |
| model_id = "mistralai/Mistral-7B-Instruct-v0.3" | |
| tokenizer = AutoTokenizer.from_pretrained(model_id) | |
| model = AutoModelForCausalLM.from_pretrained( | |
| model_id, quantization_config=bnb_config, device_map="auto" | |
| ) | |
| pdf_path = "Flykite Airlines_ HRP.pdf" | |
| raw_documents = PyMuPDFLoader(pdf_path).load() if os.path.exists(pdf_path) else None | |
| def health(): | |
| return {"status": "ready", "document_indexed": raw_documents is not None} | |
| def generate(payload: QueryRequest): | |
| # Checkpoint 1: Confirm payload extraction | |
| print(f"π¬ [API RECEIVED] Incoming query captured: '{payload.question}'") | |
| if not raw_documents: | |
| raise HTTPException(status_code=500, detail="Handbook missing on host filesystem.") | |
| # Checkpoint 2: Monitor the Data Engineering processing phase | |
| print(f"π [DATA PROCESSING] Slicing text chunks... Size: {payload.chunk_size} | Overlap: {payload.chunk_overlap}") | |
| splitter = RecursiveCharacterTextSplitter(chunk_size=payload.chunk_size, chunk_overlap=payload.chunk_overlap) | |
| chunks = splitter.split_documents(raw_documents) | |
| # Checkpoint 3: Monitor Vector Database indexing | |
| print(f"π¦ [INDEXING] Rebuilding dynamic FAISS vector matrix with {len(chunks)} nodes...") | |
| db = FAISS.from_documents(chunks, embeddings) | |
| retriever = db.as_retriever(search_kwargs={"k": payload.k_depth}) | |
| # Checkpoint 4: Confirm Retrieval Success | |
| docs = retriever.invoke(payload.question) | |
| print(f"π [RETRIEVED] Pulling context... Extracted top {len(docs)} segments for prompt insertion.") | |
| context = "\n\n".join([c.page_content for c in docs]) | |
| audit = [{"page": d.metadata.get("page", "unknown"), "content": d.page_content} for d in docs] | |
| prompt = ( | |
| f"You are the official Flykite Assistant. Base answers ONLY on context.\n\n" | |
| f"CONTEXT:\n{context}\n\nQUERY: {payload.question}\n\nRESPONSE:" | |
| ) | |
| # Checkpoint 5: Model Inference Start Execution | |
| print("π§ [LLM RUNNING] Tokenizing prompt and spinning compute core via Mistral-7B...") | |
| gen = pipeline("text-generation", model=model, tokenizer=tokenizer, max_new_tokens=512, | |
| temperature=payload.temperature, top_p=payload.top_p, do_sample=True, pad_token_id=tokenizer.eos_token_id) | |
| response_payload = gen(prompt)[0]["generated_text"].split("RESPONSE:")[-1].strip() | |
| # Checkpoint 6: Success confirmation | |
| print("π [SUCCESS] Response compiled. Shipping payload back to frontend.") | |
| return {"answer": response_payload, "audit_trail": audit} | |