File size: 4,563 Bytes
d589fb4
86a1e30
d589fb4
5720afb
d589fb4
 
 
86a1e30
 
28d21d5
 
 
 
 
 
 
 
 
 
 
 
 
 
d589fb4
40d1c74
86a1e30
 
 
 
d589fb4
 
40d1c74
d589fb4
 
 
 
 
 
 
 
 
28d21d5
d589fb4
 
 
 
 
40d1c74
d589fb4
 
 
 
 
 
 
 
 
 
 
40d1c74
 
d589fb4
40d1c74
d589fb4
 
 
 
 
 
 
 
 
 
 
 
 
28d21d5
 
d589fb4
28d21d5
 
40d1c74
28d21d5
 
 
d589fb4
 
 
 
 
 
 
 
 
 
28d21d5
d589fb4
 
28d21d5
86a1e30
d589fb4
 
40d1c74
 
 
 
 
d589fb4
 
 
 
28d21d5
 
40d1c74
 
 
 
 
 
 
 
 
28d21d5
86a1e30
 
40d1c74
86a1e30
 
40d1c74
 
37c4030
28d21d5
37c4030
 
 
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
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
import os
import json
from typing import List, Optional
from fastapi import FastAPI
from fastapi.responses import StreamingResponse
from pydantic import BaseModel
from huggingface_hub import hf_hub_download
import gradio as gr
import uvicorn
import spaces
import subprocess
import sys

# --- Instalação do llama-cpp-python via wheel (compatível com o ambiente do HF) ---
# Este comando instala a biblioteca a partir de um pacote binário pré-compilado.
# É uma alternativa robusta para evitar erros de compilação no ambiente do Space.
subprocess.check_call([
    sys.executable, "-m", "pip", "install", "--no-cache-dir",
    "https://huggingface.co/Luigi/llama-cpp-python-wheels-hf-spaces-free-cpu/resolve/main/llama_cpp_python-0.3.22-cp310-cp310-linux_x86_64.whl"
])

# Agora que o wheel foi instalado, podemos importar a biblioteca com segurança.
from llama_cpp import Llama

# --- Modelo ---
MODEL_REPO = "bartowski/Dolphin3.0-Llama3.1-8B-GGUF"
MODEL_FILE = "Dolphin3.0-Llama3.1-8B-Q4_K_M.gguf"
N_CTX = 4096
N_THREADS = 4

llm = None

def load_model():
    global llm
    if llm is None:
        print(f"Baixando/carregando modelo: {MODEL_REPO}/{MODEL_FILE}")
        model_path = hf_hub_download(repo_id=MODEL_REPO, filename=MODEL_FILE)
        llm = Llama(
            model_path=model_path,
            n_ctx=N_CTX,
            n_threads=N_THREADS,
            n_gpu_layers=-1,  # Tenta usar a GPU (se disponível)
            verbose=False,
        )
        print("Modelo carregado com sucesso.")
    return llm

# --- Schemas OpenAI ---
class ChatMessage(BaseModel):
    role: str
    content: str

class ChatCompletionRequest(BaseModel):
    model: str = "dolphin-llama3.1-8b"
    messages: List[ChatMessage]
    temperature: float = 0.7
    max_tokens: Optional[int] = 512
    stream: bool = False

# --- FastAPI ---
api = FastAPI()

@api.get("/v1/models")
async def list_models():
    return {
        "object": "list",
        "data": [
            {
                "id": "dolphin-llama3.1-8b",
                "object": "model",
                "created": 1700000000,
                "owned_by": "local",
            }
        ],
    }

@spaces.GPU  # <-- Esta linha garante que a GPU seja alocada para a inferência
def generate_response(prompt: str, max_tokens: int, temperature: float):
    model = load_model()
    output = model(prompt, max_tokens=max_tokens, temperature=temperature, stream=False)
    return output["choices"][0]["text"]

@api.post("/v1/chat/completions")
async def chat_completions(req: ChatCompletionRequest):
    # Formata o prompt para o modelo Dolphin (formato ChatML)
    prompt = ""
    for msg in req.messages:
        if msg.role == "system":
            prompt += f"<|im_start|>system\n{msg.content}<|im_end|>\n"
        elif msg.role == "user":
            prompt += f"<|im_start|>user\n{msg.content}<|im_end|>\n"
        elif msg.role == "assistant":
            prompt += f"<|im_start|>assistant\n{msg.content}<|im_end|>\n"
    prompt += "<|im_start|>assistant\n"

    # --- Modo de streaming ---
    if req.stream:
        async def stream_generator():
            model = load_model()
            output = model(prompt, max_tokens=req.max_tokens, temperature=req.temperature, stream=True)
            for chunk in output:
                token = chunk["choices"][0]["text"]
                data = {
                    "id": "chatcmpl-local",
                    "object": "chat.completion.chunk",
                    "choices": [{"delta": {"content": token}, "index": 0, "finish_reason": None}],
                }
                yield f"data: {json.dumps(data)}\n\n"
            yield "data: [DONE]\n\n"
        return StreamingResponse(stream_generator(), media_type="text/event-stream")

    # --- Modo normal ---
    text = generate_response(prompt, req.max_tokens, req.temperature)
    return {
        "id": "chatcmpl-local",
        "object": "chat.completion",
        "created": 1700000000,
        "model": req.model,
        "choices": [{"index": 0, "message": {"role": "assistant", "content": text}, "finish_reason": "stop"}],
        "usage": {"prompt_tokens": 0, "completion_tokens": 0, "total_tokens": 0},
    }

# --- Gradio (Interface web + API) ---
def dummy(x):
    return x

demo = gr.Interface(fn=dummy, inputs="text", outputs="text")

# --- Monta o Gradio dentro do FastAPI ---
app = gr.mount_gradio_app(api, demo, path="/ui")

# --- Rodar (o Hugging Face inicia via `python app.py`) ---
if __name__ == "__main__":
    load_model()
    uvicorn.run(app, host="0.0.0.0", port=7860)