File size: 3,647 Bytes
393654c
773e1cf
 
b807bd8
393654c
 
 
a5de004
fe2f396
df7017f
393654c
 
 
 
2906245
 
 
 
14a42df
 
 
 
2906245
 
 
14a42df
2906245
b807bd8
2906245
 
 
 
12a2254
2906245
 
 
b807bd8
01a7be6
2906245
01a7be6
2906245
6bb1696
 
 
 
14a42df
 
019823b
e1bcd6b
773e1cf
14a42df
fe2f396
 
393654c
773e1cf
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
393654c
773e1cf
 
 
 
 
393654c
773e1cf
393654c
773e1cf
 
393654c
b807bd8
 
773e1cf
393654c
e1bcd6b
 
df7017f
773e1cf
 
b807bd8
 
 
393654c
b807bd8
773e1cf
b807bd8
 
 
 
773e1cf
b807bd8
 
 
 
773e1cf
393654c
773e1cf
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
import os
import gc
import glob
import time
import logging
from typing import List, Union, Optional, Literal
from fastapi import FastAPI, Depends, HTTPException, Header, status
from fastapi.responses import JSONResponse, FileResponse
from pydantic import BaseModel, Field
from llama_cpp import Llama

logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)

# 🔒 SEGURANÇA: Falha imediata se API_KEY não estiver configurada
API_KEY = os.getenv("API_KEY")
if not API_KEY:
    raise RuntimeError(
        "❌ SECURITY ERROR: API_KEY environment variable is not set!\n"
        "Please configure the API_KEY secret in your Hugging Face Space:\n"
        "Settings → Variables and Secrets → Add secret\n"
        "Name: API_KEY | Value: [your-secure-key]"
    )

MODEL_PATH = glob.glob("/app/model/*.gguf")[0]
logger.info(f"Carregando modelo: {MODEL_PATH}")

load_start = time.time()
llm = Llama(
    model_path=MODEL_PATH,
    n_ctx=512,
    n_threads=2,
    n_batch=512,
    embedding=True,
    verbose=False
)
load_time = time.time() - load_start
logger.info(f"🚀 Model loaded in {load_time:.2f}s")

app = FastAPI(title="Embedding Engine (GGUF Q8_0)")

@app.get("/", include_in_schema=False)
async def root():
    return FileResponse("index.html")

class EmbeddingRequest(BaseModel):
    input: Union[str, List[str]]
    model: str = "LiquidAI/LFM2.5-Embedding-350M"
    input_type: Optional[Literal["query", "document"]] = Field(
        default="document",
        alias="inputType"
    )
    model_config = {"populate_by_name": True}

class EmbeddingObject(BaseModel):
    object: str = "embedding"
    embedding: List[float]
    index: int

class EmbeddingResponse(BaseModel):
    object: str = "list"
    data: List[EmbeddingObject]
    model: str
    usage: dict

def verify_api_key(authorization: str = Header(None)):
    if not authorization or not authorization.startswith("Bearer "):
        raise HTTPException(
            status_code=status.HTTP_401_UNAUTHORIZED,
            detail="Invalid or missing Authorization header",
            headers={"WWW-Authenticate": "Bearer"},
        )
    token = authorization.replace("Bearer ", "")
    if token != API_KEY:
        raise HTTPException(
            status_code=status.HTTP_401_UNAUTHORIZED,
            detail="Invalid API key",
            headers={"WWW-Authenticate": "Bearer"},
        )

@app.post("/v1/embeddings", response_model=EmbeddingResponse)
async def create_embeddings(
    request: EmbeddingRequest,
    _: str = Depends(verify_api_key)
):
    req_start = time.time()
    
    inputs = request.input if isinstance(request.input, list) else [request.input]
    
    if request.input_type == "query":
        inputs = [f"query: {text}" for text in inputs]
    else:
        inputs = [f"document: {text}" for text in inputs]
    
    prep_time = time.time() - req_start
    logger.info(f"📝 Prep time: {prep_time:.3f}s | Input count: {len(inputs)} | First input length: {len(inputs[0])} chars")
    
    try:
        infer_start = time.time()
        response = llm.create_embedding(input=inputs)
        infer_time = time.time() - infer_start
        
        logger.info(f"🧠 Inference time: {infer_time:.3f}s")
        
        response["model"] = request.model
        
        total_time = time.time() - req_start
        logger.info(f"✅ Total request time: {total_time:.3f}s")
        
        return JSONResponse(content=response)
    except Exception as e:
        logger.error(f"Embedding error: {str(e)}")
        raise HTTPException(status_code=500, detail="Embedding generation failed")
    finally:
        gc.collect()