/** * Model Router — abstraction layer de modelos LLM. * * ModelProvider (catálogo) → ModelSpec (capacidades/custo) → ModelRouter * (seleção por complexidade, raciocínio, risco, custo, latência e histórico). * * Izanagi não depende conceitualmente de um único provider: o catálogo pode * ser estendido via config do projeto (.izanagi/izanagi.config.json → models). */ import type { ModelProvider, ModelSpec, ModelTier, RoutingContext } from '../types.js'; import type { AgentRole } from '../contracts/task-contract.js'; export declare const DEFAULT_PROVIDERS: ModelProvider[]; /** * A partir de quantos dias a tabela de preços de um provider é tratada como * obsoleta e avisada ao usuário. * * 120 dias é uma escolha, e o motivo é o intervalo observado entre gerações de * modelo dos providers grandes: abaixo disso o aviso dispararia em catálogo * ainda correto e viraria ruído que se aprende a ignorar; muito acima, ele * nunca dispara antes do preço já estar errado. Não é uma medição. */ export declare const STALE_CATALOG_AFTER_DAYS = 120; /** * Idade em dias da tabela de preços de um provider, ou `null` quando ele não * declara data. `null` é ausência de informação, e o chamador tem que * apresentá-la como ausente: idade 0 diria "o preço é de hoje", que é * exatamente a afirmação que não se pode fazer sem a data. */ export declare function catalogAgeDays(provider: ModelProvider, now?: Date): number | null; /** `true` quando a tabela passou de `STALE_CATALOG_AFTER_DAYS`. Sem data, nunca. */ export declare function isCatalogStale(provider: ModelProvider, now?: Date): boolean; export declare class ModelRouter { private readonly providers; constructor(providers?: ModelProvider[]); /** * Lê `.izanagi/izanagi.config.json` → `models` (array de ModelProvider) e * mescla com o catálogo default (providers do projeto têm prioridade sobre * um provider default de mesmo id). Arquivo ausente ou inválido → só o * catálogo default. */ static loadProjectProviders(baseDir: string, defaults?: ModelProvider[]): ModelProvider[]; /** Todos os modelos disponíveis (com score calculado). */ catalog(): ModelSpec[]; /** Estima complexidade da tarefa 1-5 por heurística textual. */ static estimateComplexity(task: string): 1 | 2 | 3 | 4 | 5; /** Roteia o modelo mais adequado para o contexto. */ route(ctx: RoutingContext): { model: ModelSpec; provider: string; reasons: string[]; candidates: Array<{ option: string; score: number; }>; }; private scoreModel; /** * Provider dono de um model id do catálogo (ex.: `claude-sonnet-5` → * `anthropic`). Público porque o validador de canvas e o resolveNodeModel * precisam conferir provider de configuração manual sem re-implementar a * busca. */ providerOf(modelId: string): string; /** * Roteia por PAPEL, não por run. O princípio da arquitetura é "o modelo mais * forte pensa e coordena, modelos menores executam": antes desta rota, um * único modelo era escolhido no início do run e usado em TODOS os nós, * inclusive numa extração trivial. Agora cada tarefa paga o preço do seu * papel. * * Precedência: pin explícito (config `roles` / env) vence; senão escolhe o * melhor modelo dentro do tier do papel; tier vazio no catálogo disponível * cai para o tier adjacente (nunca falha por catálogo restrito). */ /** * Tier pedido pelo AGENTE do nó, quando ele pede algum. * * Os 22 agentes core declaram `model` no próprio JSON (`sonnet`, `opus`) e * até aqui NADA lia esse campo: o `AgentCapabilityRegistry` o expunha como * `modelHint` e o roteamento decidia só pelo papel. Na prática, "o * orquestrador escolhe o modelo de cada agente que ele comanda" era verdade * pela metade: o papel escolhia, o agente não tinha voz. * * O hint é um TIER, não um id de modelo, e é isso que o mantém * provider-agnostic: `opus` num catálogo sem premium continua caindo pelo * `tierFallbackOrder` de sempre, e o mesmo agente roda no melhor modelo * disponível seja Anthropic, OpenAI, local ou CLI de agente. */ static tierForHint(hint: string | undefined): ModelTier | undefined; /** * @param hintedTier Tier pedido pelo agente do nó (ver `tierForHint`). * Perde para um modelo FIXADO pelo usuário (config `roles` / * `IZANAGI_MODEL_*`), porque pin é decisão explícita de quem paga a conta, * e vence o default do papel, porque quem conhece a tarefa é o agente. */ routeForRole(role: AgentRole, ctx: RoutingContext, hintedTier?: ModelTier): RoutedModel; /** * Escalada por falha repetida: worker sobe para specialist, specialist para * commander. Commander é o topo (não existe escalada acima dele). Devolve * null quando não há para onde subir, e quem chama decide abortar. */ static escalateRole(role: AgentRole): AgentRole | null; /** * Rebaixamento por pressão de orçamento: o inverso de `escalateRole`. * Commander vira specialist, specialist vira worker. Worker é o piso (não * existe nada mais barato para onde descer), e quem chama decide se corta a * tarefa ou pede aprovação humana. */ static demoteRole(role: AgentRole): AgentRole | null; /** Modelo pinado para um papel via env ou `.izanagi/izanagi.config.json` → `roles`. */ private pinnedFor; /** Política de papéis injetada por `loadRolePolicy` (config do projeto). */ private rolePolicy?; withRolePolicy(policy: RolePolicy | undefined): this; /** * Custo em USD de uma chamada. Modelos locais (Ollama/LM Studio) têm custo 0 * declarado no catálogo, então self-hosted aparece corretamente como grátis. */ static costUsd(model: ModelSpec, inputTokens: number, outputTokens: number): number; /** * Custo estimado de gastar `tokens` no papel `role`, assumindo a divisão * típica de 70% entrada / 30% saída. Usado pelo Commander no cost-aware * planning (estimativa de TETO, não previsão). */ estimateCostForRole(role: AgentRole, tokens: number): number; /** Lê `.izanagi/izanagi.config.json` → `roles`. Ausente/inválido = sem pin. */ static loadRolePolicy(baseDir: string): RolePolicy | undefined; } export interface RoutedModel { model: ModelSpec; provider: string; role: AgentRole; tier: ModelTier; reasons: string[]; candidates: Array<{ option: string; score: number; }>; } export type RolePolicy = Partial>; /** Tier preferido por papel: o coração da inteligência assimétrica. */ export declare const TIER_FOR_ROLE: Record; //# sourceMappingURL=router.d.ts.map