@blueforge-studio/ai-routing

Pure routing core for AI generation: (domain, artifact, quality) -> an ordered, priced, explainable plan of provider/model candidates. No I/O, no gateway imports.

Informations sur le paquet

Latest Version0.1.0
LicenseMIT
Keywords-
Repositorygit+https://github.com/blueforge-studio/blueforge-org.git
Author-
Homepagehttps://github.com/blueforge-studio/blueforge-org#readme
Bugs[object Object]

Versions

VersionPubliée
0.1.02026-10-16

Chaîne d'approvisionnement par version

VersionSigSBOMAtt
0.1.0

Cliquez sur une version pour voir ses détails.

Chaîne d'approvisionnement

Provenance

Publié par
web:5
Quand
2026-10-16T21:12:01Z
sha256 (calculé par le registre)
22c57eb24fdc3068c3f2036bb7751237db46070148689f24996f574f730066f8

Observé par le registre au moment de la publication.

Déclaré par la compilation

L'éditeur n'a rien déclaré sur sa compilation.

Pièces jointes

Signature
aucune
SBOM
aucune

Dépendances

Chargement du graphe de dépendances...

README

@blueforge-studio/ai-routing

A pure routing core for AI generation. Give it a request (domain, artifact, quality) and your rules; get back an ordered, priced, explainable plan of provider/model candidates. It does no I/O, makes no HTTP calls and imports no gateway, so any gateway (image, audio, video, vision, text) can use it without pulling in the others' SDKs.

Design, inventory of the mechanisms it generalises, and decisions: docs/superpowers/specs/2026-10-16-ai-routing-unification.md. This is v0.1: the core only. No gateway uses it yet (image-gateway is the planned first adopter, behind an opt-in).

Use

import { defineRules, resolve, escalate, explain, memoryStickyStore } from "@blueforge-studio/ai-routing";

const rules = defineRules([                       // lowest priority first
  { name: "package", rules: { domain: "image", artifacts: {
      "*":     { tiers: {
          draft:    { routes: [{ provider: "gemini", model: "flash-image-2.5", config: { width: 512 } }] },
          standard: { routes: [{ provider: "gemini", model: "flash-image-2.5" }, { provider: "gemini", model: "flash-image-3.1" }] },
          premium:  { routes: [{ provider: "imagen", model: "imagen-ultra" }] } } },
      texture: { defaultTier: "draft", tiers: { draft: { config: { width: 256 } } } },   // patch config only
  } } },
  { name: "project", rules: { domain: "image", artifacts: { avatar: { tiers: { standard: { routes: [/* ... */] } } } } } },
]);

const sticky = memoryStickyStore();               // keep a book on one model
const plan = resolve(rules, { domain: "image", artifact: "chapter-cover", stickyKey: "book-7", maxCostUsd: 0.08 }, {
  isAvailable: ({ provider }) => hasKey(provider),                // from your gateway
  price: ({ model }) => costFromAiPricing(model) ?? null,          // null = unknown, never 0
  sticky,
});

if (plan.failure) throw new Error(explain(plan));
for (const c of plan.candidates) { /* try c in order with the gateway's own client */ }

const better = escalate(rules, plan, { isAvailable, price, sticky });   // "the cheap result failed validation, retry on the next tier"

Concepts

  • Quality is draft | standard | premium in every domain. What a tier means (model, size, resolution) is set by each domain's rules.
  • Layers: rule sets, lowest priority first (package defaults < project < env < per call). A layer's routes replace the route list for an (artifact, tier); its config deep-merges into the current routes' config (so "texture draft changes only width/height"). Within one layer the artifact's rules apply after the * defaults; across layers the higher layer wins, so layer priority outranks specificity. Every candidate records the layer its routes came from.
  • Strategies: fallback (as given), round-robin (from cursor), weighted (weighted-random order, so the order doubles as the fallback chain; weight 0 or less is a last resort, not dropped). Inject rng (see seededRng) for reproducible plans.
  • Capabilities: request.requires must all be provided (route.provides) or the route is excluded. A route that does not say what it provides fails any requirement. Nothing is ever substituted (an image-to-video request never gets a text-to-video model).
  • Availability comes from you (ctx.isAvailable): keys present, healthy, credit left.
  • Sticky: with a stickyKey and a ctx.sticky store, the first resolution records its primary; later ones return it while it stays available. Entries are per (domain, artifact, tier, key), so an escalated retry never rewrites the cheap tier's choice. onUnavailable defaults to fail when a stickyKey is set (a style-bound set must not silently change look) and fallback otherwise.
  • Cost: ctx.price returns dollars or null. Unknown is null, never 0, and a throwing, negative or NaN price becomes null with a trace line. maxCostUsd drops known-expensive candidates and keeps unknown ones; allowDowngrade steps down a tier when nothing fits. The core owns no budget state; jobKey is carried for your bookkeeping.
  • Failure is a value: an empty plan with failure (no-rules | no-routes | none-available | primary-unavailable | over-budget), never an exception, except defineRules which throws once with every problem and its path.
  • Explainable and deterministic: same rules + request + context (+ seeded rng) gives an identical plan. explain(plan) prints the order, costs, origin layers and the full trace.

Not here

Executing requests, retries, HTTP, a model catalogue, prices (use ai-pricing), budget state (use ai-budget), persistence for sticky choices (bring a store), default model lists (each gateway ships its own package layer when it adopts this).