Dsxir.Settings (dsxir v0.3.0)

Copy Markdown

Three-layer settings stack: globals (:persistent_term, with a per-process override), per-process scope (process dict), per-call opts (passed as args).

  • configure/1 writes node-wide globals. Call once at boot.
  • context/2 pushes a scoped frame for the duration of fun.().
  • snapshot/0 captures the current globals+stack; run/2 replays them in a worker.
  • resolve/2 looks up a key: stack top-down, then globals, then the provided default.

run/2 installs the snapshot's globals as a process-local override — it never writes to :persistent_term. Two workers replaying snapshots with different globals (different LM, adapter, etc.) cannot observe each other's globals, which matters for multi-tenant deployments where each tenant carries its own globals view.

tenant_* keys and :lm tuples whose config carries a non-nil :api_key are rejected by configure/1 with a Logger.warning. tenant_* keys nested inside :metadata are stripped from the map with a Logger.warning; non-tenant keys in the same map pass through unchanged. Per-request tenant data flows through context/2. The :lm field has shape nil | {impl_module :: module(), config :: keyword()} (see Dsxir.LM); credentials live in the config keyword list, not at the top level.

Summary

Functions

Install globals into :persistent_term. Merges with whatever is currently stored; unknown keys raise Dsxir.Errors.Invalid.Configuration. tenant_* keys and :lm tuples whose config carries a non-nil :api_key are dropped with a warning. tenant_* keys nested inside :metadata are stripped from the map (other keys preserved).

Push a scoped frame for the duration of fun.(). Restored via try/after.

Architectural defaults installed at application boot.

Walk the stack top-down, then globals, then return the default.

Replay a snapshot in the calling process for the duration of fun.().

Snapshot the live globals and scope stack for replay in another process.

Types

t()

@type t() :: %Dsxir.Settings{
  adapter: nil | module(),
  cache: boolean(),
  call_plugs: list(),
  callbacks: list(),
  lm: nil | {module(), keyword()},
  metadata: map(),
  program_plugs: list()
}

Functions

configure(opts)

@spec configure(Enumerable.t()) :: :ok

Install globals into :persistent_term. Merges with whatever is currently stored; unknown keys raise Dsxir.Errors.Invalid.Configuration. tenant_* keys and :lm tuples whose config carries a non-nil :api_key are dropped with a warning. tenant_* keys nested inside :metadata are stripped from the map (other keys preserved).

context(frame, fun)

@spec context(Enumerable.t(), (-> any())) :: any()

Push a scoped frame for the duration of fun.(). Restored via try/after.

default_globals()

@spec default_globals() :: map()

Architectural defaults installed at application boot.

resolve(key, default \\ nil)

@spec resolve(atom(), term()) :: term()

Walk the stack top-down, then globals, then return the default.

run(map, fun)

@spec run(%{globals: map(), stack: [map()]}, (-> any())) :: any()

Replay a snapshot in the calling process for the duration of fun.().

Installs the snapshot's globals as a process-local override and restores the scope stack. :persistent_term is never written, so concurrent workers replaying snapshots with different globals (different LM, adapter, etc.) do not leak globals into one another. Both the globals override and the scope stack are restored on exit, including when fun.() raises.

Nesting is supported: an inner run/2 shadows the outer override for the duration of its block.

snapshot()

@spec snapshot() :: %{globals: map(), stack: [map()]}

Snapshot the live globals and scope stack for replay in another process.