Esta é a camada que todo modelo no sistema atravessa — CLI, Council, Swarm, Factory, ETL. O contrato é rígido e curto: run(input: ModelRunInput): Promise<ModelRunResult>, um Result discriminado em ok que nunca lança. Nesta lição você vai abrir essa "cintura estreita" linha a linha e ver por que ela é a peça mais importante de confiabilidade do Alembic.
adapter-core.ts + registry.ts + router.ts, ancorados em packages/contracts/src/model.ts
Tudo nesta página é citado de arquivo real do monorepo. O único conceito em torno do qual a camada inteira gira é a cintura estreita (the narrow waist): a interface ModelAdapter definida em contracts/src/model.ts e o spine que a implementa em adapters/src/adapter-core.ts. Nada aqui é inventado.
ModelAdapter.run.Result, não uma exceção.runWithGuards: validação Zod → circuit breaker → attempt → withRetry → feedback do breaker.err que ele devolve.attempt.Result<T, E> é "ou deu certo (ok) ou deu errado (err)".Imagine um prédio inteiro com uma só porta giratória. Cada pessoa que entra ou sai — não importa de qual sala, andar ou setor — passa por aquela única porta. Se a porta for confiável, o prédio é confiável. A camada L1 é essa porta: o ponto fino por onde toda chamada de modelo do Alembic obrigatoriamente passa.
O nome técnico é cintura estreita (narrow waist). O sistema tem muitas fontes de chamada lá em cima (o CLI, o Council, o Swarm, a Factory, o pipeline de ETL) e vários destinos lá embaixo (o gateway cliproxyapi, modelos MLX locais, o adapter offline). Entre eles existe um contrato comum: ModelAdapter.run(input): Result. Tudo afunila para esse ponto e depois se reabre — como uma ampulheta.
Pense como… a tomada de parede. Mil aparelhos diferentes (lá em cima) e várias usinas de energia (lá embaixo), mas no meio há um padrão de plugue. Você não reprojeta o ferro de passar para cada usina; ele só fala "plugue". Aqui, cada parte do Alembic só fala "ModelAdapter" — e nunca precisa saber qual modelo, qual provedor, ou o que fazer quando a rede cai.
O pacote @alembic/adapters tem cerca de 30 arquivos .ts em src/ (incluindo os testes). Ele depende quase exclusivamente de @alembic/contracts, que é quem define a cintura: modelRunInputSchema, o modelRunResultSchema (uma união discriminada) e a interface ModelAdapter, todos em contracts/src/model.ts. O @alembic/adapters é quem a implementa.
O comentário de cabeçalho de model.ts é literal: "The narrow waist of Alembic. Every model invocation in the system flows through ModelAdapter.run." É a única camada que todo run de modelo (CLI, Factory, Council, Swarm, ETL) toca — por isso é também a única em que vale a pena concentrar tanta disciplina de confiabilidade.
A cintura estreita: tudo converge para um ponto de passagem (ModelAdapter.run) e se reabre para os provedores. Acima da cintura é orquestração possuída; abaixo, geração de tokens delegada.
ModelAdapter.run) por onde toda chamada de modelo passa. Mude o que mudar acima ou abaixo, o ponto do meio continua o mesmo.Se toda chamada de modelo passa por ModelAdapter.run, e esse método nunca lança exceção, então quando a rede cai no meio de uma chamada, o que o método devolve?
Result de falha — { ok: false, error: { code: 'network_error', retryable: true, … } }. A queda de rede não vira throw; vira um valor. Quem chamou ramifica em result.ok e decide retry/escalar/park. Esse é exatamente o invariante never-throws que vamos abrir a seguir.A cintura tem duas pontas. Na entrada, um ModelRunInput validável. Na saída, um ModelRunResult que é uma união discriminada em ok: ou um sucesso com texto, ou uma falha com código de erro. Esses dois tipos vivem em contracts/src/model.ts, não em adapters — o contrato é declarado por quem o publica.
A entrada (modelRunInputSchema) carrega o essencial: requestId, modelId, systemPrompt, userPrompt e alguns campos opcionais (maxOutputTokens, temperature, timeoutMs, roleId, metadata). Há um detalhe fino: o signal (de cancelamento) não está no schema Zod — é um objeto de runtime que o Zod não consegue validar, então ele viaja ao lado dos campos validados.
A saída é onde mora a inteligência do contrato. Em vez de "devolve texto ou explode", ela sempre devolve um objeto com um campo ok. Se ok === true: tem text, usage (tokens), opcionalmente costUsd. Se ok === false: tem error com code, message e a flag decisiva retryable.
// contracts/src/model.ts export const modelRunInputSchema = z.object({ requestId: z.string().min(1), modelId: z.string().min(1), systemPrompt: z.string(), userPrompt: z.string(), maxOutputTokens: z.number().int().positive().optional(), temperature: z.number().min(0).max(2).optional(), timeoutMs: z.number().int().positive().optional(), roleId: z.string().min(1).optional(), metadata: z.record(z.string(), z.unknown()).optional(), }); // signal?: AbortSignal — carregado FORA do schema (host object, não-validável)
export const modelRunResultSchema = z.discriminatedUnion('ok', [ modelRunSuccessSchema, // { ok:true, text, usage?, costUsd?, durationMs, … } modelRunFailureSchema, // { ok:false, error:{ code, message, retryable }, … } ]);
O comentário na própria interface ModelAdapter deixa o invariante explícito: "INVARIANT: ModelAdapter.run NEVER throws. Any error … MUST be returned as a ModelRunFailure … Callers therefore never need a try/catch around run; they branch on result.ok."
ok é o discriminante.retryable é o que separa "tenta de novo" (429, 5xx, rede, timeout) de "não adianta" (4xx de cliente, auth, parse). O harness lê a flag, nunca a mensagem de erro — assim a decisão é estável e testável.signal (cancelamento) é um objeto de runtime, então viaja junto, fora do Zod.Aqui está o coração da camada. runWithGuards (em adapter-core.ts) é a função reutilizável que transforma "uma única tentativa" num run que respeita todo o contrato. Cada provider escreve só a tentativa; o spine faz o resto — sempre na mesma ordem.
Pense numa linha de montagem de cinco estações. A peça (a chamada) entra e percorre: (1) validação da entrada, (2) o disjuntor (circuit breaker) decide se pode executar, (3) a tentativa de fato — a única coisa que o provider implementa, (4) o retry com backoff se a falha for retryable, e (5) o feedback de sucesso/falha de volta para o disjuntor. No fim, sai um Result. Nunca uma exceção.
/** The reusable spine that turns a "single attempt" into a * compliant ModelAdapter `run`. It enforces, in order: * 1. boundary validation of ModelRunInput via Zod; * 2. the never-throws invariant (any escaped throw → failureFromThrown); * 3. an optional CircuitBreaker gate with correct success/failure feedback; * 4. withRetry backoff driven by each result's `retryable` flag. * Adapters implement only the inner attempt(input) => Promise<Result>. */
const guardedAttempt = async (...) => { if (breaker && !breaker.canExecute()) // (2) disjuntor aberto return { retry: true, value: breakerRejection(...), reason: 'circuit_open' }; let result; try { result = await attempt(input); } // (3) a tentativa do provider catch (cause) { result = failureFromThrown(..., cause); } // throw → Result if (result.ok) { breaker?.recordSuccess(); return { retry:false, value:result }; } breaker?.recordFailure(); // (5) feedback return result.error.retryable ? { retry:true, value:result, reason:result.error.code } // (4) deixa o withRetry decidir : { retry:false, value:result }; };
E a entrada pública, runWithGuards, começa exatamente pela validação: const validation = validate(adapterId, input); if (!validation.ok) return validation.result; — invalidou? Devolve Result de falha CLIENT_ERROR sem nunca tocar o provider.
O fluxo garantido de runWithGuards: o provider só escreve a estação 3 (attempt). O spine cuida das outras quatro — por isso todos os adapters se comportam igual.
Result.ModelRunInput válido. validate() roda safeParse, passa. O spine segue.canExecute() → true). Entra em guardedAttempt.attempt do provider, por um bug, lança uma exceção. O try/catch do spine captura e chama failureFromThrown(...) → vira um Result de falha. O throw não escapa.result.ok === false, o breaker recebe recordFailure(). Se a falha for retryable, o withRetry aguarda o backoff e tenta de novo; senão, devolve já.catch de guardedAttempt — o failureFromThrown converte o throw em Result. Nenhum provider precisa se preocupar com isso.)attempt correto. Todo o comportamento difícil de confiabilidade já vem de graça e idêntico para todos.CIRCUIT_OPEN, retryable); depois de um cooldown ele testa uma vez antes de voltar a confiar.Duas peças pequenas conectam tier a adapter. O registry (registry.ts) monta o mapa id → adapter. O router (router.ts) escolhe o modelo mais barato do tier e resolve o adapter dele — e, crucialmente, nunca troca de modelo em silêncio.
O createAdapterRegistry sempre registra o cliproxyapi (é o gateway padrão). Os outros entram só quando você os configura: o local só se você passar uma baseUrl (ex.: MLX/Ollama no Mac), e pi-dev, codex-oauth, anthropic, local-cli só quando suas opções são dadas. Assim um servidor local não-configurado não rouba o roteamento do gateway.
O pickAdapter(tier) chama pickCheapestForTier para achar o modelo mais barato daquele tier; se não houver, devolve err. Achou o modelo mas o adapter dele não está registrado? Outro err. Ele nunca devolve um substituto silencioso.
export const createAdapterRegistry = (options = {}) => { const registry = new Map(); registry.set(cliproxy.id, cliproxy); // SEMPRE if (options.localOpenai?.baseUrl) registry.set(local.id, local); // condicional if (options.piDev) registry.set(piDev.id, piDev); if (options.codexOauth) registry.set(codex.id, codex); if (options.anthropic) registry.set(anthropic.id, anthropic); if (options.localCli?.command) registry.set(cli.id, cli); if (options.offline) { registry.set(offline.id, offline); if (options.offline.asDefault) // liga offline em TODOS os tiers for (const e of Object.values(MODEL_REGISTRY)) registry.set(e.adapterId, offline); } return registry; };
export const pickAdapter = (tier, adapters, options = {}) => { const entry = pickCheapestForTier(tier, registry); if (!entry) return err({ kind: 'no_model_for_tier', tier }); const adapter = adapters.get(entry.adapterId); if (!adapter) return err({ kind: 'adapter_not_registered', tier, modelId, adapterId }); return ok({ entry, adapter }); }; // adapterForModel(modelId, …) ignora o tier e resolve um modelo específico (+ kind:'unknown_model')
O comentário do router é categórico: "NO SILENT FALLBACK. … A router that quietly substituted a different model would hide cost/capability regressions, so that is deliberately not done here."
A regra NO SILENT FALLBACK: dois pontos onde o router pode devolver err explícito, e nunca um modelo substituto. Falhar barulhento > falhar escondido.
err possíveis, um ok. Nunca um substituto escondido.err obriga quem chamou a decidir conscientemente: escalar o tier ou abortar.O gateway padrão. É o único adapter que createAdapterRegistry registra incondicionalmente — toda configuração de registry tem, no mínimo, ele.
cliproxyapi → registrado sempre.
Em volta do spine há um conjunto de arquivos obrigatórios — as preocupações transversais que todo provider herda sem reescrever: retry, circuit breaker, custo, http, erros, logger, clock, token-store. Cada um faz uma coisa, bem.
| Arquivo | Responsabilidade | Fato-chave |
|---|---|---|
retry.ts | Backoff exponencial com full jitter | DEFAULT_RETRY_POLICY: 3 tentativas, 200ms→2s |
circuit-breaker.ts | Máquina de estados; canExecute() + feedback | abre o circuito após falhas e dá um cooldown |
base·2^n, o retry espera um valor aleatório em [0, delay]. Isso espalha as novas tentativas no tempo e evita que mil chamadas reenviem todas no mesmo instante (o "thundering herd").| Arquivo | Responsabilidade | Fato-chave |
|---|---|---|
cost.ts | Contabilização de tokens → USD | computeCostUsd, arredondado a 6 casas |
errors.ts | Taxonomia de erros + failureFromThrown | 10 ErrorCode com retryable definido |
costForModel devolve undefined quando o modelo é desconhecido — o chamador então omite costUsd em vez de reportar um zero enganoso. Não fabricar um número é melhor que mentir um.| Arquivo | Responsabilidade | Fato-chave |
|---|---|---|
http.ts | Wrapper de fetch compartilhado | usado pelos providers de rede |
clock.ts | Clock injetável (sleep) | um fake clock adianta o backoff nos testes |
token-store.ts | Persistência de credenciais/tokens | para adapters com OAuth |
withRetry de fato aguarda o backoff via clock.sleep. Em produção é o relógio real; no teste é um falso que pula o tempo — assim os testes de retry rodam em milissegundos sem perder o realismo.Existem oito providers concretos, e todos têm a mesma forma minúscula: implementam apenas o attempt. O spine envolve cada um com validação + breaker + retry. Por isso o comportamento é idêntico independentemente de quem gera os tokens.
O offline.ts é o exemplo mais bonito disso: ele é o adapter mais simples possível justamente porque o spine já cuida de tudo. Ele é o caso hermético de $0 — determinístico, sem rede — usado em runs offline. Já o cliproxyapi.ts é um proxy fetch puro para o gateway: ele não tem lógica de inferência, só repassa.
// adapters/src/ cliproxyapi.ts // gateway padrão (proxy fetch puro) local-openai.ts // servidor OpenAI-compatível local (MLX/Ollama) offline.ts // hermético $0, determinístico anthropic.ts // Anthropic de primeira classe codex-oauth.ts // Codex via OAuth codex-exec.ts // Codex via subprocess (CLI) pi-dev.ts // pi.dev local-cli.ts // CLI local genérico (presets em cli-presets.ts)
Há ainda openai-compatible.ts como base compartilhada para os providers que falam o dialeto OpenAI. Todos terminam construindo seu run com runWithGuards, então nenhum reimplementa "nunca lançar" ou retry.
asDefault faz o registry sobrescrever cada adapterId de MODEL_REGISTRY com o mesmo adapter offline — qualquer tier resolve para o $0, sem rede.| Dimensão | offline.ts | cliproxyapi.ts |
|---|---|---|
| Toca a rede? | não | sim (fetch ao gateway) |
| Custo | $0 hermético | conforme tokens |
| Determinístico | sim | não |
| O que implementa | só attempt | só attempt |
| never-throws / retry | do spine | do spine |
offline.asDefault: true, o registry liga o adapter offline a todos os adapterId de MODEL_REGISTRY — então o roteamento em qualquer tier resolve para ele, sem rede. É um opt-in explícito (não um fallback silencioso): é assim que alembic distill --offline roda a $0.attempt correto e registrá-lo — o resto vem pronto.Alterne entre a explicação leiga e a precisa. Use "Técnico" quando quiser os nomes reais; "Simples" quando quiser a intuição.
ModelAdapter.run é o contrato; runWithGuards o implementa com validate (safeParse) → CircuitBreaker.canExecute → attempt (capturado por failureFromThrown) → withRetry (DEFAULT_RETRY_POLICY: 3 tentativas, 200ms→2s, full jitter) → recordSuccess/recordFailure. pickAdapter usa pickCheapestForTier e devolve err em vez de substituir. computeCostUsd é puro; offline.asDefault liga o $0 a todos os tiers.Vire cada cartão (clique, ou Enter/Espaço) e tente responder antes de ver o verso. É prática de recuperação — vale mais que reler.
ModelAdapter.run(input): Result por onde toda chamada de modelo passa. Um contrato, um ponto de passagem.run nunca lança?try/catch: ele ramifica em result.ok. Falha é um valor, não uma exceção — controle de fluxo uniforme.attempt(input). Validação, breaker, retry e never-throws vêm do spine (runWithGuards), iguais para todos.err (no_model_for_tier / adapter_not_registered) em vez de trocar de modelo escondido — que esconderia regressões de custo/capacidade.Result tem retryable: true.costForModel devolve undefined e o chamador omite costUsd — em vez de reportar um zero enganoso. Honestidade > número falso.Responda as três. O placar abaixo conta seus acertos.
attempt de um provider lança uma exceção por um bug, o que acontece?guardedAttempt tem um try/catch de defesa em profundidade: qualquer throw vira Result via failureFromThrown, honrando o never-throws. (a) viola o invariante; (c) é falso — o breaker recebe recordFailure().pickAdapter devolve?err({ kind: 'no_model_for_tier', tier }). (a) é exatamente o que o router se recusa a fazer; (b) inventaria um sucesso falso.offline.ts é tão simples?runWithGuards, um provider só precisa de um attempt correto — e o offline determinístico $0 é o mais enxuto. (b) e (c) violariam o contrato.Nada nesta página vale sem prova. Aqui está como confirmar, no seu próprio terminal, que a cintura se comporta como descrito.
pnpm --filter @alembic/adapters test. Foque em adapter-core.test.ts (guarded + never-throws), retry.test.ts, circuit-breaker.test.ts, cost.test.ts e offline.test.ts.attempt lança e confirme que o Result final tem ok: false com um code adequado e que o breaker recebeu recordFailure().alembic distill ./corpus --offline e verifique no FunnelReport que costUsd = 0 e nenhum adapter de rede foi tocado (registry com offline.asDefault: true).createAdapterRegistry e compare o comportamento de offline vs cliproxyapi no mesmo input. O que muda no FunnelReport? (Dica: a forma do Result é idêntica; o que muda é custo e determinismo.)# a camada inteira, verde: pnpm --filter @alembic/adapters test # o baseline do monorepo (toda mudança mantém isto verde): pnpm -r typecheck && pnpm -r build && pnpm -w test
pickAdapter + run (que devolve Result). Qualquer throw interno é convertido pelo spine — você nunca precisa de try/catch em volta de run.ModelAdapter.run — a cintura estreita.run nunca lança; devolve um Result discriminado em ok.retryable (não a mensagem) decide retry vs falha terminal.attempt; o spine faz o resto.err, nunca um modelo substituto (NO SILENT FALLBACK).undefined para modelo desconhecido, nunca um zero falso.offline.asDefault liga o $0 hermético a todos os tiers — um opt-in explícito.