Config

Layered, project-aware configuration for the praisonai CLI

The config command manages layered CLI defaults — global, project, environment, and runtime flags merged into one resolved config.

Diagram

Configuration Precedence (highest wins)

⚡ CLI flags

🌱 Environment variables

📁 Project config

🏠 Global config

📋 Built-in defaults

Quick Start

  1. Set a default model
    bash
    praisonai config set agent.model gpt-4o-mini
  2. Verify resolution
    bash
    praisonai config show

Usage

bash
praisonai config [OPTIONS] COMMAND [ARGS]...

Commands

CommandDescription
showShow fully resolved configuration
sourcesList configuration layers in precedence order
provenance [KEY]Show the resolved value of each key and which layer/file supplied it, including a synthetic run.model key that reports the model a bare run/chat/code will actually dispatch
validate [FILE]Validate YAML syntax and schema
listList configuration values (`–scope all\user\project`)
get KEYGet a value by dotted path (e.g. agent.model)
set KEY VALUESet a value (`–scope user\project`)
resetReset config to defaults (`–scope user\project`)
pathShow config file path
envShow registered environment variables
doctorRun configuration diagnostics

show

bash
praisonai config show                          # YAML (default)
praisonai config show --format json            # JSON output
praisonai config show --format table           # Flat key=value table
praisonai config show --sources                # Include contributing layers

validate

Validates discovered config files against the schema, catching typos with difflib-based suggestions.

bash
praisonai config validate                          # Validate discovered configs (warn-by-default)
praisonai config validate .praisonai/config.yaml   # Validate a specific file
praisonai config validate --strict-config          # Treat warnings as errors (exit 1)
praisonai config validate --json                   # Machine-readable output

Example output for a typo:

⚠ Unknown config key 'temprature' in .praisonai/config.yaml (section: agent). Did you mean 'temperature'?
⚠ Configuration loaded with 1 warning(s).
ℹ Use --strict-config (or PRAISONAI_STRICT_CONFIG=1) to fail on these.
FlagTypeDefaultBehaviour
--strict-config / --strictboolFalseTreat unknown/typo keys as errors (exit 1)
--jsonboolFalseEmit {valid, warnings, sources, hint} JSON

sources

bash
praisonai config sources

Prints the five-layer hierarchy and which global/project/env sources are currently active.

provenance

bash
praisonai config provenance                     # all keys, text output
praisonai config provenance agent.model         # one key
praisonai config provenance agent.model --json  # machine-readable
praisonai config provenance run.model           # the model a bare run will use

Prints the reconciled resolved view: every key that survived the deep-merge, its winning value, its layer (defaults / global / project / environment / cli), and the absolute path of the file that supplied it ((runtime) for env / CLI / built-in defaults). It also adds a synthetic run.model key (below) which may report the extra model-resolver layer.

Reach for this when praisonai config show returns a value you did not expect and you need to find which file set it.

Single key, text output:

$ praisonai config provenance agent.model
agent.model = gpt-4o-mini
  layer: project  source: /home/alice/proj/.praisonai/config.yaml

All keys, text output:

$ praisonai config provenance
Resolved configuration (per-key provenance):

  agent.model = gpt-4o-mini
      project · /home/alice/proj/.praisonai/config.yaml
  agent.temperature = 0.7
      defaults · (defaults)
  telemetry = true
      global · /home/alice/.praisonai/config.yaml

JSON output (via the global --json flag):

json
{
  "agent.model": {
    "value": "gpt-4o-mini",
    "layer": "project",
    "source": "/home/alice/proj/.praisonai/config.yaml"
  },
  "agent.temperature": {
    "value": 0.7,
    "layer": "defaults",
    "source": null
  }
}

A missing key exits 1 with Key not found: <key>.

The run.model key

run.model reports the model that a bare praisonai run, praisonai chat, or praisonai code would actually dispatch — accounting for both the config layers above and the separate model-resolution chain used when no agent.model is set.

Diagram

Yes

No

praisonai config provenance run.model

agent.model configured?

Mirror agent.model
same value, layer, source

Model resolver chain

layer: model-resolver
source: winning rung

When the model-resolver layer wins, source tells you which rung of the default model ladder picked the model:

sourceMeaning
explicitModel passed explicitly — only from explain_default_model(explicit=...) in Python; config provenance never reports it
state:<path>Recent user-chosen model persisted at <state_dir>/model.json
env:MODEL_NAMEMODEL_NAME env var is set
env:OPENAI_MODEL_NAMEOPENAI_MODEL_NAME env var is set
localNo cloud credential; a reachable local OpenAI-compatible server (e.g. Ollama) was detected
provider:<ENV_VAR>A provider credential (e.g. provider:ANTHROPIC_API_KEY) is present; its default model was picked
fallbackNone of the above; the terminal fallback (gpt-4o-mini) was picked

Configured in a project file:

$ praisonai config provenance run.model
run.model = gpt-4o-mini
  layer: project  source: /home/alice/proj/.praisonai/config.yaml

Set via MODEL_NAME (the environment layer feeds agent.model, exactly as run sees it):

$ MODEL_NAME=gpt-env praisonai config provenance run.model
run.model = gpt-env
  layer: environment  source: (runtime)

Nothing configured, Ollama running locally:

$ praisonai config provenance run.model
run.model = ollama/llama3.2
  layer: model-resolver  source: local

JSON output:

json
{
  "key": "run.model",
  "value": "anthropic/claude-3-5-sonnet-latest",
  "layer": "model-resolver",
  "source": "provider:ANTHROPIC_API_KEY"
}

list

bash
praisonai config list                     # resolved, merged config (default: --scope all)
praisonai config list --scope user        # only what ~/.praisonai/config contributes
praisonai config list --scope project     # only what ./.praisonai/config contributes
ScopeShows
all (default)The resolved, deep-merged configuration across all layers
userOnly the global user config file’s contribution
projectOnly this project’s config file contribution

set / get / reset / path

bash
praisonai config set agent.model gpt-4o-mini
praisonai config set agent.temperature 0.3 --scope project
praisonai config get agent.model
praisonai config reset --scope project -y
praisonai config path --scope user
ScopeFilePermissions
user (default)~/.praisonai/config.yaml0600
user (default)~/.praisonai/config.toml0644
project./.praisonai/config.yaml0644

praisonai config set / praisonai config reset write TOML to the canonical ~/.praisonai/config.toml. Both the canonical home and the config.yaml written by setup / init honour PRAISONAI_HOME — see CLI Home Root.


Strict Validation

Unknown or mistyped keys are reported with the closest valid match. By default they are warnings — your valid keys still apply. Opt into strict mode to fail-fast.

ModeTriggerBehaviour
Warn (default)—Logs UserWarning per unknown key, run continues
Strict (per-call)--strict-config on config validate or ConfigResolver(strict=True)Raises ValueError on first unknown key
Strict (global)PRAISONAI_STRICT_CONFIG=1 env varAll resolver calls run in strict mode
bash
# Catch typos in CI:
PRAISONAI_STRICT_CONFIG=1 praisonai config validate

# Or per-call:
praisonai config validate --strict-config

The validator inspects the raw discovered config (before normalisation through ResolvedConfig), so nested mistakes like agent.temprature surface instead of being silently dropped.


Configuration File

Primary format is YAML at ~/.praisonai/config.yaml (global) or ./.praisonai/config.yaml (project):

yaml
agent:
  model: gpt-4o-mini
  temperature: 0.7
  max_tokens: 16000
  stream: true

mcp:
  servers:
    playwright:
      command: ["npx", "-y", "@playwright/mcp"]

permissions:
  default: ask
  rules:
    - { pattern: "bash:git *", action: allow }
    - { pattern: "bash:rm *",  action: deny }

output:
  verbose: false
  color: true

telemetry: true

Project discovery

From cwd, the resolver walks up to the git root or $HOME (whichever comes first) searching:

  1. .praisonai/config.yaml
  2. .praisonai/config.yml
  3. praisonai.yaml / praisonai.yml (canonical project-root config, added in PR #3422)
  4. praison.yaml / praison.yml (legacy fallback, discovered for backward compat)
  5. .praison/config.toml (legacy global; loaded exclusively as global: when at $HOME)

At $HOME the legacy .praison/config.toml name is skipped — it is loaded exactly once as global: by the global loader (see Walk-up Boundaries).


Value Interpolation

Every value in every discovered config file is interpolated at load time, so secrets and reused prompt bodies can live outside your tracked config.

Diagram

Value Interpolation

📄 config.yaml

🔧 _read_config_file

✨ interpolate

${VAR} / {env:…}

{file:./path}

✅ Resolved value

DirectiveMeaningIf the value is missing
${VAR}Legacy environment variable substitutionDirective kept literally when unset
{env:VAR}Environment variable, no defaultDirective kept literally when unset
{env:VAR:-default}Environment variable with shell-style fallback (:-)Returns default (may be empty)
{file:./path}Read the referenced file’s contents into the value (trailing newline stripped); path resolves relative to the directory of the config fileDirective kept literally on read failure or security refusal

All four in one file:

yaml
agent:
  model: ${PRAISONAI_MODEL}                  # legacy shell form
  provider: "{env:PRAISONAI_PROVIDER:-openai}"  # with default
  system_prompt: "{file:./prompts/system.md}"   # file include

telemetry: "{env:PRAISONAI_TELEMETRY:-true}"

From Python

python
from praisonai_code.cli.configuration.resolver import ConfigResolver

provenance = ConfigResolver().resolve_with_provenance()
print(provenance["agent.model"])
# {'value': 'gpt-4o-mini', 'layer': 'project',
#  'source': '/home/alice/proj/.praisonai/config.yaml'}

To see which model a bare run would pick (and why) when agent.model is unset:

python
from praisonai_code.cli.configuration.model_resolver import explain_default_model

model, source = explain_default_model()
print(model, source)
# e.g. "anthropic/claude-3-5-sonnet-latest provider:ANTHROPIC_API_KEY"

explain_default_model() is side-effect free — it never persists a model or prints a notice.


agent.* Schema

KeyTypeDefaultDescription
agent.modelstr—Default model; if unset, resolved via the provider-aware ladder rather than a hardcoded OpenAI default
agent.providerstr—Provider name
agent.base_urlstr—Custom API base URL
agent.toolslist[str][]Default tool names
agent.toolsetstr—Named toolset
agent.default_agentstr—Default agent slug
agent.memorybool/dict—Memory config
agent.streambooltrueStream responses
agent.temperaturefloat0.7Sampling temperature
agent.max_tokensint16000Max output tokens

Store API keys via env vars or praisonai auth — api_key is never written to YAML.


mcp.servers.<name> Schema

KeyTypeDefaultDescription
commandlist[str] or str—Local (stdio) server launch command. List form preferred.
argslist[str][]Extra args appended to command.
envdict[str, str]{}Env vars for the server process.
enabledbooltrueSet false to declare-but-not-wire a server.
type"remote"—Marks a remote server; skipped by the run command-string path.
urlstr—Remote endpoint; presence implies remote.

permissions.* Schema

KeyTypeDefaultDescription
default"allow" \"deny" \"ask"—Fallback action when no rule matches.
ruleslist[{pattern, action}][]Structured rule list. action must be allow, deny, or ask.
<pattern>"allow" \"deny" \"ask"—Flat shorthand — same shape as --allow/--deny produces.

Environment Variables

VariableMaps to
MODEL_NAME / OPENAI_MODEL_NAME / PRAISONAI_MODELagent.model
PRAISONAI_PROVIDERagent.provider
OPENAI_BASE_URL / OPENAI_API_BASE / PRAISONAI_BASE_URLagent.base_url
PRAISONAI_OUTPUT_FORMAToutput.format
PRAISONAI_COLORoutput.color
PRAISONAI_VERBOSEoutput.verbose
PRAISONAI_QUIEToutput.quiet
PRAISONAI_TELEMETRYtelemetry
OPENAI_API_KEY / ANTHROPIC_API_KEYSecrets (env only)

Legacy Formats

Legacy paths still work and are migrated on read:

PathStatus
~/.praison/config.tomlRead-only legacy fallback — migrated to agent. / rag.; resolved only if the canonical ~/.praisonai/config.yaml is absent
~/.praisonai/.envMerged for model/provider when no YAML
.praison/config.toml (project)Found via walk-up discovery

TOML [rules], [output], [mcp], [traces], and [session] sections remain valid in legacy files. New projects should use YAML.


See Also