Z User
fix: Vicuna template instead of ChatML
8addbf1
Raw
History Blame Contribute Delete
5.34 kB
#!/usr/bin/env python3
"""WHOAMISec Wizard-Vicuna-13B — OpenAI-compatible API via Ollama (zero compilation)."""
import os, sys, json, time, subprocess
from starlette.applications import Starlette
from starlette.routing import Route
from starlette.responses import JSONResponse, StreamingResponse
from starlette.middleware.cors import CORSMiddleware
import httpx
MODEL_DIR = "/data/model"
MODEL_FILE = os.path.join(MODEL_DIR, "Wizard-Vicuna-13B-Uncensored.Q4_K_M.gguf")
MODEL_REPO = "TheBloke/Wizard-Vicuna-13B-Uncensored-GGUF"
MODEL_FILENAME = "Wizard-Vicuna-13B-Uncensored.Q4_K_M.gguf"
MODEL_NAME = "whoamisec-wizard-13b"
OLLAMA_URL = "http://localhost:11434"
ready = False
def download_model():
os.makedirs(MODEL_DIR, exist_ok=True)
if os.path.exists(MODEL_FILE):
print(f"[SRV] Model cached: {MODEL_FILE}", flush=True)
return
print(f"[SRV] Downloading {MODEL_FILENAME}...", flush=True)
hf_token = os.environ.get("HF_TOKEN", "")
from huggingface_hub import hf_hub_download
path = hf_hub_download(MODEL_REPO, MODEL_FILENAME, local_dir=MODEL_DIR, local_dir_use_symlinks=False, token=hf_token or None)
print(f"[SRV] Downloaded: {path}", flush=True)
def start_ollama():
global ready
# Step 1: Download model FIRST
download_model()
# Step 2: Start Ollama server
print("[SRV] Starting Ollama server...", flush=True)
subprocess.Popen(
["ollama", "serve"],
stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL
)
for _ in range(30):
try:
r = httpx.get(f"{OLLAMA_URL}/api/tags", timeout=2)
if r.status_code == 200:
print("[SRV] Ollama ready!", flush=True)
break
except:
pass
time.sleep(1)
# Step 3: Generate Modelfile dynamically
modelfile_content = f'''FROM {MODEL_FILE}
TEMPLATE """{{{{ if .System }}}}{{{{ .System }}}}
{{{{ end }}}}{{{{ range .Messages }}}}{{{{ if eq .Role "user" }}}}
USER: {{{{ .Content }}}}
ASSISTANT: {{{{ end }}}}
{{{{ end }}}}
"""
PARAMETER stop "</s>"
PARAMETER num_ctx 2048
'''
mf_path = "/tmp/Modelfile"
with open(mf_path, "w") as f:
f.write(modelfile_content)
# Step 4: Create model in Ollama
print(f"[SRV] Creating model '{MODEL_NAME}' from {MODEL_FILE}...", flush=True)
result = subprocess.run(
["ollama", "create", MODEL_NAME, "-f", mf_path],
capture_output=True, text=True
)
if result.returncode != 0:
print(f"[SRV] ERROR creating model: {result.stderr[:500]}", flush=True)
sys.exit(1)
print(f"[SRV] Model '{MODEL_NAME}' ready!", flush=True)
ready = True
# Start everything in background (download + ollama setup)
import threading
threading.Thread(target=start_ollama, daemon=True).start()
async def chat_completions(request):
if not ready:
return JSONResponse({"error": {"message": "model loading", "type": "server_error"}}, status_code=503)
try:
body = await request.json()
async with httpx.AsyncClient(timeout=600) as client:
if body.get("stream", False):
resp = await client.post(
f"{OLLAMA_URL}/v1/chat/completions",
json={**body, "model": MODEL_NAME},
headers={"Content-Type": "application/json"},
)
return StreamingResponse(
resp.aiter_bytes(),
media_type=resp.headers.get("content-type", "text/event-stream"),
headers={"Cache-Control": "no-cache", "X-Accel-Buffering": "no"},
)
else:
resp = await client.post(
f"{OLLAMA_URL}/v1/chat/completions",
json={**body, "model": MODEL_NAME},
headers={"Content-Type": "application/json"},
)
data = resp.json()
if "model" in data:
data["model"] = body.get("model", MODEL_NAME)
return JSONResponse(data)
except Exception as e:
return JSONResponse({"error": {"message": str(e), "type": "server_error"}}, status_code=500)
async def models(request):
return JSONResponse({
"object": "list",
"data": [{"id": MODEL_NAME, "object": "model", "owned_by": "whoamisec",
"permissions": [{"allow_sampling": True, "allow_logprobs": True, "allow_search_indices": True, "allow_view": True, "allow_fine_tuning": False}]}],
})
async def health(request):
if ready:
try:
async with httpx.AsyncClient(timeout=5) as client:
r = await client.get(f"{OLLAMA_URL}/api/tags")
if r.status_code == 200:
return JSONResponse({"status": "ok", "model": MODEL_NAME})
except:
pass
return JSONResponse({"status": "loading", "model": MODEL_NAME})
app = Starlette(debug=False, routes=[
Route("/v1/chat/completions", chat_completions, methods=["POST"]),
Route("/v1/models", models, methods=["GET"]),
Route("/health", health, methods=["GET"]),
])
app.add_middleware(CORSMiddleware, allow_origins=["*"], allow_methods=["*"], allow_headers=["*"])
if __name__ == "__main__":
import uvicorn
uvicorn.run(app, host="0.0.0.0", port=7860, log_level="warning")