Duas ferramentas — speech-to-text e compreensão de imagem — construídas no exato mesmo padrão de portas que você já viu seis vezes. Cada uma é um kernel de despacho minúsculo: validar o pedido (Zod), chamar o backend injetado, re-validar o resultado não confiável (Zod), retornar um Result. Os backends de produção são impls fetch sem dependências. Esta lição é a prova final de que a disciplina escala: uma capacidade nova é uma porta mais um kernel mais uma costura fetch fina — nada mais. Um CLONE do transcription_tools.py (1799 LOC) + vision_tools.py do Hermes, só o caminho CLOUD.
A chamada de rede de cada ferramenta vira uma porta injetada — uma única função retornando um Result. O módulo não importa SDK:
// packages/hermes/src/media/types.ts:133-148 export type TranscriptionBackend = ( req: TranscriptionRequest, ) => Promise<Result<TranscriptionResult, Error>>; export type VisionBackend = ( req: VisionRequest, ) => Promise<Result<VisionResult, Error>>;
// packages/hermes/src/media/media.ts:51-68 — transcribe const parsedReq = transcriptionRequestSchema.safeParse(req); if (!parsedReq.success) return err(new Error(`Invalid transcription request: …`)); const transcribed = await deps.backend(parsedReq.data); if (!transcribed.ok) return transcribed; // falha do backend ⇒ err const parsed = transcriptionResultSchema.safeParse(transcribed.value); if (!parsed.success) return err(new Error(`Invalid transcription result: …`)); // saída não confiável return ok(parsed.data);
analyzeImage é byte-a-byte a mesma forma (validar pedido → backend → validar resultado), diferindo só nos schemas. Essa simetria é a lição: uma vez estabelecido o padrão, uma nova ferramenta de mídia é mecânica.
O pedido de transcrição modela uma fonte de áudio portável — uma URL que o backend busca, ou base64 inline — e impõe "exatamente uma" com uma refinação Zod (um XOR sobre os dois opcionais):
// packages/hermes/src/media/types.ts:62-74 — transcriptionRequestSchema z.object({ audioUrl: z.string().url('audioUrl must be a valid URL').optional(), audioBase64: z.string().min(1, 'audioBase64 cannot be empty').optional(), mimeType: z.string().min(1).optional(), }).refine( (req) => (req.audioUrl === undefined) !== (req.audioBase64 === undefined), { message: 'exactly one of audioUrl or audioBase64 is required' }, );
O !== sobre dois checks === undefined é um XOR booleano: verdadeiro só quando exatamente uma fonte está presente. Dois testes fixam ambos os modos de falha — nenhuma fonte, e ambas as fontes, são cada um rejeitados na fronteira antes do backend ser chamado.
{success, transcript, provider, error} para STT, {success, analysis} para vision. Aqui, success/error colapsam no envelope Result, e o núcleo de sucesso é aparado: transcript → o text idiomático do motor (com provider opcional de proveniência), analysis fica analysis. O ganho: uma falha não pode se disfarçar de text vazio — é um err, estruturalmente distinto de ok({text:''}) (silêncio legítimo).Como o backend web, os backends de mídia são impls fetch finas sobre o fetch global, com mapeamento defensivo de payload e fallbacks de campo. O kernel re-valida, então o mapeador pode ser tolerante:
// packages/hermes/src/media/fetch-backends.ts:163-178 — mapeamento defensivo de linha const mapTranscriptionRow = (payload) => { const provider = readField(payload, 'provider'); return { text: asString(readField(payload, 'text') ?? readField(payload, 'transcript')), // fallback ...(typeof provider === 'string' && provider.length > 0 ? { provider } : {}), }; }; const mapVisionRow = (payload) => ({ analysis: asString(readField(payload, 'analysis') ?? readField(payload, 'content')), // fallback });
A fonte suporta seis provedores STT cloud e um caminho local faster-whisper (ML Python, território de GPU/download de modelos). A matriz de fusão marca o caminho local como IGNORE: é amarrado a ML Python e fora de escopo para um kernel TypeScript portável, enquanto os seis provedores cloud colapsam para uma porta injetada (todos só fazem POST de áudio para um endpoint). Esta é a disciplina funcionando como pretendido — clonar a estrutura portável, ignorar o que não traduz, e dizer isso explicitamente. Testes injetam um fetch fake provando o mapeamento e os caminhos de transporte falha-fechado (não-2xx, throw de rede, JSON não parseável cada um → err) sem abrir um socket. Um teste até prova que um campo de payload não-string falha fechado pelo portão Zod do kernel — defesa em profundidade, de novo.
audioUrl e audioBase64. O que acontece?.refine do schema é um XOR: (audioUrl===undefined) !== (audioBase64===undefined) é verdadeiro só quando exatamente um está presente. Ambos (ou nenhum) falha fechado com err — nunca lança, nunca chega ao backend.success/error não aparecem como campos em TranscriptionResult?success/error do envelope plano do Python viram o ok/err do Result. O benefício é que um transcript vazio (silêncio real) é ok({text:''}), nunca confundido com uma falha, que é err.faster-whisper de transcrição da fonte foi marcado IGNORE na fusão?TranscriptionBackend.createFetchTranscriptionBackend/createFetchVisionBackend são costuras finas de JSON genérico sobre um fetch injetável, então os testes nunca abrem um socket.text vazio é legítimo (silêncio) e chega como ok({text:''}). Uma falha é um err. Manter success/error fora do payload e no Result é exatamente o que torna os dois inequívocos.@alembic/hermes obedece à mesma disciplina: injetar as portas, retornar Result, validar entrada não confiável com Zod, nunca lançar, sem Date.now()/Math.random(). Isso é a Lição 5 tornada concreta, sete vezes. Releia a Lição 5 agora e ela deve soar como um resumo de tudo acima.