Stringly typed config (#5463)

This commit is contained in:
Jack Amadeo
2025-10-30 15:13:32 -04:00
committed by GitHub
parent 1714990d42
commit 2970b5fa34
30 changed files with 243 additions and 275 deletions
+2 -2
View File
@@ -101,11 +101,11 @@ impl GooseAcpAgent {
let config = Config::global();
let provider_name: String = config
.get_param("GOOSE_PROVIDER")
.get_goose_provider()
.map_err(|e| anyhow::anyhow!("No provider configured: {}", e))?;
let model_name: String = config
.get_param("GOOSE_MODEL")
.get_goose_model()
.map_err(|e| anyhow::anyhow!("No model configured: {}", e))?;
let model_config = goose::model::ModelConfig {
+42 -56
View File
@@ -14,7 +14,8 @@ use goose::config::paths::Paths;
use goose::config::permission::PermissionLevel;
use goose::config::signup_tetrate::TetrateAuth;
use goose::config::{
configure_tetrate, Config, ConfigError, ExperimentManager, ExtensionEntry, PermissionManager,
configure_tetrate, Config, ConfigError, ExperimentManager, ExtensionEntry, GooseMode,
PermissionManager,
};
use goose::conversation::message::Message;
use goose::model::ModelConfig;
@@ -421,7 +422,7 @@ fn select_model_from_list(
}
fn try_store_secret(config: &Config, key_name: &str, value: String) -> anyhow::Result<bool> {
match config.set_secret(key_name, Value::String(value)) {
match config.set_secret(key_name, &value) {
Ok(_) => Ok(true),
Err(e) => {
cliclack::outro(style(format!(
@@ -450,7 +451,7 @@ pub async fn configure_provider_dialog() -> anyhow::Result<bool> {
.collect();
// Get current default provider if it exists
let current_provider: Option<String> = config.get_param("GOOSE_PROVIDER").ok();
let current_provider: Option<String> = config.get_goose_provider().ok();
let default_provider = current_provider.unwrap_or_default();
// Select provider
@@ -487,7 +488,7 @@ pub async fn configure_provider_dialog() -> anyhow::Result<bool> {
return Ok(false);
}
} else {
config.set_param(&key.name, Value::String(env_value))?;
config.set_param(&key.name, &env_value)?;
}
let _ = cliclack::log::info(format!("Saved {} to {}", key.name, config.path()));
}
@@ -529,7 +530,7 @@ pub async fn configure_provider_dialog() -> anyhow::Result<bool> {
return Ok(false);
}
} else {
config.set_param(&key.name, Value::String(value))?;
config.set_param(&key.name, &value)?;
}
}
}
@@ -558,9 +559,9 @@ pub async fn configure_provider_dialog() -> anyhow::Result<bool> {
};
if key.secret {
config.set_secret(&key.name, Value::String(value))?;
config.set_secret(&key.name, &value)?;
} else {
config.set_param(&key.name, Value::String(value))?;
config.set_param(&key.name, &value)?;
}
}
}
@@ -648,9 +649,8 @@ pub async fn configure_provider_dialog() -> anyhow::Result<bool> {
match result {
Ok((_message, _usage)) => {
// Update config with new values only if the test succeeds
config.set_param("GOOSE_PROVIDER", Value::String(provider_name.to_string()))?;
config.set_param("GOOSE_MODEL", Value::String(model.clone()))?;
config.set_goose_provider(provider_name)?;
config.set_goose_model(&model)?;
print_config_file_saved()?;
Ok(true)
}
@@ -877,7 +877,7 @@ pub fn configure_extensions_dialog() -> anyhow::Result<()> {
// Try to store in keychain
let keychain_key = key.to_string();
match config.set_secret(&keychain_key, Value::String(value.clone())) {
match config.set_secret(&keychain_key, &value) {
Ok(_) => {
// Successfully stored in keychain, add to env_keys
env_keys.push(keychain_key);
@@ -973,7 +973,7 @@ pub fn configure_extensions_dialog() -> anyhow::Result<()> {
// Try to store in keychain
let keychain_key = key.to_string();
match config.set_secret(&keychain_key, Value::String(value.clone())) {
match config.set_secret(&keychain_key, &value) {
Ok(_) => {
// Successfully stored in keychain, add to env_keys
env_keys.push(keychain_key);
@@ -1093,7 +1093,7 @@ pub fn configure_extensions_dialog() -> anyhow::Result<()> {
// Try to store in keychain
let keychain_key = key.to_string();
match config.set_secret(&keychain_key, Value::String(value.clone())) {
match config.set_secret(&keychain_key, &Value::String(value.clone())) {
Ok(_) => {
// Successfully stored in keychain, add to env_keys
env_keys.push(keychain_key);
@@ -1273,46 +1273,35 @@ pub fn configure_goose_mode_dialog() -> anyhow::Result<()> {
let mode = cliclack::select("Which goose mode would you like to configure?")
.item(
"auto",
GooseMode::Auto,
"Auto Mode",
"Full file modification, extension usage, edit, create and delete files freely"
)
.item(
"approve",
GooseMode::Approve,
"Approve Mode",
"All tools, extensions and file modifications will require human approval"
)
.item(
"smart_approve",
GooseMode::SmartApprove,
"Smart Approve Mode",
"Editing, creating, deleting files and using extensions will require human approval"
)
.item(
"chat",
GooseMode::Chat,
"Chat Mode",
"Engage with the selected provider without using tools, extensions, or file modification"
)
.interact()?;
match mode {
"auto" => {
config.set_param("GOOSE_MODE", Value::String("auto".to_string()))?;
cliclack::outro("Set to Auto Mode - full file modification enabled")?;
}
"approve" => {
config.set_param("GOOSE_MODE", Value::String("approve".to_string()))?;
cliclack::outro("Set to Approve Mode - all tools and modifications require approval")?;
}
"smart_approve" => {
config.set_param("GOOSE_MODE", Value::String("smart_approve".to_string()))?;
cliclack::outro("Set to Smart Approve Mode - modifications require approval")?;
}
"chat" => {
config.set_param("GOOSE_MODE", Value::String("chat".to_string()))?;
cliclack::outro("Set to Chat Mode - no tools or modifications enabled")?;
}
_ => unreachable!(),
config.set_goose_mode(mode)?;
let msg = match mode {
GooseMode::Auto => "Set to Auto Mode - full file modification enabled",
GooseMode::Approve => "Set to Approve Mode - all tools and modifications require approval",
GooseMode::SmartApprove => "Set to Smart Approve Mode - modifications require approval",
GooseMode::Chat => "Set to Chat Mode - no tools or modifications enabled",
};
cliclack::outro(msg)?;
Ok(())
}
@@ -1321,28 +1310,25 @@ pub fn configure_goose_router_strategy_dialog() -> anyhow::Result<()> {
let enable_router = cliclack::select("Would you like to enable smart tool routing?")
.item(
"true",
true,
"Enable Router",
"Use LLM-based intelligence to select tools",
)
.item(
"false",
false,
"Disable Router",
"Use the default tool selection strategy",
)
.interact()?;
match enable_router {
"true" => {
config.set_param("GOOSE_ENABLE_ROUTER", Value::String("true".to_string()))?;
cliclack::outro("Router enabled - using LLM-based intelligence for tool selection")?;
}
"false" => {
config.set_param("GOOSE_ENABLE_ROUTER", Value::String("false".to_string()))?;
cliclack::outro("Router disabled - using default tool selection")?;
}
_ => unreachable!(),
config.set_param("GOOSE_ENABLE_ROUTER", enable_router)?;
let msg = if enable_router {
"Router enabled - using LLM-based intelligence for tool selection"
} else {
"Router disabled - using default tool selection"
};
cliclack::outro(msg)?;
Ok(())
}
@@ -1360,15 +1346,15 @@ pub fn configure_tool_output_dialog() -> anyhow::Result<()> {
match tool_log_level {
"high" => {
config.set_param("GOOSE_CLI_MIN_PRIORITY", Value::from(0.8))?;
config.set_param("GOOSE_CLI_MIN_PRIORITY", 0.8)?;
cliclack::outro("Showing tool output of high importance only.")?;
}
"medium" => {
config.set_param("GOOSE_CLI_MIN_PRIORITY", Value::from(0.2))?;
config.set_param("GOOSE_CLI_MIN_PRIORITY", 0.2)?;
cliclack::outro("Showing tool output of medium importance.")?;
}
"all" => {
config.set_param("GOOSE_CLI_MIN_PRIORITY", Value::from(0.0))?;
config.set_param("GOOSE_CLI_MIN_PRIORITY", 0.0)?;
cliclack::outro("Showing all tool output.")?;
}
_ => unreachable!(),
@@ -1441,11 +1427,11 @@ pub async fn configure_tool_permissions_dialog() -> anyhow::Result<()> {
let config = Config::global();
let provider_name: String = config
.get_param("GOOSE_PROVIDER")
.get_goose_provider()
.expect("No provider configured. Please set model provider first");
let model: String = config
.get_param("GOOSE_MODEL")
.get_goose_model()
.expect("No model configured. Please set model first");
let model_config = ModelConfig::new(&model)?;
@@ -1591,7 +1577,7 @@ fn configure_recipe_dialog() -> anyhow::Result<()> {
if input_value.clone().trim().is_empty() {
config.delete(key_name)?;
} else {
config.set_param(key_name, Value::String(input_value))?;
config.set_param(key_name, &input_value)?;
}
Ok(())
}
@@ -1618,7 +1604,7 @@ pub fn configure_max_turns_dialog() -> anyhow::Result<()> {
.interact()?;
let max_turns: u32 = max_turns_input.parse()?;
config.set_param("GOOSE_MAX_TURNS", Value::from(max_turns))?;
config.set_param("GOOSE_MAX_TURNS", max_turns)?;
cliclack::outro(format!(
"Set maximum turns to {} - goose will ask for input after {} consecutive actions",
@@ -1651,7 +1637,7 @@ pub async fn handle_openrouter_auth() -> anyhow::Result<()> {
// Test configuration - get the model that was configured
println!("\nTesting configuration...");
let configured_model: String = config.get_param("GOOSE_MODEL")?;
let configured_model: String = config.get_goose_model()?;
let model_config = match goose::model::ModelConfig::new(&configured_model) {
Ok(config) => config,
Err(e) => {
@@ -1729,7 +1715,7 @@ pub async fn handle_tetrate_auth() -> anyhow::Result<()> {
// Test configuration
println!("\nTesting configuration...");
let configured_model: String = config.get_param("GOOSE_MODEL")?;
let configured_model: String = config.get_goose_model()?;
let model_config = match goose::model::ModelConfig::new(&configured_model) {
Ok(config) => config,
Err(e) => {
+2 -2
View File
@@ -139,7 +139,7 @@ pub async fn handle_web(
let config = goose::config::Config::global();
let provider_name: String = match config.get_param("GOOSE_PROVIDER") {
let provider_name: String = match config.get_goose_provider() {
Ok(p) => p,
Err(_) => {
eprintln!("No provider configured. Run 'goose configure' first");
@@ -147,7 +147,7 @@ pub async fn handle_web(
}
};
let model: String = match config.get_param("GOOSE_MODEL") {
let model: String = match config.get_goose_model() {
Ok(m) => m,
Err(_) => {
eprintln!("No model configured. Run 'goose configure' first");
+1 -2
View File
@@ -11,7 +11,6 @@ use goose::recipe::build_recipe::{
};
use goose::recipe::validate_recipe::parse_and_validate_parameters;
use goose::recipe::Recipe;
use serde_json::Value;
fn create_user_prompt_callback() -> impl Fn(&str, &str) -> Result<String> {
|key: &str, description: &str| -> Result<String> {
@@ -98,7 +97,7 @@ pub fn collect_missing_secrets(requirements: &[SecretRequirement]) -> Result<()>
.unwrap_or_else(|_| String::new());
if !value.trim().is_empty() {
config.set_secret(&req.key, Value::String(value))?;
config.set_secret(&req.key, &value)?;
println!("✅ Secret stored securely for {}", req.extension_name);
} else {
println!("⏭️ Skipped {} for {}", req.key, req.extension_name);
+2 -2
View File
@@ -208,7 +208,7 @@ pub async fn build_session(session_config: SessionBuilderConfig) -> CliSession {
.as_ref()
.and_then(|s| s.goose_provider.clone())
})
.or_else(|| config.get_param("GOOSE_PROVIDER").ok())
.or_else(|| config.get_goose_provider().ok())
.expect("No provider configured. Run 'goose configure' first");
let model_name = session_config
@@ -219,7 +219,7 @@ pub async fn build_session(session_config: SessionBuilderConfig) -> CliSession {
.as_ref()
.and_then(|s| s.goose_model.clone())
})
.or_else(|| config.get_param("GOOSE_MODEL").ok())
.or_else(|| config.get_goose_model().ok())
.expect("No model configured. Run 'goose configure' first");
let temperature = session_config.settings.as_ref().and_then(|s| s.temperature);
+22 -29
View File
@@ -12,6 +12,7 @@ use crate::session::task_execution_display::{
};
use goose::conversation::Conversation;
use std::io::Write;
use std::str::FromStr;
pub use self::export::message_to_markdown;
pub use builder::{build_session, SessionBuilderConfig, SessionSettings};
@@ -28,7 +29,7 @@ use completion::GooseCompleter;
use goose::agents::extension::{Envs, ExtensionConfig};
use goose::agents::types::RetryConfig;
use goose::agents::{Agent, SessionConfig};
use goose::config::Config;
use goose::config::{Config, GooseMode};
use goose::providers::pricing::initialize_pricing_cache;
use goose::session::SessionManager;
use input::InputResult;
@@ -545,21 +546,18 @@ impl CliSession {
save_history(&mut editor);
let config = Config::global();
let mode = mode.to_lowercase();
// Check if mode is valid
if !["auto", "approve", "chat", "smart_approve"].contains(&mode.as_str()) {
output::render_error(&format!(
"Invalid mode '{}'. Mode must be one of: auto, approve, chat",
mode
));
continue;
}
config
.set_param("GOOSE_MODE", Value::String(mode.to_string()))
.unwrap();
output::goose_mode_message(&format!("Goose mode set to '{}'", mode));
let mode = match GooseMode::from_str(&mode.to_lowercase()) {
Ok(mode) => mode,
Err(_) => {
output::render_error(&format!(
"Invalid mode '{}'. Mode must be one of: auto, approve, chat, smart_approve",
mode
));
continue;
}
};
config.set_goose_mode(mode)?;
output::goose_mode_message(&format!("Goose mode set to '{:?}'", mode));
continue;
}
input::InputResult::Plan(options) => {
@@ -787,12 +785,9 @@ impl CliSession {
self.run_mode = RunMode::Normal;
// set goose mode: auto if that isn't already the case
let config = Config::global();
let curr_goose_mode =
config.get_param("GOOSE_MODE").unwrap_or("auto".to_string());
if curr_goose_mode != "auto" {
config
.set_param("GOOSE_MODE", Value::String("auto".to_string()))
.unwrap();
let curr_goose_mode = config.get_goose_mode().unwrap_or(GooseMode::Auto);
if curr_goose_mode != GooseMode::Auto {
config.set_goose_mode(GooseMode::Auto).unwrap();
}
// clear the messages before acting on the plan
@@ -807,10 +802,8 @@ impl CliSession {
output::hide_thinking();
// Reset run & goose mode
if curr_goose_mode != "auto" {
config
.set_param("GOOSE_MODE", Value::String(curr_goose_mode.to_string()))
.unwrap();
if curr_goose_mode != GooseMode::Auto {
config.set_goose_mode(curr_goose_mode)?;
}
} else {
// add the plan response (assistant message) & carry the conversation forward
@@ -1324,7 +1317,7 @@ impl CliSession {
.unwrap_or(false);
let provider_name = config
.get_param::<String>("GOOSE_PROVIDER")
.get_goose_provider()
.unwrap_or_else(|_| "unknown".to_string());
// Do not get costing information if show cost is disabled
@@ -1488,7 +1481,7 @@ async fn get_reasoner() -> Result<Arc<dyn Provider>, anyhow::Error> {
} else {
println!("WARNING: GOOSE_PLANNER_PROVIDER not found. Using default provider...");
config
.get_param::<String>("GOOSE_PROVIDER")
.get_goose_provider()
.expect("No provider configured. Run 'goose configure' first")
};
@@ -1498,7 +1491,7 @@ async fn get_reasoner() -> Result<Arc<dyn Provider>, anyhow::Error> {
} else {
println!("WARNING: GOOSE_PLANNER_MODEL not found. Using default model...");
config
.get_param::<String>("GOOSE_MODEL")
.get_goose_model()
.expect("No model configured. Run 'goose configure' first")
};
+2 -2
View File
@@ -68,7 +68,7 @@ thread_local! {
pub fn set_theme(theme: Theme) {
let config = Config::global();
config
.set_param("GOOSE_CLI_THEME", Value::String(theme.as_config_string()))
.set_param("GOOSE_CLI_THEME", theme.as_config_string())
.expect("Failed to set theme");
CURRENT_THEME.with(|t| *t.borrow_mut() = theme);
@@ -79,7 +79,7 @@ pub fn set_theme(theme: Theme) {
Theme::Ansi => "ansi",
};
if let Err(e) = config.set_param("GOOSE_CLI_THEME", Value::String(theme_str.to_string())) {
if let Err(e) = config.set_param("GOOSE_CLI_THEME", theme_str) {
eprintln!("Failed to save theme setting to config: {}", e);
}
}