ffreemt
loguru
2f61920
Raw
History Blame Contribute Delete
3.64 kB
"""
ResuMinder embedding service β€” Nemotron-3-Embed-1B @ 1024d on ZeroGPU.
Public Space, secret-gated: the gate check runs BEFORE the @spaces.GPU
function, so unauthorised calls are rejected without spending one GPU-second
of quota. The shared secret lives in the Space's Secrets (never in this
repo); callers present it per request.
Contract (kept deliberately dumb β€” the caller owns text composition):
embed(payload_json, secret) -> JSON
payload: {"texts": [...], "is_query": false}
returns: {"dims": 1024, "vectors": [[...f32 x 1024], ...]}
Vectors are sliced to the first 1024 dims of the model's native 2048 and
re-L2-normalised β€” the exact configuration that tied the 8B's ceiling on the
production-corpus bake-off (dup@1 0.824 vs 0.836 Β± 0.014) while halving
vector storage. Prefixes are the model's own: 'query: ' / 'passage: '.
"""
import json
import os
import gradio as gr
import numpy as np
import spaces
import torch
from loguru import logger
from sentence_transformers import SentenceTransformer
MODEL_ID = "nvidia/Nemotron-3-Embed-1B-BF16"
DIM = 1024
MAX_TOKENS = 2048
MAX_TEXTS = 512 # one call embeds at most this many texts
API_KEY = os.environ.get("API_KEY", "")
model = SentenceTransformer(
MODEL_ID,
trust_remote_code=True,
model_kwargs={"torch_dtype": torch.bfloat16},
)
model.max_seq_length = MAX_TOKENS
# ZeroGPU pattern: a module-level .to('cuda') is intercepted by the spaces
# runtime and materialised when a GPU slice attaches to the decorated call.
try:
model.to("cuda")
except Exception:
pass
@spaces.GPU(duration=110)
def _embed_gpu(texts, is_query):
# Timed INSIDE the decorator: this duration approximates what ZeroGPU
# bills, so callers can meter real quota spend instead of wall clock
# (which includes network/queue and runs ~5x hot from the prod side).
import time
t0 = time.monotonic()
prefix = "query: " if is_query else "passage: "
v = model.encode(
[prefix + t for t in texts],
batch_size=32,
normalize_embeddings=False,
convert_to_numpy=True,
show_progress_bar=False,
).astype(np.float32)
v = v[:, :DIM]
v /= np.linalg.norm(v, axis=1, keepdims=True) + 1e-9
return v, round(time.monotonic() - t0, 2)
def embed(payload_json: str, secret: str) -> str:
# Reject BEFORE the GPU decorator: a wrong secret must cost zero quota.
if not API_KEY or secret != API_KEY:
return json.dumps({"error": "unauthorised"})
try:
payload = json.loads(payload_json)
logger.debug(" payload: {}", payload)
texts = payload["texts"]
logger.debug(" texts: {}", texts)
is_query = bool(payload.get("is_query", False))
except Exception as exc:
return json.dumps({"error": f"bad payload: {exc}"})
if not isinstance(texts, list) or not texts:
return json.dumps({"error": "texts must be a non-empty list"})
if len(texts) > MAX_TEXTS:
return json.dumps({"error": f"max {MAX_TEXTS} texts per call"})
texts = [str(t)[:20000] for t in texts]
v, gpu_s = _embed_gpu(texts, is_query)
return json.dumps({"dims": DIM, "vectors": v.tolist(), "gpu_s": gpu_s})
demo = gr.Interface(
fn=embed,
inputs=[
gr.Textbox(label="""payload_json (e.g.: {"texts": ["a", "b"]})"""),
gr.Textbox(label="API_KEY (hp 6)"),
],
outputs=gr.Textbox(label="result"),
title="embedding service",
description=(
"Private-use embedding endpoint (API_KEY). "
"Not a public demo β€” calls without API_KEY are rejected."
),
)
demo.queue(max_size=64).launch()