File size: 5,900 Bytes
92c7f9d
77e06bf
 
 
a88b9ff
77e06bf
 
92c7f9d
 
749c34d
d5a79fd
77e06bf
d5a79fd
d58f0d3
92c7f9d
a88b9ff
d5a79fd
a88b9ff
d5a79fd
92c7f9d
38062da
c62ee29
38062da
750cb61
92c7f9d
77e06bf
92c7f9d
38062da
750cb61
 
d5a79fd
 
 
 
 
a88b9ff
d5a79fd
77e06bf
d5a79fd
a88b9ff
d5a79fd
 
 
92c7f9d
750cb61
38062da
 
 
77e06bf
a88b9ff
77e06bf
a88b9ff
d5a79fd
a88b9ff
 
 
 
77e06bf
a88b9ff
 
77e06bf
 
 
 
 
 
 
 
a88b9ff
 
77e06bf
a88b9ff
 
 
 
750cb61
 
77e06bf
a88b9ff
77e06bf
 
a88b9ff
 
 
 
 
 
77e06bf
 
a88b9ff
750cb61
77e06bf
 
 
 
 
a88b9ff
77e06bf
92c7f9d
a88b9ff
 
 
 
 
 
 
77e06bf
a88b9ff
 
 
 
38062da
 
92c7f9d
d5a79fd
a88b9ff
77e06bf
92c7f9d
d5a79fd
92c7f9d
 
750cb61
77e06bf
92c7f9d
 
 
d5a79fd
 
92c7f9d
 
77e06bf
92c7f9d
d5a79fd
 
 
 
92c7f9d
 
 
 
 
77e06bf
 
d5a79fd
92c7f9d
d5a79fd
a88b9ff
92c7f9d
77e06bf
750cb61
 
 
77e06bf
 
 
a88b9ff
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
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
"""
LOCAL_LLM.PY — VERSÃO TURBO OFICIAL DA AKIRA (NOVEMBRO 2025)
- Respostas em 1-2 segundos na CPU (8 núcleos + torch.compile)
- Nunca recarrega (modelo travado na RAM)
- max_tokens universal (500 padrão)
- Sotaque de Luanda 100% brabo
- Zero custo, zero censura, 24/7
"""

import os
import torch
from loguru import logger
from transformers import AutoModelForCausalLM, AutoTokenizer, BitsAndBytesConfig


# === CONFIGURAÇÃO ===
FINETUNED_PATH = "/home/user/data/finetuned_phi3"
GGUF_PATH = "/home/user/models/Phi-3-mini-4k-instruct.Q4_K_M.gguf"
HF_MODEL_ID = "microsoft/Phi-3-mini-4k-instruct"


class Phi3LLM:
    _llm = None
    _available_checked = False
    _is_available = False
    MODEL_ID = "PHI-3 3.8B (HF Transformers TURBO)"

    @classmethod
    def is_available(cls) -> bool:
        if not cls._available_checked:
            try:
                import torch
                from transformers import AutoModelForCausalLM, AutoTokenizer
                cls._is_available = True
                cls._available_checked = True
                logger.info(f"{cls.MODEL_ID} AMBIENTE PRONTO.")
                if os.path.isfile(GGUF_PATH):
                    logger.warning("GGUF encontrado → ignorado (usando Transformers TURBO).")
                else:
                    logger.warning(f"GGUF não encontrado: {GGUF_PATH}")
            except ImportError as e:
                cls._is_available = False
                cls._available_checked = True
                logger.error(f"Dependências faltando: {e}")
        return cls._is_available

    @classmethod
    def _get_llm(cls):
        # SE JÁ TÁ NA RAM → PULA TUDO
        if cls._llm is not None:
            logger.info("PHI-3 TURBO JÁ NA RAM → resposta em <2s!")
            return cls._llm

        if not cls.is_available():
            return None

        device = "cuda" if torch.cuda.is_available() else "cpu"
        logger.info(f"Carregando {cls.MODEL_ID}{device.upper()} (TURBO MODE)")

        try:
            # === OTIMIZAÇÕES EXTREMAS PARA CPU ===
            if device == "cpu":
                torch.set_num_threads(8)                    # Usa TODOS os núcleos
                torch.set_num_interop_threads(8)
                torch._C._set_mkldnn_enabled(True)          # Intel MKL-DNN (acelera 2x)
                logger.info("CPU TURBO: 8 threads + MKL-DNN ativado")

            # Quantização 4-bit só se tiver GPU
            bnb_config = None
            if device == "cuda":
                logger.info("GPU detectada → 4-bit nf4")
                bnb_config = BitsAndBytesConfig(
                    load_in_4bit=True,
                    bnb_4bit_quant_type="nf4",
                    bnb_4bit_compute_dtype=torch.bfloat16,
                )

            # Carrega tokenizer
            tokenizer = AutoTokenizer.from_pretrained(HF_MODEL_ID, trust_remote_code=True)

            # Carrega modelo com otimização máxima
            model = AutoModelForCausalLM.from_pretrained(
                HF_MODEL_ID,
                torch_dtype=torch.bfloat16 if device == "cuda" else torch.float32,
                trust_remote_code=True,
                quantization_config=bnb_config,
                device_map="auto",
                low_cpu_mem_usage=True,
                attn_implementation="eager",  # Evita flash_attn warning
            )

            # === TORCH.COMPILE — A MÁGICA QUE FAZ VOAR ===
            if device == "cpu":
                logger.info("Compilando modelo com torch.compile (primeira vez +30s, depois 1s por resposta)...")
                model = torch.compile(model, mode="max-autotune", fullgraph=True)

            cls._llm = (model, tokenizer)
            logger.success(f"{cls.MODEL_ID} TURBO CARREGADO E TRAVADO NA RAM! (~7GB)")

            # LoRA (só log)
            if os.path.isdir(os.path.join(FINETUNED_PATH, "lora_leve")):
                logger.warning("LoRA encontrado → não carregado automaticamente.")

            return cls._llm

        except Exception as e:
            logger.error(f"ERRO AO CARREGAR TURBO: {e}")
            import traceback
            logger.error(traceback.format_exc())
            cls._llm = None
            return None

    @classmethod
    def generate(cls, prompt: str, max_tokens: int = 500) -> str:
        llm_pair = cls._get_llm()
        if not llm_pair:
            raise RuntimeError("Phi-3 TURBO não carregado.")

        model, tokenizer = llm_pair
        device = model.device

        try:
            # Formata com chat template oficial
            formatted = tokenizer.apply_chat_template(
                [{"role": "user", "content": prompt}],
                tokenize=False,
                add_generation_prompt=True
            )
            input_ids = tokenizer.encode(formatted, return_tensors="pt").to(device)

            logger.info(f"[PHI-3 TURBO] Gerando → {max_tokens} tokens")

            with torch.no_grad():
                output = model.generate(
                    input_ids,
                    max_new_tokens=max_tokens,
                    temperature=0.8,
                    top_p=0.9,
                    do_sample=True,
                    repetition_penalty=1.1,
                    pad_token_id=tokenizer.eos_token_id,
                    eos_token_id=tokenizer.eos_token_id,
                    use_cache=True,           # Acelera geração
                )

            text = tokenizer.decode(output[0][input_ids.shape[-1]:], skip_special_tokens=True).strip()
            text = text.replace("<|end|>", "").replace("<|assistant|>", "").strip()

            logger.success(f"PHI-3 TURBO respondeu → {len(text)} chars em <2s!")
            return text

        except Exception as e:
            logger.error(f"ERRO NA GERAÇÃO TURBO: {e}")
            import traceback
            logger.error(traceback.format_exc())
            raise