File size: 9,134 Bytes
2695555
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
"""openTARS: a lista da p谩gina "Mais modelos" (Configura莽玫es). S贸 dados e contas simples, sem Qt, para testar 脿 parte.

Nomes e tamanhos conferidos nas p谩ginas do ollama.com em 21/10/2026 (s贸 entra o que a p谩gina mostra com um tamanho 煤nico; gemma4 e
qwen3.5 ficaram de fora porque a p谩gina d谩 uma faixa de tamanhos). S贸 entram o Bonsai 2 oficial (Prism ML), os modelos de
imagem do namespace x/ (experimental, do Ollama) e a biblioteca principal. Vers玫es da comunidade ficam de fora de prop贸sito (origem desconhecida e algumas "sem filtro").
Se um tamanho mudar l谩, 茅 aqui que se corrige.
"""

# (id do grupo, ((tag no Ollama, nome mostrado, tamanho do download em GB, o que sabe fazer), ...))
GRUPOS = (
    # 25/10 (decis茫o do Enzo): os modelos do openTARS no topo, o Lite primeiro e destacado (茅 o que fica em segundo plano)
    ("opentars", (
        ("ProjectOpenTARS/Gargantua-2:lite", "Gargantua 2 Lite", 0.617, ("vision", "tools")),
        ("ProjectOpenTARS/Gargantua-2:latest", "Gargantua 2", 1.6, ("vision", "tools")),
        ("ProjectOpenTARS/Gargantua:code", "Gargantua Code", 1.1, ("tools",)),
    )),
    ("bonsai", (
        ("bonsai-2:2b", "Bonsai 2 路 2B", 0.911, ("vision", "tools", "thinking")),
        ("bonsai-2:4b", "Bonsai 2 路 4B", 1.6, ("vision", "tools", "thinking")),
        ("bonsai-2:9b", "Bonsai 2 路 9B", 3.2, ("vision", "tools", "thinking")),
        ("bonsai-2:27b", "Bonsai 2 路 27B", 8.3, ("vision", "tools", "thinking")),
    )),
    # A biblioteca oficial do Ollama s贸 tem estas duas fam铆lias de imagem (namespace x/, experimental); o que muda
    # entre as linhas 茅 o tamanho e a precis茫o dos pesos (fp4 茅 o padr茫o, fp8 e bf16 pesam mais).
    ("imagem", (
        ("x/flux2-klein:4b", "FLUX.2 klein 路 4B", 5.7, ("image",)),
        ("x/flux2-klein:4b-fp8", "FLUX.2 klein 路 4B fp8", 9.5, ("image",)),
        ("x/flux2-klein:4b-bf16", "FLUX.2 klein 路 4B bf16", 16.0, ("image",)),
        ("x/flux2-klein:9b", "FLUX.2 klein 路 9B", 12.0, ("image",)),
        ("x/flux2-klein:9b-fp8", "FLUX.2 klein 路 9B fp8", 20.0, ("image",)),
        ("x/flux2-klein:9b-bf16", "FLUX.2 klein 路 9B bf16", 35.0, ("image",)),
        ("x/z-image-turbo", "Z-Image Turbo 路 6B", 13.0, ("image",)),
        ("x/z-image-turbo:bf16", "Z-Image Turbo 路 6B bf16", 33.0, ("image",)),
    )),
    ("geral", (
        ("tinyllama:1.1b", "TinyLlama 路 1.1B", 0.638, ("text",)),
        ("qwen2.5:0.5b", "Qwen2.5 路 0.5B", 0.398, ("tools",)),
        ("qwen3:0.6b", "Qwen3 路 0.6B", 0.523, ("tools", "thinking")),
        ("gemma3:1b", "Gemma 3 路 1B", 0.806, ("text",)),
        ("qwen2.5:1.5b", "Qwen2.5 路 1.5B", 0.986, ("tools",)),
        ("deepseek-r1:1.5b", "DeepSeek-R1 路 1.5B", 1.1, ("tools", "thinking")),
        ("qwen3:1.7b", "Qwen3 路 1.7B", 1.4, ("tools", "thinking")),
        ("gemma2:2b", "Gemma 2 路 2B", 1.6, ("text",)),
        ("qwen2.5:3b", "Qwen2.5 路 3B", 1.9, ("tools",)),
        ("llama3.2:3b", "Llama 3.2 路 3B", 2.0, ("tools",)),
        ("phi3:3.8b", "Phi-3 路 3.8B", 2.2, ("text",)),
        ("qwen3:4b", "Qwen3 路 4B", 2.5, ("tools", "thinking")),
        ("mistral:7b", "Mistral 路 7B", 4.4, ("tools",)),
        ("qwen2.5:7b", "Qwen2.5 路 7B", 4.7, ("tools",)),
        ("deepseek-r1:7b", "DeepSeek-R1 路 7B", 4.7, ("tools", "thinking")),
        ("llama3.1:8b", "Llama 3.1 路 8B", 4.9, ("tools",)),
        ("qwen3:8b", "Qwen3 路 8B", 5.2, ("tools", "thinking")),
        ("deepseek-r1:8b", "DeepSeek-R1 路 8B", 5.2, ("tools", "thinking")),
        ("gemma2:9b", "Gemma 2 路 9B", 5.4, ("text",)),
        ("mistral-nemo:12b", "Mistral Nemo 路 12B", 7.1, ("tools",)),
        ("phi3:14b", "Phi-3 medium 路 14B", 7.9, ("text",)),
        ("qwen2.5:14b", "Qwen2.5 路 14B", 9.0, ("tools",)),
        ("deepseek-r1:14b", "DeepSeek-R1 路 14B", 9.0, ("tools", "thinking")),
        ("phi4:14b", "Phi-4 路 14B", 9.1, ("text",)),
        ("qwen3:14b", "Qwen3 路 14B", 9.3, ("tools", "thinking")),
    )),
    ("visao", (
        ("qwen3-vl:2b", "Qwen3-VL 路 2B", 1.9, ("vision", "tools", "thinking")),
        ("qwen3-vl:4b", "Qwen3-VL 路 4B", 3.3, ("vision", "tools", "thinking")),
        ("gemma3:4b", "Gemma 3 路 4B", 3.4, ("vision",)),
        ("llava:7b", "LLaVA 路 7B", 4.7, ("vision",)),
        ("qwen3-vl:8b", "Qwen3-VL 路 8B", 6.2, ("vision", "tools", "thinking")),
        ("llava:13b", "LLaVA 路 13B", 8.0, ("vision",)),
        ("gemma3:12b", "Gemma 3 路 12B", 8.2, ("vision",)),
    )),
    ("codigo", (
        ("qwen2.5-coder:1.5b", "Qwen2.5-Coder 路 1.5B", 0.986, ("tools",)),
        ("qwen2.5-coder:3b", "Qwen2.5-Coder 路 3B", 1.9, ("tools",)),
        ("qwen2.5-coder:7b", "Qwen2.5-Coder 路 7B", 4.7, ("tools",)),
        ("qwen2.5-coder:14b", "Qwen2.5-Coder 路 14B", 9.0, ("tools",)),
        ("qwen2.5-coder:32b", "Qwen2.5-Coder 路 32B", 20.0, ("tools",)),
        ("qwen3-coder:30b", "Qwen3-Coder 路 30B", 19.0, ("tools",)),
    )),
    # Pedem uma placa de v铆deo grande ou muita mem贸ria (a RTX 3050 de 6 GB do Enzo n茫o os roda bem).
    ("grandes", (
        ("gpt-oss:20b", "gpt-oss 路 20B", 14.0, ("tools", "thinking")),
        ("gemma2:27b", "Gemma 2 路 27B", 16.0, ("text",)),
        ("gemma3:27b", "Gemma 3 路 27B", 17.0, ("vision",)),
        ("qwen3:30b", "Qwen3 路 30B", 19.0, ("tools", "thinking")),
        ("qwen3:32b", "Qwen3 路 32B", 20.0, ("tools", "thinking")),
        ("qwen2.5:32b", "Qwen2.5 路 32B", 20.0, ("tools",)),
        ("deepseek-r1:32b", "DeepSeek-R1 路 32B", 20.0, ("tools", "thinking")),
        ("llava:34b", "LLaVA 路 34B", 20.0, ("vision",)),
    )),
)

CAPACIDADES = ("vision", "tools", "thinking", "image", "text")
DESTAQUE = "ProjectOpenTARS/Gargantua-2:lite"

# Etiquetas que o Ollama mostra com outro nome quando o modelo foi baixado sem a tag (o mesmo arquivo).
_APELIDOS = {
    "bonsai-2:9b": "bonsai-2:latest",
    "gemma3:4b": "gemma3:latest",
    "gemma2:9b": "gemma2:latest",
    "llama3.2:3b": "llama3.2:latest",
    "llama3.1:8b": "llama3.1:latest",
    "qwen3:8b": "qwen3:latest",
    "qwen3-vl:8b": "qwen3-vl:latest",
    "qwen3-coder:30b": "qwen3-coder:latest",
    "qwen2.5:7b": "qwen2.5:latest",
    "qwen2.5-coder:7b": "qwen2.5-coder:latest",
    "deepseek-r1:8b": "deepseek-r1:latest",
    "mistral:7b": "mistral:latest",
    "mistral-nemo:12b": "mistral-nemo:latest",
    "llava:7b": "llava:latest",
    "phi3:3.8b": "phi3:latest",
    "phi4:14b": "phi4:latest",
    "gpt-oss:20b": "gpt-oss:latest",
    "tinyllama:1.1b": "tinyllama:latest",
    "x/flux2-klein:4b": "x/flux2-klein:latest",
    "x/z-image-turbo": "x/z-image-turbo:latest",
}


def todas_as_tags():
    return [item[0] for _, itens in GRUPOS for item in itens]


def _norma(tag):
    tag = tag.strip().lower()
    return tag if ":" in tag else tag + ":latest"


def instalado(tag, instalados):
    """O modelo `tag` j谩 est谩 entre os `instalados` (lista de nomes do /api/tags)?"""
    nomes = {_norma(n) for n in instalados}
    return _norma(tag) in nomes or _norma(_APELIDOS.get(tag, tag)) in nomes


def tamanho_texto(gb):
    return f"{round(gb * 1000)} MB" if gb < 1 else f"{gb:.1f} GB"


def espaco_necessario(gb):
    """O download mais 10% e 1 GB de folga (o Ollama descompacta e guarda camadas por cima)."""
    return gb * 1.1 + 1.0


def cabe(gb, livre_gb):
    """Cabe no disco? Sem saber o espa莽o livre (None), deixa tentar: o Ollama avisa se faltar."""
    return livre_gb is None or livre_gb >= espaco_necessario(gb)


# 26/10 (pedido do Enzo): os modelos que combinam com o computador
GRUPOS_RECOMENDADOS = ("geral", "visao", "codigo")      # um recomendado por grupo


def hardware(vram_gb=None, ram_gb=None):
    """{'vram_gb': placa dedicada (0 se n茫o h谩), 'ram_gb': mem贸ria}. Sem argumentos, l锚 o computador."""
    if vram_gb is None:
        try:
            import tars_primeiro_uso as pu
            vram_gb = pu._vram_mb() / 1024
        except Exception:  # noqa: BLE001
            vram_gb = 0.0
    if ram_gb is None:
        try:
            import psutil
            ram_gb = psutil.virtual_memory().total / 2 ** 30
        except Exception:  # noqa: BLE001
            ram_gb = 8.0
    return {"vram_gb": vram_gb if vram_gb >= 2.0 else 0.0, "ram_gb": ram_gb}      # menos de 2 GB 茅 v铆deo integrado


def avaliar(gb, hw):
    """'otimo' (cabe na placa com folga, ou 茅 pequeno o bastante para o processador), 'ok' (roda, mais devagar) ou 'pesado'.
    O tamanho do download aproxima a mem贸ria usada (j谩 vem quantizado)."""
    if hw["vram_gb"]:
        if gb <= hw["vram_gb"] * 0.8:
            return "otimo"
    elif gb <= min(hw["ram_gb"] * 0.3, 5.0):
        return "otimo"
    return "ok" if gb <= hw["ram_gb"] * 0.5 else "pesado"


def recomendados(hw):
    """Em cada grupo, o maior modelo 'otimo', preferindo os que usam ferramentas e, no empate, os sem racioc铆nio longo."""
    achados = []
    for grupo, itens in GRUPOS:
        cabem = [i for i in itens if grupo in GRUPOS_RECOMENDADOS and avaliar(i[2], hw) == "otimo"]
        if cabem:
            achados.append(max(cabem, key=lambda i: ("tools" in i[3], i[2], "thinking" not in i[3]))[0])
    return achados