diba-embed-api / app.py
Mousiol's picture
Update app.py
737f360 verified
Raw History Blame Contribute Delete
5.77 kB
import os
import sys
import secrets
import spaces
import torch
import gradio as gr
from typing import List, Union, Optional, Dict, Any
from fastapi import APIRouter, Header, HTTPException, status
from pydantic import BaseModel
from huggingface_hub import snapshot_download
from sentence_transformers import SentenceTransformer
EMBED_MODEL_ID = "Dibachain/Diba-Embed"
MIZAN_MODEL_ID = "Nimruz/Mizan-v3-3B"
API_KEY = os.environ.get("MY_API_KEY", "")
device = "cuda" if torch.cuda.is_available() else "cpu"
# 1. Load Diba-Embed and register with ZeroGPU
print(f"Loading {EMBED_MODEL_ID}...")
embed_model = SentenceTransformer(
EMBED_MODEL_ID,
trust_remote_code=True,
model_kwargs={"torch_dtype": torch.bfloat16}
)
embed_model.max_seq_length = 512
if torch.cuda.is_available():
embed_model = embed_model.to("cuda")
print("Diba-Embed loaded successfully!")
# 2. Download and Load Nimruz/Mizan-v3-3B
print(f"Downloading and loading {MIZAN_MODEL_ID}...")
mizan_path = snapshot_download(MIZAN_MODEL_ID)
if mizan_path not in sys.path:
sys.path.insert(0, mizan_path)
from joint_schema_model import load_release_model, systemone
target_device = "cuda" if torch.cuda.is_available() else "cpu"
mizan_model, mizan_processor = load_release_model(mizan_path, device=target_device)
print("Mizan-v3-3B loaded successfully!")
# 3. API Key Helper
def verify_key(provided_key: Optional[str]):
if not API_KEY:
raise RuntimeError("MY_API_KEY secret is not configured in Space Settings.")
if not provided_key or not secrets.compare_digest(provided_key, API_KEY):
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Invalid or missing API Key"
)
# 4. ZeroGPU Function 1: Embeddings
@spaces.GPU(duration=30)
def compute_embeddings(
api_key: str,
texts: Union[str, List[str]],
input_type: str = "document",
normalize: bool = True
):
if not API_KEY or not secrets.compare_digest(api_key or "", API_KEY):
raise gr.Error("Unauthorized: Invalid API Key")
text_list = [texts] if isinstance(texts, str) else texts
prompt_name = "query" if input_type == "query" else None
embeddings = embed_model.encode(
text_list,
prompt_name=prompt_name,
normalize_embeddings=normalize,
batch_size=8
)
return [
{"object": "embedding", "index": i, "embedding": emb.tolist()}
for i, emb in enumerate(embeddings)
]
# 5. ZeroGPU Function 2: Mizan-v3-3B Decision Inference
@spaces.GPU(duration=30)
def compute_decision(
api_key: str,
state: Any,
questions: Dict[str, Any]
):
if not API_KEY or not secrets.compare_digest(api_key or "", API_KEY):
raise gr.Error("Unauthorized: Invalid API Key")
payload = {
"model": MIZAN_MODEL_ID,
"state": state,
"questions": questions
}
with torch.no_grad():
return systemone(mizan_model, mizan_processor, payload)
# 6. Build a Tabbed Gradio Interface so ZeroGPU scans and registers BOTH @spaces.GPU functions
embed_ui = gr.Interface(
fn=compute_embeddings,
inputs=[
gr.Textbox(label="API Key", type="password"),
gr.Textbox(label="Input Text"),
gr.Radio(["document", "query"], value="document", label="Input Type"),
gr.Checkbox(value=True, label="Normalize")
],
outputs=gr.JSON(label="Embeddings Output"),
api_name=False
)
mizan_ui = gr.Interface(
fn=compute_decision,
inputs=[
gr.Textbox(label="API Key", type="password"),
gr.Textbox(label="State (Text or JSON)"),
gr.JSON(label="Questions Dict")
],
outputs=gr.JSON(label="Decision Output"),
api_name=False
)
demo = gr.TabbedInterface(
[embed_ui, mizan_ui],
["Diba-Embed", "Mizan-v3-3B"],
title="Diba-Embed & Mizan-v3-3B Private API"
)
# Launch Gradio with ssr_mode=False so FastAPI binds directly to port 7860
demo.launch(prevent_thread_lock=True, ssr_mode=False)
# 7. Create an APIRouter for custom endpoints
api_router = APIRouter()
class EmbedRequest(BaseModel):
input: Union[str, List[str]]
input_type: Optional[str] = "document"
normalize: Optional[bool] = True
class DecisionRequest(BaseModel):
model: Optional[str] = MIZAN_MODEL_ID
state: Any
questions: Dict[str, Any]
def extract_token(authorization: Optional[str], x_api_key: Optional[str]) -> Optional[str]:
if x_api_key:
return x_api_key
if authorization and authorization.lower().startswith("bearer "):
return authorization[7:].strip()
return None
@api_router.get("/health")
def health_check():
return {
"status": "online",
"models": [EMBED_MODEL_ID, MIZAN_MODEL_ID],
"endpoints": ["/v1/embeddings", "/v1/systemone", "/v1/decide"]
}
@api_router.post("/v1/embeddings")
def create_embeddings(
req: EmbedRequest,
authorization: Optional[str] = Header(default=None),
x_api_key: Optional[str] = Header(default=None)
):
token = extract_token(authorization, x_api_key)
verify_key(token)
data = compute_embeddings(token, req.input, req.input_type or "document", req.normalize)
return {
"object": "list",
"model": EMBED_MODEL_ID,
"data": data
}
@api_router.post("/v1/systemone")
@api_router.post("/v1/decide")
def create_decision(
req: DecisionRequest,
authorization: Optional[str] = Header(default=None),
x_api_key: Optional[str] = Header(default=None)
):
token = extract_token(authorization, x_api_key)
verify_key(token)
return compute_decision(token, req.state, req.questions)
# Prepend custom routes BEFORE Gradio's catch-all GET /{path:path} route
demo.app.router.routes[:0] = api_router.routes
demo.block_thread()