| import os |
| import json |
| import uuid |
| import time |
| from fastapi import FastAPI, Request |
| from fastapi.staticfiles import StaticFiles |
| from fastapi.responses import FileResponse, JSONResponse |
| from pydantic import BaseModel |
| from typing import List, Optional |
| import uvicorn |
| import torch |
|
|
| try: |
| from unsloth import FastLanguageModel |
| UNSLOTH_AVAILABLE = True |
| except ImportError: |
| UNSLOTH_AVAILABLE = False |
|
|
| |
| |
| |
| DATASET_FILE = os.path.join("datasets", "human_alignment.jsonl") |
| os.makedirs("datasets", exist_ok=True) |
|
|
| |
| |
| |
| app = FastAPI(title="SAIL Sovereign Chat") |
|
|
| |
| class Message(BaseModel): |
| role: str |
| content: str |
| |
| class ChatRequest(BaseModel): |
| messages: List[Message] |
| |
| class FeedbackRequest(BaseModel): |
| conversation_id: str |
| user_prompt: str |
| ai_response: str |
| feedback: str |
|
|
| |
| |
| |
| MODEL_PATH = os.path.join(os.path.dirname(__file__), "sail_5b_hf_model") |
| model = None |
| tokenizer = None |
|
|
| def load_model(): |
| global model, tokenizer |
| if model is not None: |
| return |
| |
| print("--------------------------------------------------") |
| print("π§ BOOTING 5-BILLION PARAMETER ENGINE INTO VRAM...") |
| print("Loading via Unsloth 4-bit NF4 to secure 8GB RTX 4060 Space.") |
| print("--------------------------------------------------") |
| |
| if UNSLOTH_AVAILABLE: |
| model, tokenizer = FastLanguageModel.from_pretrained( |
| model_name=MODEL_PATH, |
| max_seq_length=2048, |
| load_in_4bit=True, |
| fast_inference=True, |
| ) |
| FastLanguageModel.for_inference(model) |
| print("β
5B Engine Locked and Loaded Successfully.") |
| else: |
| print("β CRITICAL: Unsloth not found. Model cannot fit in VRAM natively.") |
|
|
| |
| if os.path.exists(MODEL_PATH) and UNSLOTH_AVAILABLE: |
| load_model() |
|
|
| def generate_reply(conv_history: list) -> str: |
| if model is None or tokenizer is None: |
| return "<thought>\nThe 5B weights haven't been loaded into memory correctly.</thought>\n\n<answer>\nError: Model architecture is not loaded. Make sure `sail_5b_hf_model` exists and Unsloth is active.</answer>" |
| |
| |
| try: |
| |
| prompt = tokenizer.apply_chat_template( |
| conv_history, tokenize=False, add_generation_prompt=True |
| ) |
| except: |
| |
| prompt = "" |
| for msg in conv_history: |
| prompt += f"{msg['role'].capitalize()}: {msg['content']}\n" |
| prompt += "Assistant: " |
|
|
| |
| inputs = tokenizer([prompt], return_tensors="pt").to("cuda") |
| |
| |
| outputs = model.generate( |
| **inputs, |
| max_new_tokens=512, |
| use_cache=True, |
| temperature=0.7, |
| do_sample=True, |
| pad_token_id=tokenizer.eos_token_id |
| ) |
| |
| |
| generated_ids = outputs[0][inputs.input_ids.shape[1]:] |
| response_text = tokenizer.decode(generated_ids, skip_special_tokens=True).strip() |
| |
| return response_text |
|
|
| def save_to_dataset(record: dict): |
| with open(DATASET_FILE, "a", encoding="utf-8") as f: |
| f.write(json.dumps(record) + "\n") |
|
|
| |
| |
| |
|
|
| @app.post("/api/chat") |
| async def api_chat(req: ChatRequest): |
| req_msgs = [{"role": m.role, "content": m.content} for m in req.messages] |
| |
| |
| response_text = generate_reply(req_msgs) |
| |
| conv_id = str(uuid.uuid4()) |
| |
| return JSONResponse({ |
| "conversation_id": conv_id, |
| "reply": response_text |
| }) |
|
|
| @app.post("/api/feedback") |
| async def api_feedback(req: FeedbackRequest): |
| |
| record = { |
| "id": req.conversation_id, |
| "prompt": req.user_prompt, |
| "chosen": req.ai_response if req.feedback == "good" else "", |
| "rejected": req.ai_response if req.feedback == "bad" else "", |
| "timestamp": time.time() |
| } |
| save_to_dataset(record) |
| return JSONResponse({"status": "saved"}) |
|
|
| |
| |
| |
| UI_PATH = os.path.join(os.path.dirname(__file__), "ui") |
| os.makedirs(UI_PATH, exist_ok=True) |
|
|
| |
| app.mount("/static", StaticFiles(directory=UI_PATH), name="static") |
|
|
| @app.get("/") |
| async def root(): |
| index_path = os.path.join(UI_PATH, "index.html") |
| if not os.path.exists(index_path): |
| return JSONResponse({"error": "UI files not built yet."}) |
| return FileResponse(index_path) |
|
|
| if __name__ == "__main__": |
| print("--------------------------------------------------") |
| print("π SAIL REST SERVER BOOTING...") |
| print("π‘ Running on: http://127.0.0.1:8000") |
| print("--------------------------------------------------") |
| uvicorn.run("api_server:app", host="127.0.0.1", port=8000, reload=True) |
|
|