From 169355b591f6006362dc4cb82fba45cddaf4752f Mon Sep 17 00:00:00 2001 From: Lasmar Khalifa Date: Fri, 26 Jun 2026 16:00:45 -0400 Subject: [PATCH] Add env variable override for the default agent provider --- README.md | 4 +- .../comments/doc-comments-external.md | 8 ++-- .../documentation/comments/doc-comments.md | 3 +- lib/roast/cogs/agent/config.rb | 21 +++++++-- sorbet/rbi/shims/lib/roast/config_context.rbi | 2 +- test/roast/cogs/agent/config_test.rb | 44 ++++++++++++++++++- tutorial/02_chaining_cogs/README.md | 2 +- 7 files changed, 71 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index 08ba0751..c18e1954 100644 --- a/README.md +++ b/README.md @@ -74,7 +74,7 @@ gem 'roast-ai' ## Provider Configuration -Roast provider settings are configured in workflow `config` blocks. There is not currently a CLI flag or environment variable that changes the default provider globally; edit the workflow config to select a different provider. +Roast provider settings are configured in workflow `config` blocks. To change the default **agent** provider without editing each workflow, set the `ROAST_DEFAULT_AGENT_PROVIDER` environment variable (e.g. `export ROAST_DEFAULT_AGENT_PROVIDER=claude`). A `provider` set in a workflow's `config` always takes precedence over this variable, and an invalid value raises an error. This affects the `agent` cog only; the `chat` cog's provider is unaffected. ### Chat cog @@ -97,7 +97,7 @@ end ### Agent cog -The `agent` cog runs local agent CLIs. It defaults to `:pi` and currently supports: +The `agent` cog runs local agent CLIs. It defaults to `:pi` (override globally with `ROAST_DEFAULT_AGENT_PROVIDER`) and currently supports: - `:claude` - Claude Code CLI - `:pi` - Pi CLI diff --git a/internal/documentation/comments/doc-comments-external.md b/internal/documentation/comments/doc-comments-external.md index 1b27dbb7..5de98db1 100644 --- a/internal/documentation/comments/doc-comments-external.md +++ b/internal/documentation/comments/doc-comments-external.md @@ -165,7 +165,8 @@ shortened names can be confusing. Use the complete module path. ```ruby # Configure the cog to use the default provider when invoking an agent # -# The default provider used by Roast is Pi (`:pi`). +# The default provider is the one named by the `ROAST_DEFAULT_AGENT_PROVIDER` environment variable, +# or Pi (`:pi`) when that variable is unset. #: () -> void def use_default_provider! @values[:provider] = nil @@ -386,7 +387,8 @@ end # Configure the cog to use a specified provider when invoking an agent # # The provider is the source of the agent tool itself. -# If no provider is specified, Pi (`:pi`) will be used as the default provider. +# If no provider is specified, Roast uses the provider named by the `ROAST_DEFAULT_AGENT_PROVIDER` +# environment variable, or Pi (`:pi`) when that variable is unset. # # A provider must be properly installed on your system in order for Roast to be able to use it. # @@ -519,7 +521,7 @@ Methods in `config_context.rbi` expose cog configuration interfaces and are the # # #### Configure the LLM provider # - `provider(symbol)` - Set the agent provider (e.g., `:claude`) -# - `use_default_provider!` - Use the default provider (`:pi`) +# - `use_default_provider!` - Use the default provider (`:pi`, or `$ROAST_DEFAULT_AGENT_PROVIDER` when set) # # #### Configure the base command used to run the coding agent # - `command(string_or_array)` - Set the base command for invoking the agent diff --git a/internal/documentation/comments/doc-comments.md b/internal/documentation/comments/doc-comments.md index b758faa1..847c8e9d 100644 --- a/internal/documentation/comments/doc-comments.md +++ b/internal/documentation/comments/doc-comments.md @@ -70,7 +70,8 @@ These files provide the primary interface between users and Roast. The documenta # Configure the cog to use a specified provider when invoking an agent # # The provider is the source of the agent tool itself. -# If no provider is specified, Pi (`:pi`) will be used as the default provider. +# If no provider is specified, Roast uses the provider named by the `ROAST_DEFAULT_AGENT_PROVIDER` +# environment variable, or Pi (`:pi`) when that variable is unset. # # A provider must be properly installed on your system in order for Roast to be able to use it. # diff --git a/lib/roast/cogs/agent/config.rb b/lib/roast/cogs/agent/config.rb index d52d5e90..e41fbc0d 100644 --- a/lib/roast/cogs/agent/config.rb +++ b/lib/roast/cogs/agent/config.rb @@ -7,10 +7,19 @@ class Agent < Cog class Config < Cog::Config VALID_PROVIDERS = [:pi, :claude].freeze #: Array[Symbol] + # Environment variable that overrides the built-in default agent provider. + # + # When an agent cog does not explicitly configure a provider, Roast uses the provider named by + # this variable, falling back to the built-in default (`VALID_PROVIDERS.first`, i.e. `:pi`) when it + # is unset or blank. The value is normalized (surrounding whitespace stripped, then downcased) + # before lookup, and an explicit `provider` configured on the cog always takes precedence over it. + DEFAULT_PROVIDER_ENV_VAR = "ROAST_DEFAULT_AGENT_PROVIDER" #: String + # Configure the cog to use a specified provider when invoking an agent # # The provider is the source of the agent tool itself. - # If no provider is specified, Pi (`:pi`) will be used as the default provider. + # If no provider is specified, Roast uses the provider named by the `ROAST_DEFAULT_AGENT_PROVIDER` + # environment variable, or Pi (`:pi`) when that variable is unset. # # A provider must be properly installed on your system in order for Roast to be able to use it. # @@ -24,7 +33,8 @@ def provider(provider) # Configure the cog to use the default provider when invoking an agent # - # The default provider used by Roast is Pi (`:pi`). + # The default provider is the one named by the `ROAST_DEFAULT_AGENT_PROVIDER` environment variable, + # or Pi (`:pi`) when that variable is unset. # # The provider must be properly installed on your system in order for Roast to be able to use it. # @@ -38,6 +48,10 @@ def use_default_provider! # Get the validated provider name that the cog is configured to use when invoking an agent # + # The provider is resolved in order of precedence: the provider explicitly configured on the cog, + # then the `ROAST_DEFAULT_AGENT_PROVIDER` environment variable (normalized by stripping surrounding + # whitespace and downcasing), then the built-in default (`VALID_PROVIDERS.first`, i.e. `:pi`). + # # Note: this method will return the name of a valid provider or raise an `InvalidConfigError`. # It will __not__, however, validate that the agent is properly installed on your system. # If the agent is not properly installed, you will likely experience a failure when Roast attempts to @@ -49,7 +63,8 @@ def use_default_provider! # #: () -> Symbol def valid_provider! - provider = @values[:provider] || VALID_PROVIDERS.first + env_default = ENV[DEFAULT_PROVIDER_ENV_VAR].presence&.strip&.downcase&.to_sym + provider = @values[:provider] || env_default || VALID_PROVIDERS.first unless VALID_PROVIDERS.include?(provider) raise InvalidConfigError, "'#{provider}' is not a valid provider. Available providers include: #{VALID_PROVIDERS.join(", ")}" end diff --git a/sorbet/rbi/shims/lib/roast/config_context.rbi b/sorbet/rbi/shims/lib/roast/config_context.rbi index 7665bfe7..14d8b1a8 100644 --- a/sorbet/rbi/shims/lib/roast/config_context.rbi +++ b/sorbet/rbi/shims/lib/roast/config_context.rbi @@ -141,7 +141,7 @@ module Roast # # #### Configure the agent provider # - `provider(symbol)` - Set the agent provider (e.g., `:claude`) - # - `use_default_provider!` - Use the default provider (`:pi`) + # - `use_default_provider!` - Use the default provider (`:pi`, or `$ROAST_DEFAULT_AGENT_PROVIDER` when set) # # #### Configure the base command used to run the coding agent # - `command(string_or_array)` - Set the base command for invoking the agent diff --git a/test/roast/cogs/agent/config_test.rb b/test/roast/cogs/agent/config_test.rb index 80c27762..60396b7b 100644 --- a/test/roast/cogs/agent/config_test.rb +++ b/test/roast/cogs/agent/config_test.rb @@ -21,11 +21,15 @@ def setup @config.provider(:fake_provider) @config.use_default_provider! - assert_equal @default_provider, @config.valid_provider! + with_env(Agent::Config::DEFAULT_PROVIDER_ENV_VAR, nil) do + assert_equal @default_provider, @config.valid_provider! + end end test "valid_provider! returns default when not set" do - assert_equal @default_provider, @config.valid_provider! + with_env(Agent::Config::DEFAULT_PROVIDER_ENV_VAR, nil) do + assert_equal @default_provider, @config.valid_provider! + end end test "valid_provider! raises on invalid provider" do @@ -38,6 +42,42 @@ def setup assert_match(/invalid_provider.*not a valid provider/, error.message) end + test "valid_provider! uses ROAST_DEFAULT_AGENT_PROVIDER when no provider is configured" do + with_env(Agent::Config::DEFAULT_PROVIDER_ENV_VAR, "claude") do + assert_equal :claude, @config.valid_provider! + end + end + + test "explicitly configured provider takes precedence over ROAST_DEFAULT_AGENT_PROVIDER" do + @config.provider(:claude) + + with_env(Agent::Config::DEFAULT_PROVIDER_ENV_VAR, "pi") do + assert_equal :claude, @config.valid_provider! + end + end + + test "valid_provider! falls back to the built-in default when ROAST_DEFAULT_AGENT_PROVIDER is blank" do + with_env(Agent::Config::DEFAULT_PROVIDER_ENV_VAR, "") do + assert_equal @default_provider, @config.valid_provider! + end + end + + test "valid_provider! normalizes ROAST_DEFAULT_AGENT_PROVIDER casing and surrounding whitespace" do + with_env(Agent::Config::DEFAULT_PROVIDER_ENV_VAR, " CLAUDE ") do + assert_equal :claude, @config.valid_provider! + end + end + + test "valid_provider! raises when ROAST_DEFAULT_AGENT_PROVIDER names an invalid provider" do + with_env(Agent::Config::DEFAULT_PROVIDER_ENV_VAR, "unknown_provider") do + error = assert_raises(Cog::Config::InvalidConfigError) do + @config.valid_provider! + end + + assert_match(/unknown_provider.*not a valid provider/, error.message) + end + end + # Command configuration tests test "command sets command value" do @config.command("test-command") diff --git a/tutorial/02_chaining_cogs/README.md b/tutorial/02_chaining_cogs/README.md index b7d2b074..a2916190 100644 --- a/tutorial/02_chaining_cogs/README.md +++ b/tutorial/02_chaining_cogs/README.md @@ -127,7 +127,7 @@ execute do end ``` -The `agent` cog is backed by a locally installed coding agent -- Pi is the default provider. +The `agent` cog is backed by a locally installed coding agent -- Pi is the default provider (set `ROAST_DEFAULT_AGENT_PROVIDER` to change it globally, e.g. to `claude`). You'll need to have Pi installed and configured correctly for this cog to run. ### Specifying a Model