Lookup table for the building blocks dynamic councils reference by string name: profiles, tools, output schemas, routers, custom rounds.
Two storage tiers:
Config (boot-time) — set in
config :council_ex, :registry, profiles: %{...}. Read on every lookup; no copy. Safe across releases.Runtime overrides (in-memory) —
register_*/2writes to a shared ETS table. Useful for hot-loaded modules that should not be persisted to config (e.g., experiment branches, plugin code). Survives until the BEAM dies. Wins over config on lookup conflict.
Example
# config/config.exs
config :council_ex, :registry,
profiles: %{
"openai_mini" => CouncilEx.Profiles.OpenAIMini,
"anthropic_balanced" => CouncilEx.Profiles.AnthropicBalanced
},
tools: %{
"calculator" => MyApp.Tools.Calculator
}
# at runtime
:ok = CouncilEx.Registry.register_tool("scratchpad", MyApp.Tools.Scratchpad)
MyApp.Tools.Scratchpad = CouncilEx.Registry.lookup!(:tool, "scratchpad")
Summary
Functions
Return the merged map (config + runtime) for a kind.
List the known registry kinds.
Sorted list of registered names for a kind.
Look up a value by kind + name. Returns nil if not registered. Runtime
overrides win over config.
Look up a value by kind + name. Raises if not registered.
Generic register — kind must be one of [:profile, :tool, :schema, :router, :round, :sub_council, :input_mapper, :council].
Register a routable council under name. Used by CouncilEx.AutoCouncil
to discover councils a strategy can pick from.
Register an input mapper under name. The value must be a 1-arity
function (typically a remote capture like &MyApp.Mappers.to_seo_input/1)
used by dynamic sub-council members to project the parent input.
Register a profile module (or struct) at runtime under name.
Register a round module at runtime under name.
Register a router module at runtime under name.
Register an output schema (Ecto module or JSON Schema map) under name.
Register a sub-council target at runtime under name. Accepts either a
static council module or a %CouncilEx.DynamicCouncil{} struct. Used by
dynamic councils that reference sub-councils by string name.
Register a tool module at runtime under name.
Wipe ALL runtime registrations. Config entries unaffected.
Remove a runtime registration. Has no effect on config-defined entries.
Functions
Return the merged map (config + runtime) for a kind.
@spec kinds() :: [atom()]
List the known registry kinds.
Sorted list of registered names for a kind.
Look up a value by kind + name. Returns nil if not registered. Runtime
overrides win over config.
Look up a value by kind + name. Raises if not registered.
Generic register — kind must be one of [:profile, :tool, :schema, :router, :round, :sub_council, :input_mapper, :council].
Register a routable council under name. Used by CouncilEx.AutoCouncil
to discover councils a strategy can pick from.
The value should be a map with at least:
%{
council: module() | CouncilEx.DynamicCouncil.t(),
desc: String.t(), # human-readable description (used by LLM strategies)
match: Regex.t() | (binary() -> boolean()) | nil, # optional, used by Rules
vector: [float()] | nil, # optional, used by Embedding
tags: [atom()] | nil, # optional, free-form
providers: MapSet.t(atom()) | nil # cached at registration; see below
}Strategies may ignore fields they don't need. Only :council is mandatory.
At registration time the registry computes the council's provider set
(via CouncilEx.AutoCouncil.Providers.used/1) and stashes it under
:providers. This cache backs provider_check: true so the resolver
doesn't re-walk the council on every route.
Cache invalidation
Re-register to invalidate. The cache only goes stale if the underlying
council's member providers change and the entry isn't re-registered.
Static councils carry their own __providers__/0, so the cache is
effectively free; dynamic councils get a single to_spec/1 walk per
registration.
Pass providers: nil (or any explicit MapSet) to override the
auto-computed value.
Register an input mapper under name. The value must be a 1-arity
function (typically a remote capture like &MyApp.Mappers.to_seo_input/1)
used by dynamic sub-council members to project the parent input.
Register a profile module (or struct) at runtime under name.
Register a round module at runtime under name.
Register a router module at runtime under name.
Register an output schema (Ecto module or JSON Schema map) under name.
@spec register_sub_council(String.t(), module() | CouncilEx.DynamicCouncil.t()) :: :ok
Register a sub-council target at runtime under name. Accepts either a
static council module or a %CouncilEx.DynamicCouncil{} struct. Used by
dynamic councils that reference sub-councils by string name.
Register a tool module at runtime under name.
@spec reset_runtime() :: :ok | {:error, term()}
Wipe ALL runtime registrations. Config entries unaffected.
Remove a runtime registration. Has no effect on config-defined entries.