# Configuration
Configure Jidoka with a small set of application defaults. Provider keys stay
in the process environment, where ReqLLM reads them at call time.
## When To Use This
- Use this guide when you want to change the default model, generation
parameters, loop budget, or turn timeout for a host application.
- Use this guide as a reference when wiring `:jidoka` into a Phoenix or umbrella
project for the first time.
- Do **not** use this guide for per-agent configuration; those values live in
the DSL or in the imported spec. See [Agent DSL](agent-dsl.md).
- Do **not** use this guide as a credential-management primer. Jidoka does
not own provider auth; that is ReqLLM's responsibility.
## Prerequisites
- A working Jidoka DSL agent. See [Getting Started](getting-started.md).
- A `config/config.exs` (and ideally `config/runtime.exs`) in the host
application.
- For live turns: provider credentials available in the runtime environment.
## Quick Example
A minimal `config/config.exs` for a Jidoka-backed application:
```elixir
import Config
config :jidoka,
default_model: "openai:gpt-4o-mini",
default_max_model_turns: 8,
default_turn_timeout_ms: 30_000,
default_generation: %{
params: %{
temperature: 0.0,
max_tokens: 500
}
}
import_config "#{config_env()}.exs"
```
`Jidoka.Config` reads these keys on first use; agents that do not specify
their own `model`, `generation`, `max_model_turns`, or `timeout_ms` fall
through to these values.
## Concepts
```diagram
╭───────────────────────────╮
│ config/config.exs │
│ config :jidoka, ... │
╰─────────────┬─────────────╯
│ Application.get_env(:jidoka, key)
▼
╭───────────────────────────╮
│ Jidoka.Config │
│ default_model │
│ default_generation │
│ default_max_model_turns │
│ default_turn_timeout_ms │
╰─────────────┬─────────────╯
│ used by
▼
╭───────────────────────────╮ ╭───────────────────────────╮
│ Jidoka.Agent.Spec │ │ Turn.Plan defaults │
│ (when DSL omits values) │ │ (loop + timeout budget) │
╰───────────────────────────╯ ╰───────────────────────────╯
╭───────────────────────────╮
│ Process environment │
│ OPENAI_API_KEY │
│ ANTHROPIC_API_KEY │
│ GEMINI_API_KEY │
╰─────────────┬─────────────╯
│ read by
▼
╭───────────────────────────╮
│ ReqLLM │
│ per-call provider auth │
╰───────────────────────────╯
```
Three concepts cover the config story:
1. **Four `:jidoka` keys.** `default_model`, `default_generation`,
`default_max_model_turns`, and `default_turn_timeout_ms`. Each one has a
built-in fallback inside `Jidoka.Config`, so the application config block
is optional for development.
2. **Provider env vars.** Jidoka never reads provider keys; ReqLLM does, at
call time. Setting `OPENAI_API_KEY` (or the equivalent for another
provider) is enough.
3. **No dotenv loading.** The package does **not** load `.env` files. Set
environment variables through your shell, your supervisor, or your
deployment platform.
### Security / Trust Boundaries
- Provider credentials must not appear in `config/*.exs`. Compiled config is
baked into releases; rotating a secret would force a redeploy and bake the
old secret into release artifacts.
- Per-agent credentials, tenant ids, and actor data belong in the runtime
context for a turn (`Jidoka.turn(spec, input, context: %{...})`), not in
the application config.
- `Jidoka.Config.default_model/0` resolves the configured model through
`ReqLLM.model/1`. That call validates the model id but does not contact a
provider; configuration is still side-effect free.
- The four `:jidoka` keys are public defaults. Treat them as the floor of
agent behaviour: any agent may raise the loop budget or timeout, but
no agent should silently lower the floor by relying on missing config.
- `mix release` snapshots application config at build time. Use
`config/runtime.exs` for any value that should be evaluated at boot, and
read provider env vars through `System.fetch_env!/1` if you must surface
them at boot rather than at call time.
## How To
### Step 1: Set The Default Model
The model is the value most callers want to override. The string form is
parsed by ReqLLM at first use.
```elixir
# config/config.exs
import Config
config :jidoka, default_model: "openai:gpt-4o-mini"
```
An agent that does not specify `model "..."` inherits this value. To pin a
specific agent, declare it in the DSL:
```elixir
agent :time_agent do
model "anthropic:claude-3-5-sonnet"
end
```
### Step 2: Set Generation Defaults
Generation parameters control sampling. The default favours determinism for
tests; relax it for product code that wants creative output.
```elixir
config :jidoka,
default_generation: %{
params: %{
temperature: 0.7,
max_tokens: 1_024
}
}
```
`Jidoka.Config.default_generation/0` normalizes the value through
`Jidoka.Agent.Spec.Generation.from_input/1` and raises with a useful error if
the map is malformed.
### Step 3: Tune The Loop And Timeout Budget
Two integer keys cap a single turn's work.
```elixir
config :jidoka,
default_max_model_turns: 6,
default_turn_timeout_ms: 20_000
```
`default_max_model_turns` is the maximum number of model invocations inside
one tool loop; `default_turn_timeout_ms` is the wall-clock cap for the turn.
Both round-trip through `Jidoka.Config.normalize_positive_integer/2`, which
rejects zero and negative values with a clear error.
### Step 4: Layer Env-Specific Overrides
The standard `config/{dev,test,prod}.exs` layering applies to `:jidoka` like
any other application:
```elixir
# config/dev.exs
import Config
config :jidoka, default_model: "openai:gpt-4o-mini"
```
```elixir
# config/test.exs
import Config
config :jidoka,
default_model: %{provider: :test, id: "model"},
default_generation: %{params: %{temperature: 0.0, max_tokens: 200}},
default_max_model_turns: 4,
default_turn_timeout_ms: 5_000
```
```elixir
# config/prod.exs
import Config
config :jidoka, default_model: "openai:gpt-4o-mini"
```
The `:test` model lets the deterministic test path bypass any real provider
client.
### Step 5: Evaluate Late-Bound Values At Runtime
`config/runtime.exs` runs after release start. Use it to source values from
the environment without baking them into the release artifact:
```elixir
# config/runtime.exs
import Config
if model = System.get_env("JIDOKA_DEFAULT_MODEL") do
config :jidoka, default_model: model
end
if budget = System.get_env("JIDOKA_DEFAULT_MAX_TURNS") do
config :jidoka, default_max_model_turns: String.to_integer(budget)
end
```
### Step 6: Export Provider Credentials Where The Application Can See Them
Jidoka does not read `.env` files. ReqLLM reads `OPENAI_API_KEY` and similar
env vars at call time. In development, export them in the shell:
```bash
export OPENAI_API_KEY=...
export ANTHROPIC_API_KEY=...
```
In production, use the deployment platform's secret manager (systemd
`EnvironmentFile`, Fly secrets, AWS Parameter Store, Kubernetes secrets) so
that the BEAM process sees them at boot. Inside Livebook, prefer the `LB_*`
prefix and mirror with `Jidoka.Kino.load_provider_env/1`. See
[Kino Notebooks](kino-notebooks.md).
## Common Patterns
- **Keep `config.exs` minimal.** The four `:jidoka` keys plus an
`import_config "#{config_env()}.exs"` line is enough for most apps.
- **Pin a deterministic model in `config/test.exs`.** `%{provider: :test, id:
"model"}` keeps tests fully reproducible.
- **Lower the loop budget in tests.** A short `default_max_model_turns`
surfaces runaway loops fast.
- **Treat provider env vars as out-of-band configuration.** Document the
required vars in the host application's README; do not try to encode them
in `:jidoka` keys.
## What Does Not Belong In `:jidoka` Config
- Per-agent overrides. Use the DSL: `model "..."`, `generation %{...}`,
`controls do max_turns ... end`.
- Provider credentials. Use the process environment.
- Per-tenant or per-actor values. Use the runtime context passed to
`Jidoka.turn/3` or `Jidoka.chat/3`.
- Operation source registrations (`ash_resources`, `mcp_endpoints`). Those
belong in the supervision tree (`Jido.MCP.register_endpoint/1`) or the
agent module.
- Live LLM endpoints, request retries, or rate limits. Those live in ReqLLM
config (`config :req_llm, ...`) or in the host's own request pipeline.
## Testing
The configuration helpers are pure functions over `Application.get_env/3`,
so they are easy to test in isolation:
```elixir
defmodule MyApp.JidokaConfigTest do
use ExUnit.Case, async: false
setup do
previous_model = Application.get_env(:jidoka, :default_model)
on_exit(fn ->
if previous_model do
Application.put_env(:jidoka, :default_model, previous_model)
else
Application.delete_env(:jidoka, :default_model)
end
end)
:ok
end
test "model defaults are normalized through ReqLLM" do
Application.put_env(:jidoka, :default_model, "openai:gpt-4o-mini")
assert Jidoka.Config.model_ref(Jidoka.Config.default_model()) == "openai:gpt-4o-mini"
end
test "invalid positive integer raises" do
assert_raise ArgumentError, ~r/invalid default_max_model_turns/, fn ->
Jidoka.Config.normalize_positive_integer!(0, :default_max_model_turns)
end
end
end
```
Avoid `async: true` for tests that mutate `Application` env. The setup above
also captures and restores the previous value so the suite does not leak
state.
## Troubleshooting
| Symptom | Likely Cause | Fix |
| --- | --- | --- |
| `ArgumentError: invalid default_model: ...` | The configured model string is not parseable by ReqLLM. | Use a `provider:id` string or a `%{provider:, id:}` map. |
| `ArgumentError: invalid default_max_model_turns: ...` | The value is zero, negative, or not an integer. | Use a positive integer; strings are parsed when they trim to one. |
| `{:error, :missing_provider_credentials}` from a live turn | The provider env var is not set in the BEAM process. | Export the var before starting the application or set it through your deployment platform. |
| Config changes do not take effect after deploy | `config/config.exs` is compiled into the release. | Move late-bound values to `config/runtime.exs`. |
| Tests interfere with each other on `:jidoka` config | A test mutated `Application.put_env/3` without an `on_exit` restore. | Capture the previous value in `setup` and restore it on exit. |
## Reference
Key modules touched in this guide:
- [`Jidoka.Config`](`Jidoka.Config`) - typed accessors for `default_model/0`,
`default_generation/0`, `default_max_model_turns/0`,
`default_turn_timeout_ms/0`, and the `normalize_*` helpers.
- [`Jidoka.Agent.Spec.Generation`](`Jidoka.Agent.Spec.Generation`) - shape of
the generation map normalized from `default_generation`.
- [`Jidoka`](`Jidoka`) - the facade that reads defaults when a turn does not
override them.
## Related Guides
- [Getting Started](getting-started.md) - the smallest DSL agent end to end.
- [Agent DSL](agent-dsl.md) - per-agent overrides for the four defaults.
- [Errors And Config Reference](errors-and-config-reference.md) - the full
list of error structs and config keys.
- [Jido Process Integration](jido-process-integration.md) - supervising
Jidoka agents alongside the default `Jidoka.Jido` runtime.
- [Kino Notebooks](kino-notebooks.md) - mirroring `LB_*` secrets into
provider env vars for Livebook.