memoryweave/deploy/embed-server.py

85 lines
2.4 KiB
Python

#!/usr/bin/env python3
"""轻量 bge-m3 embedding server — OpenAI 兼容 /v1/embeddings 接口"""
import os, sys, json, time
from typing import List
# 环境变量
HOST = os.environ.get("EMBED_HOST", "0.0.0.0")
PORT = int(os.environ.get("EMBED_PORT", "8000"))
MODEL = os.environ.get("EMBED_MODEL", "BAAI/bge-m3")
DEVICE = os.environ.get("EMBED_DEVICE", "cpu")
from fastapi import FastAPI, Request
from fastapi.responses import JSONResponse
from pydantic import BaseModel, Field
import uvicorn
app = FastAPI(title="bge-m3 Embedding Server", version="1.0")
class EmbedRequest(BaseModel):
input: str | List[str]
model: str = MODEL
class EmbeddingObject(BaseModel):
object: str = "embedding"
index: int
embedding: List[float]
class UsageInfo(BaseModel):
prompt_tokens: int
total_tokens: int
class EmbedResponse(BaseModel):
object: str = "list"
data: List[EmbeddingObject]
model: str = MODEL
usage: UsageInfo
# 延迟加载
model_pipe = None
def load_model():
global model_pipe
if model_pipe is not None:
return
print(f"[embed] Loading {MODEL} on {DEVICE}...", flush=True)
t0 = time.time()
from sentence_transformers import SentenceTransformer
model_pipe = SentenceTransformer(MODEL, device=DEVICE)
elapsed = time.time() - t0
print(f"[embed] Loaded in {elapsed:.1f}s. Dimension: {model_pipe.get_sentence_embedding_dimension()}", flush=True)
@app.on_event("startup")
async def startup():
load_model()
@app.get("/health")
async def health():
return {"status": "ok", "model": MODEL, "device": DEVICE,
"loaded": model_pipe is not None}
@app.post("/v1/embeddings")
async def embed(req: EmbedRequest):
t0 = time.time()
texts = req.input if isinstance(req.input, list) else [req.input]
# bge-m3 需要加 prefix
prefixed = [f"为这个句子生成表示以用于检索相关文章:{t}" for t in texts]
vecs = model_pipe.encode(prefixed, normalize_embeddings=True,
show_progress_bar=False)
vecs = vecs.tolist()
total_tokens = sum(len(t) for t in texts)
elapsed = time.time() - t0
data = [
EmbeddingObject(index=i, embedding=vecs[i])
for i in range(len(vecs))
]
return EmbedResponse(data=data, usage=UsageInfo(prompt_tokens=total_tokens, total_tokens=total_tokens))
if __name__ == "__main__":
load_model()
uvicorn.run(app, host=HOST, port=PORT, log_level="info")