File size: 3,505 Bytes
64e1ec4
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
# -*- coding: utf-8 -*-
"""QUAL MÓDULO ATENDE CADA TIPO — num lugar só, para o worker e o Space.

POR QUE ISTO EXISTE. Havia duas tabelas com a mesma decisão: o `_MODULOS` do
`gerenciador.py` (worker CUDA) e o `_MODULOS` do `nucleo.py` (Space ZeroGPU).
Enquanto cada tipo tinha um módulo só, elas eram iguais por sorte. No dia em
que a imagem passou a ter DOIS módulos possíveis — Z-Image e Qwen-Image,
escolhidos pelo `IMAGE_MODEL` —, duas tabelas viraram duas respostas para a
mesma pergunta, e a que fica para trás é sempre a que ninguém olha. É o
defeito mais caro desta casa, e ele já cobrou a peneira do navegador, os dois
renderizadores de resumo do estudo e as duas regras de imagem no chat.

A REGRA DE SEMPRE VALE AQUI: a ORDEM é nossa, quem diz o que EXISTE é a
conta — ou, neste caso, a variável. O que está escrito abaixo são MARCAS DE
FAMÍLIA (`qwen-image`, `z-image`), nunca um id completo: um `Qwen-Image-2512`
publicado amanhã cai sozinho no módulo certo, sem ninguém tocar no arquivo.
Lista de ids escrita à mão já mordeu este projeto cinco vezes.

E O DESCONHECIDO TEM DONO. Um id que não bate com marca nenhuma vai para o
módulo PADRÃO do tipo, em vez de virar `None`: devolver nada aqui apagaria a
geração inteira por causa de um nome que ninguém previu — e o pior desfecho de
um palpite é uma imagem com a calibragem errada, que ainda é uma imagem.
"""
from __future__ import annotations

import os
from typing import Dict, List, Tuple

# Marca da família -> nome do módulo dentro de `modelos/`. A ordem importa:
# a primeira marca que aparecer no id ganha.
_FAMILIAS_IMAGEM: List[Tuple[str, str]] = [
    ("qwen-image", "qwen_image"),
    ("z-image",    "z_image"),
]
_FAMILIAS_VIDEO: List[Tuple[str, str]] = [
    ("ltx",  "ltx"),
    ("wan",  "wan22"),
]

# O padrão de cada tipo quando o id não bate com marca nenhuma.
_PADRAO = {"image": "qwen_image", "speech": "qwen3_tts", "voice": "qwen3_tts",
           "music": "ace_step", "video": "wan22"}

_VARIAVEL = {"image": "IMAGE_MODEL", "speech": "TTS_MODEL",
             "voice": "TTS_MODEL", "music": "MUSIC_MODEL",
             "video": "VIDEO_MODEL"}

_POR_TIPO: Dict[str, List[Tuple[str, str]]] = {
    "image": _FAMILIAS_IMAGEM,
    "video": _FAMILIAS_VIDEO,
}


def nome_do_modulo(tipo: str, modelo: str = "") -> str:
    """O nome do módulo que atende este tipo, pela FAMÍLIA do id configurado.

    `modelo` vazio faz a função ler a variável de ambiente do tipo — é como os
    dois consumidores a chamam. Passar o id à mão existe para o teste, que
    precisa medir a escolha sem mexer no ambiente do processo.
    """
    tipo = (tipo or "").strip().lower()
    id_modelo = (modelo or os.getenv(_VARIAVEL.get(tipo, ""), "")).strip().lower()
    for marca, modulo in _POR_TIPO.get(tipo, []):
        if marca in id_modelo:
            return modulo
    return _PADRAO.get(tipo, "")


def modulo_de(tipo: str):
    """O módulo em si, importado na hora.

    O import é TARDIO de propósito, e é a regra do `modelos/__init__.py`: o
    worker sobe e responde `/health` sem torch instalado, e o peso de um
    modelo só entra quando um job pede. Importar os dois módulos de imagem no
    topo obrigaria o processo a resolver as dependências dos DOIS para atender
    um pedido de voz.
    """
    nome = nome_do_modulo(tipo)
    if not nome:
        return None
    from importlib import import_module
    return import_module(f".{nome}", package=__package__)