Repository navigation
[P3][packaging][detection][seed] DL para quem usa Docker: pacote /extras montado a partir do wheelhouse próprio (tokenizers e safetensors cp314t, torch CPU) #2024
Description
U: U3 — sem pressão de prazo.
G: G2 — o README anuncia "ML and optional DL", mas a imagem canônica publicada não inclui o DL; quem usa Docker fica sem ele, em silêncio.
Depende de: #1451 (opção de config para modelo local e revisão, identidade no recibo, falha alta), #1452 (degraded-mode). Sem código antes do PLAN e da decisão do operador.
Decisões do operador (30/set):
- as dependências do DL são criadas no wheelhouse próprio;
- musl e no-AVX: DL limitado e documentado; a limitação é aceita;
- qualquer sidecar tem licença própria e zero trust bidirecional.
Correções (30/set), erros meus: (a) a primeira versão dizia que
torcheonnxruntimenão têm wheel cp314t: têm; eu ignorei a tag de ABI. (b) a segunda versão propunha "em processo na imagem × sidecar" sem ter lidodocs/DOCKER_SETUP.md: a rota do Docker já está documentada (/extras). Esta versão parte dela.
Contexto (verificado por gh api, PyPI e índice CPU do PyTorch, 30/set)
- O ML já está na imagem (
scikit-learn==1.9.1,numpy,scipy,joblibcom hash norequirements.txt). Não executei a imagem. - A imagem é CPython 3.14t (cp314t) em runtime distroless (ADR-0091: uma imagem só, ~309 MB).
docs/DOCKER_SETUP.mddiz que ela é "lean on purpose": "no fat image, no image matrix, noFROMour image + rebuild", sem shell nem pip. O extradlestá na coluna "via runtime mount". - O mecanismo já existe (
/extras): o cliente monta um pacote de wheels ABI-compatível,-v extras-pack:/extras:ro(uid 65532), comPYTHONPATH=/extras:/app.python main.py --check-extrasmostra cada extra por origem (imagem ×/extras), e um pacote com ABI errada falha alto na partida. - Cadeia do DL contra o cp314t:
| Pacote | Situação |
|---|---|
torch 2.14.0 |
cp314t: sim. PyPI (Linux x86_64 555 MB) e índice CPU do PyTorch (2.14.0+cpu, 196 MB, medido por HEAD, sem baixar). O wheel do PyPI para Linux puxa dependências CUDA; o do índice CPU não |
onnxruntime 1.30.0 |
cp314t: sim, Linux x86_64 (24 MB) |
tokenizers 0.23.2 |
só abi3, sem cp314t |
safetensors 0.8.0 |
só abi3, sem cp314t |
sentence-transformers 6.1.0 |
puro; exige torch>=2.2 (o extra onnx não remove o torch) |
transformers, huggingface-hub |
puros |
hf-xet, numpy, scipy, scikit-learn, regex, pillow, pyyaml |
cp314t: sim |
Wheels abi3 não carregam em build free-threaded (regra do CPython; confirmar com pip install --dry-run num 3.14t). O que falta para pip install --target ./extras-pack 'data-boar[dl]' funcionar num host cp314t são tokenizers e safetensors em cp314t; sem eles o pip cai em build de Rust na máquina do cliente.
- Invariante já escrito no repo:
PLAN_PACKAGING_EXTRAS.md: "degrade LOUD and FN-first, never silent"; para o[dl], "never omit embeddings in silence". O código atual descarta o erro (_ = str(model_err),core/dl_backend.py). data-boarno PyPI (1.7.4.post12,requires-python >=3.12) já tem o extradl, então fora do Dockerpipx install "data-boar[dl]"funciona no host (não executei).
Proposta (síntese do Claude, a confirmar)
- Rota principal do Docker: o pacote
/extrasdodl, sem mexer na imagem. O pacote é construído a partir do wheelhouse, offline:pip install --target ./extras-pack --no-index --find-links <wheelhouse> ....- Cuidado de reprodutibilidade:
/extrasvem antes de/appnoPYTHONPATH, então o que estiver no pacote sobrepõe o que está na imagem. O pacote deve conter só o delta (torch, sentence-transformers, tokenizers, safetensors e o que faltar), construído com um arquivo de constraints igual aorequirements.txtda imagem, para não trocarnumpy/scikit-learnpinados. Conferir por origem no--check-extras. (Raciocínio pela mecânica doPYTHONPATH; não executei.)
- Cuidado de reprodutibilidade:
- Wheelhouse próprio (mesmo canal hospedado em
data-boar-site, comSHA256SUMSe índicesimple/): construirtokenizersesafetensorspara cp314t (Rust, maturin; commit fixado por SHA, sem rede, código lido) e espelhar otorchCPU (+cpu) cp314t com hash conferido. Não compilar o torch do zero. - Spike com go/no-go num Python 3.14t:
import sentence_transformers,encode()de um lote fixo com o GIL ligado e desligado, comparar os vetores com o caminho atual, medir tamanho do pacote e latência. Nada disso foi executado. - Camadas de execução do encoder (recomendado + fallback, sem coroado):
- v1, recomendado:
torchCPU +sentence-transformersno pacote/extras(paridade exata com o caminho atual; tamanho do pacote não medido); sentence-transformerscombackend=onnx: sem ganho de tamanho, o torch continua obrigatório;- ONNX Runtime direto +
tokenizers(ORT 24 MB): pacote enxuto, mas exige exportar o MiniLM para ONNX, escrever o pooling e um teste de paridade (fp32 deve ficar muito próximo; int8 muda os vetores); - Latent Lynx (Rust, candle): hoje é scaffold. O embedder padrão é um mock (SHA-256 → 384 floats) que não é o MiniLM; o candle real fica atrás de feature e exige
--model-dir(sem download silencioso); não há modo "só vetores". Candidato de longo prazo, com retreino da cabeça e teste de paridade.
- v1, recomendado:
- Sidecar só se o spike pedir (torch cp314t com GIL desligado, ou tamanho). Se adotado, vale a regra do operador: licença própria e zero trust bidirecional:
- o sidecar só processa payload depois de validar um grant assinado do Boar (escopo, capacidade, expiração), como o
handshake.pydo Stoat já faz do lado dele ("no attestation, no transform — fail closed"); - o Boar valida o sidecar antes de enviar o primeiro byte: produto e licença, versão, identidade do encoder (nome, revisão, sha256), e recusa o encoder mock;
- nenhum lado confia no dado do outro: o Boar valida dimensão (384) e valores finitos; o sidecar valida tamanho e taxa dos pedidos e não loga payload;
- isto já existe no Boar (correção 30/set, erro meu anterior): o SDK bidirecional de atestação mútua — feat(sdk): #865 bidirecional/zero-trust — wiring do contrato simétrico + boar_fast_filter como 1º plugin full-fledged (dogfood, 1.8.x) #1116 (CLOSED), ADR-0087,
core/sdk/mutual_attestation.py(MeshRolehost/guest,trust_is_acceptable_for_guest, challenge/response Ed25519, Safe-Hold em peer adulterado) — já faz os dois sentidos ("host contém plugin ruim ↔ plugin recusa host adulterado"). O sidecar de DL reusa esse contrato (PLAN_SDK_BIDIRECTIONAL.md,PLAN_PLUGIN_SDK_CONTRACT.md/epic(sdk): formalizar Boar Plugin SDK — contrato agnóstico versionado + fronteira L1/L2/L3 + fail-graceful (plugin = superfície do sidecar) #865), não inventa handshake novo. O que falta é só o item específico do encoder: o Boar recusar um sidecar que se identifique comomocke exigir a identidade do modelo (nome, revisão, sha256) no payload de atestação.
- o sidecar só processa payload depois de validar um grant assinado do Boar (escopo, capacidade, expiração), como o
- musl e no-AVX: DL limitado e documentado (decisão do operador): degrada em voz alta para regex + ML, como o
PLAN_PACKAGING_EXTRASjá manda. O ML continua nessas plataformas pelo wheelhouse x86-64-v1. - Air-gap: três artefatos verificados por hash no destino (courier em desenho no ecossistema): a imagem salva (
docker save→ tar com sha256; conferir se o digest do manifesto se preserva nodocker load, senão registrar o digest nas notas do release), o pacote/extrase os pesos do modelo. - Supply chain: builds de Rust/PyO3 de terceiros e espelho de binário do PyTorch com pin por hash, SBOM e proveniência (security/release: implement generate-sbom/check-sbom/emit-provenance reference wrappers (split from #1950) #1966) e licenças conferidas. O
safetensors(formato sem execução de código) é o argumento de segurança a favor.
Não fazer
Segunda tag da imagem do Boar; imagem "fat" com DL; baixar pesos em runtime; compilar o torch do zero; qualquer LLM no caminho crítico.
Limites e riscos (nada disto foi medido)
Tamanho do pacote; latência e throughput por lote; paridade entre CPUs (float); comportamento do torch cp314t com o GIL desligado (tier pago, ADR-0091); piso de CPU x86-64-v2+ da imagem; licença do modelo a conferir antes de redistribuir; formato dos pesos (preferir safetensors, conferir).
Critérios de aceite
- Criar
docs/plans/PLAN_DL_DOCKER_AND_WHEELHOUSE.mdcom<!-- plans-hub-summary: ... -->e ligá-lo dePLAN_WHEELHOUSE_DISTRIBUTION.md. - Executar
python scripts/plans_hub_sync.py --write. - Adicionar entry em
docs/plans/PLANS_TODO.md. - Spike em 3.14t registrado (import,
encode(), GIL ligado e desligado, paridade dos vetores, tamanho, latência), com go/no-go explícito. - Receita do wheelhouse para
tokenizersesafetensorscp314t, comSHA256SUMS. - Receita do pacote
/extrasdodl(só o delta, com constraints da imagem), documentada emdocs/DOCKER_SETUP.md. -
--check-extras([P1][1.8.x][packaging][ci] EXTRAS_MANIFEST.json + --check-extras + guarda no docker-image-smoke.sh — impedir a divergência artefato × pyproject de voltar #1401) reporta o DL por origem (/extras) e distingue "ausente" de "presente sem pesos". - Teste: sem DL utilizável,
dl: UNAVAILABLEno recibo e no log, e o resultado é idêntico ao de hoje. - Limitação de musl e no-AVX documentada em
docs/TROUBLESHOOTING.md. - README sem prometer DL para a imagem padrão;
docs/CLAIMS.ymlcom o claim "ML/DL" por perfil.
Fila e PR
Uma issue; no máximo 1 PR (plano, receita do wheelhouse e do pacote, spike); nunca uma PR por item. Publicar o asset do wheelhouse é ação do operador. Prioridade e milestone são sugestão; o sequenciamento é do operador.
Relacionado: #1451, #1452, #1059, #1401, #1822, #1966, #2023, #1116 (SDK bidirecional, ADR-0087), #865 (contrato do SDK), ADR-0091.
🤖 Registrado por Claude (Opus 4.8, T14, auditor RO), a pedido do operador em 30/set/2026. Verificado por gh api, PyPI e índice do PyTorch; nada executado nem baixado. Implementação: Cursor. Tom recomendatório.
Activity
[-][P3][packaging][detection][seed] DL para quem usa Docker: sidecar de embeddings por profile do compose, sem segunda tag e sem torch no cp314t[/-][+][P3][packaging][detection][seed] DL para quem usa Docker: wheelhouse próprio (tokenizers e safetensors cp314t, torch CPU) e decisão em processo × sidecar depois de medir[/+][-][P3][packaging][detection][seed] DL para quem usa Docker: wheelhouse próprio (tokenizers e safetensors cp314t, torch CPU) e decisão em processo × sidecar depois de medir[/-][+][P3][packaging][detection][seed] DL para quem usa Docker: pacote /extras montado a partir do wheelhouse próprio (tokenizers e safetensors cp314t, torch CPU)[/+]FabioLeitao commentedon Sep 30, 2026
Refino do item 4 (Lynx como encoder) — 2 análises externas independentes (30/set)
Duas leituras externas do ecossistema (Codex GPT-6-Luna e Grok 4.7), feitas sob supervisão do operador, confirmam o gap de air-gap do DL desta issue e da #1451 (o
DLClassifiercarrega o MiniLM semlocal_files_only; a 1ª vez exige o HF Hub). Independente de mim — reforça o item de falha alta.Correção ao item 4 (Lynx como encoder de longo prazo), verificada: o caminho
candledo Latent Lynx não tem o forward do MiniLM implementado — o embedder padrão é oMockEmbedder(SHA-256 → 384), e a featurecandleexige--model-dirmas, mesmo com os pesos no lugar, o load hoje retorna erro explícito (sósqlite-vec+--queryrodam sobre o mock). Ou seja, "servir o DL do Boar pelo Lynx" não é só apontar um diretório — exige primeiro implementar o forward em Rust/candle. Isso empurra a opção Lynx para mais longe do que a issue sugeria; a rota v1 (torch CPU +sentence-transformersno pacote/extras, do wheelhouse) segue sendo a de paridade exata.Ideia convergente (registro, decisão do operador): implementar o forward MiniLM do Lynx com
safetensorslocais e deixar o mesmo artefato de peso servir as duas missões — retrieval (Lynx) e classificação do Boar (vialocal_files_only/ diretório de modelo compartilhado): "um peso, duas missões, zero Hub". Uniria esta issue aolatent-lynx. Não é ação desta issue; fica como ponteiro.🤖 Claude (Opus 4.8, T14, auditor RO). Insight de análises externas (Codex/Grok) verificado contra o código; tom recomendatório.
FabioLeitao commentedon Sep 30, 2026
Ligação com a ideia de encoder compartilhado (30/set): criei
DataBoar/latent-lynx#24para implementar o forward real do MiniLM no Lynx (candle, safetensors locais). Se aquilo for adiante, o item 4 desta issue (Lynx como encoder do DL do Boar) deixa de ser "apontar um diretório" e passa a ser "consumir o mesmo artefato de peso do Lynx" — com o retreino/paridade da cabeça classificadora que esta issue já ressalva. O peso continua sendo ummodel.safetensorslocal pinado (não vira YAML nem entra no SQLite); no config do Boar entra só o PONTEIRO (diretório +local_files_only+ sha256 esperado).🤖 Claude (Opus 4.8, T14, auditor RO). Tom recomendatório.