Insurgently

Obsidian plugin· Active · beta · not yet released

Model Hub

Named roles mapped to provider and model, keys kept in secret storage, and a cost ledger with the billed cost of every call. Other plugins call it for their AI.

Beta. Model Hub is not released yet. These pages describe the build in progress.

The API, call by call

What each Hub call does, what it takes and what comes back.

This page is the map. The full reference is generated from the code:

  • For reading: the API reference, with every declaration, its signature and its comment.
  • For tools and coding agents: model-hub-api.d.ts, every public declaration in one TypeScript file, and api.json, the same as data.

Both are also in the repo under docs/api/.

Finding the Hub

CallWhat it does
getHub(app, wanted)Returns { hub }, or { hub: null, reason, message } when the Hub is missing or its API is too old.
onHubReady(app, plugin, wanted, cb)Runs cb(hub) now if the Hub is loaded, and again each time it loads.
isCompatible(actual, wanted)True when an API at actual serves a plugin built for wanted.
hub.versionThe API's version.

Roles and tiers

CallWhat it does
registerRoles(pluginId, roles)Declares your roles. Harmless to repeat.
roles(pluginId)Your roles as they resolve now: the tier, the models to try, and every limit that applies. tierMissing is set when a role names a tier that doesn't exist.
setRole(pluginId, role, override)Changes one of your roles; null puts it back to your default. Only the owning plugin may.
openRoleEditor(pluginId)Opens the Hub's editor for your roles.
tiers()Every tier as the user has set it: the seven built-in ones, then the user's own. Each has a stable id, a display name and custom.
createTier(pluginId, name, policy)Asks the user to add a tier. The Hub shows a dialog naming your plugin. Resolves to the tier, or null if the user declines.
deleteTier(pluginId, id)Asks the user to delete one of their own tiers. Resolves true or false.

A role definition:

{
  name: 'mine.summarize',       // namespace.name, lowercase
  description: 'Summaries of a note',
  tier: 'quick',                // a tier's id or name; 'balanced' if left out
  ceilingUsd: 0.02,             // optional: this role's own limit per call
  monthlyBudgetUsd: 2,          // optional: this role's own limit per month
  allowAnyModel: false,         // true for interactive roles where the user picks a model per run
}

A role may also carry its own ordered candidates instead of a tier. Store the id that createTier returns; names keep working across renames.

Calls

Every call takes a role name first. All of them reject with an error whose code is one of the refusal codes.

CallWhat it does
complete(role, messages, opts?)One call. Returns the text, plus tool calls, sources and a structured object when asked for.
stream(role, messages, opts?)The same, with textStream to read as it arrives.
embed(role, values, opts?)Embeddings for a batch of texts.
decide(role, request, opts?)Typed decisions: yes/no, pick one, or a score. calibrated says whether the probabilities come from a decision model.
batch(items, fn, opts?)Runs many calls with limited concurrency, reporting progress and cost. Stops early on an overcharged call, a spend limit or an abort.

Options on complete and stream:

OptionWhat it does
systemThe system prompt.
maxOutputTokensThe longest reply allowed. The per-call limit is checked against it.
toolsTools the model may call. They are returned, not run: calls are single-turn.
outputA schema (zod or JSON Schema) the reply must match. complete only.
webSearchAsk the provider to search the web.
skillsSkills to apply, by name.
needsCapabilities you need beyond what the options imply.
forWhat the call is about, for the ledger.
signalAn AbortSignal to stop the call.
tier, model, candidatesAnother tier, one model or a list for this run. Only for roles with allowAnyModel.
routingRouting merged into each model's own for this call, such as an OpenRouter price cap.

What every finished call reports:

FieldWhat it holds
servedModel, servedProviderWho answered.
usageTokens in and out, cached and reasoning tokens.
listedUsdThe cost at listed prices; null when unknown.
billedUsdWhat the provider says it charged, when it says.
overchargedBilled well above listed.
attempts, fellBackEvery model considered, in order, and whether the answer came from a fallback.
budgetsThe monthly budgets that apply, with this month's spend; warn at 80%.
ledgerIdThe call's row in the ledger.

Models, skills and usage

CallWhat it does
models(filter?)The models the Hub knows, filtered by provider or by capability, for pickers.
skills()The skills the Hub can apply: its own, those plugins registered, and the vault's. Each has files() for its own text files.
registerSkills(pluginId, skills)Registers your built-in skills, each as a folder of text files with a SKILL.md. Replaces what you registered before.
skillsReady()Resolves once the vault's skills have loaded.
onSkillsChanged(cb)Calls cb when skills change. Returns the function that stops it.
usage(filter?)Ledger rows: every call, with tokens and cost.

A skill your plugin registers is used only when a call names it; it can't be always on. A vault skill with the same name overrides yours.

Related