You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
[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
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 torch e onnxruntime nã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 lido docs/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, joblib com hash no requirements.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.md diz que ela é "lean on purpose": "no fat image, no image matrix, no FROM our image + rebuild", sem shell nem pip. O extra dl está 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), com PYTHONPATH=/extras:/app. python main.py --check-extras mostra 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 onnxnão remove o torch)
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-boar no PyPI (1.7.4.post12, requires-python >=3.12) já tem o extra dl, então fora do Docker pipx install "data-boar[dl]" funciona no host (não executei).
Proposta (síntese do Claude, a confirmar)
Rota principal do Docker: o pacote /extras do dl, 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:/extras vem antes de /app no PYTHONPATH, 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 ao requirements.txt da imagem, para não trocar numpy/scikit-learn pinados. Conferir por origem no --check-extras. (Raciocínio pela mecânica do PYTHONPATH; não executei.)
Wheelhouse próprio (mesmo canal hospedado em data-boar-site, com SHA256SUMS e índice simple/): construir tokenizers e safetensors para cp314t (Rust, maturin; commit fixado por SHA, sem rede, código lido) e espelhar o torch CPU (+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:torch CPU + sentence-transformers no pacote /extras (paridade exata com o caminho atual; tamanho do pacote não medido);
sentence-transformers com backend=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.
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.py do 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;
musl e no-AVX: DL limitado e documentado (decisão do operador): degrada em voz alta para regex + ML, como o PLAN_PACKAGING_EXTRAS já 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 no docker load, senão registrar o digest nas notas do release), o pacote /extras e os pesos do modelo.
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.md com <!-- plans-hub-summary: ... --> e ligá-lo de PLAN_WHEELHOUSE_DISTRIBUTION.md.
Teste: sem DL utilizável, dl: UNAVAILABLE no 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.yml com 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.
🤖 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.
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):
Contexto (verificado por
gh api, PyPI e índice CPU do PyTorch, 30/set)scikit-learn==1.9.1,numpy,scipy,joblibcom hash norequirements.txt). Não executei a imagem.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"./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.torch2.14.02.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ãoonnxruntime1.30.0tokenizers0.23.2safetensors0.8.0sentence-transformers6.1.0torch>=2.2(o extraonnxnão remove o torch)transformers,huggingface-hubhf-xet,numpy,scipy,scikit-learn,regex,pillow,pyyamlWheels abi3 não carregam em build free-threaded (regra do CPython; confirmar com
pip install --dry-runnum 3.14t). O que falta parapip install --target ./extras-pack 'data-boar[dl]'funcionar num host cp314t sãotokenizersesafetensorsem cp314t; sem eles o pip cai em build de Rust na máquina do cliente.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)
/extrasdodl, sem mexer na imagem. O pacote é construído a partir do wheelhouse, offline:pip install --target ./extras-pack --no-index --find-links <wheelhouse> ..../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.)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.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.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;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);--model-dir(sem download silencioso); não há modo "só vetores". Candidato de longo prazo, com retreino da cabeça e teste de paridade.handshake.pydo Stoat já faz do lado dele ("no attestation, no transform — fail closed");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.PLAN_PACKAGING_EXTRASjá manda. O ML continua nessas plataformas pelo wheelhouse x86-64-v1.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.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
docs/plans/PLAN_DL_DOCKER_AND_WHEELHOUSE.mdcom<!-- plans-hub-summary: ... -->e ligá-lo dePLAN_WHEELHOUSE_DISTRIBUTION.md.python scripts/plans_hub_sync.py --write.docs/plans/PLANS_TODO.md.encode(), GIL ligado e desligado, paridade dos vetores, tamanho, latência), com go/no-go explícito.tokenizersesafetensorscp314t, comSHA256SUMS./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".dl: UNAVAILABLEno recibo e no log, e o resultado é idêntico ao de hoje.docs/TROUBLESHOOTING.md.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.