feat: update config endpoints for use with providers (#1563)

This commit is contained in:
Lily Delalande
2025-03-10 09:51:54 -07:00
committed by GitHub
parent 3b36591cb5
commit 5df2875c1c
43 changed files with 945 additions and 428 deletions
+1 -1
View File
@@ -351,7 +351,7 @@ impl Capabilities {
let mut system_prompt_extensions = self.system_prompt_extensions.clone();
let config = Config::global();
let goose_mode = config.get("GOOSE_MODE").unwrap_or("auto".to_string());
let goose_mode = config.get_param("GOOSE_MODE").unwrap_or("auto".to_string());
if goose_mode == "chat" {
system_prompt_extensions.push(
"Right now you are in the chat only mode, no access to any tool use and system."
+1 -1
View File
@@ -50,7 +50,7 @@ impl AgentFactory {
pub fn configured_version() -> String {
let config = Config::global();
config
.get::<String>("GOOSE_AGENT")
.get_param::<String>("GOOSE_AGENT")
.unwrap_or_else(|_| Self::default_version().to_string())
}
+1 -1
View File
@@ -177,7 +177,7 @@ impl Agent for SummarizeAgent {
// Load settings from config
let config = Config::global();
let goose_mode = config.get("GOOSE_MODE").unwrap_or("auto".to_string());
let goose_mode = config.get_param("GOOSE_MODE").unwrap_or("auto".to_string());
// we add in the 2 resource tools if any extensions support resources
// TODO: make sure there is no collision with another extension's tool name
+1 -1
View File
@@ -171,7 +171,7 @@ impl Agent for TruncateAgent {
// Load settings from config
let config = Config::global();
let goose_mode = config.get("GOOSE_MODE").unwrap_or("auto".to_string());
let goose_mode = config.get_param("GOOSE_MODE").unwrap_or("auto".to_string());
// we add in the 2 resource tools if any extensions support resources
// TODO: make sure there is no collision with another extension's tool name
+35 -17
View File
@@ -78,7 +78,7 @@ impl From<keyring::Error> for ConfigError {
///
/// // Get a string value
/// let config = Config::global();
/// let api_key: String = config.get("OPENAI_API_KEY").unwrap();
/// let api_key: String = config.get_param("OPENAI_API_KEY").unwrap();
///
/// // Get a complex type
/// #[derive(Deserialize)]
@@ -87,7 +87,7 @@ impl From<keyring::Error> for ConfigError {
/// port: u16,
/// }
///
/// let server_config: ServerConfig = config.get("server").unwrap();
/// let server_config: ServerConfig = config.get_param("server").unwrap();
/// ```
///
/// # Naming Convention
@@ -204,7 +204,25 @@ impl Config {
}
}
/// Get a configuration value.
// check all possible places for a parameter
pub fn get(&self, key: &str, is_secret: bool) -> Result<Value, ConfigError> {
if is_secret {
self.get_secret(key)
} else {
self.get_param(key)
}
}
// save a parameter in the appropriate location based on if it's secret or not
pub fn set(&self, key: &str, value: Value, is_secret: bool) -> Result<(), ConfigError> {
if is_secret {
self.set_secret(key, value)
} else {
self.set_param(key, value)
}
}
/// Get a configuration value (non-secret).
///
/// This will attempt to get the value from:
/// 1. Environment variable with the exact key name
@@ -220,7 +238,7 @@ impl Config {
/// - The key doesn't exist in either environment or config file
/// - The value cannot be deserialized into the requested type
/// - There is an error reading the config file
pub fn get<T: for<'de> Deserialize<'de>>(&self, key: &str) -> Result<T, ConfigError> {
pub fn get_param<T: for<'de> Deserialize<'de>>(&self, key: &str) -> Result<T, ConfigError> {
// First check environment variables (convert to uppercase)
let env_key = key.to_uppercase();
if let Ok(val) = env::var(&env_key) {
@@ -239,7 +257,7 @@ impl Config {
.and_then(|v| Ok(serde_json::from_value(v.clone())?))
}
/// Set a configuration value in the config file.
/// Set a configuration value in the config file (non-secret).
///
/// This will immediately write the value to the config file. The value
/// can be any type that can be serialized to JSON/YAML.
@@ -252,7 +270,7 @@ impl Config {
/// Returns a ConfigError if:
/// - There is an error reading or writing the config file
/// - There is an error serializing the value
pub fn set(&self, key: &str, value: Value) -> Result<(), ConfigError> {
pub fn set_param(&self, key: &str, value: Value) -> Result<(), ConfigError> {
let mut values = self.load_values()?;
values.insert(key.to_string(), value);
@@ -377,15 +395,15 @@ mod tests {
let config = Config::new(temp_file.path(), TEST_KEYRING_SERVICE)?;
// Set a simple string value
config.set("test_key", Value::String("test_value".to_string()))?;
config.set_param("test_key", Value::String("test_value".to_string()))?;
// Test simple string retrieval
let value: String = config.get("test_key")?;
let value: String = config.get_param("test_key")?;
assert_eq!(value, "test_value");
// Test with environment variable override
std::env::set_var("TEST_KEY", "env_value");
let value: String = config.get("test_key")?;
let value: String = config.get_param("test_key")?;
assert_eq!(value, "env_value");
Ok(())
@@ -403,7 +421,7 @@ mod tests {
let config = Config::new(temp_file.path(), TEST_KEYRING_SERVICE)?;
// Set a complex value
config.set(
config.set_param(
"complex_key",
serde_json::json!({
"field1": "hello",
@@ -411,7 +429,7 @@ mod tests {
}),
)?;
let value: TestStruct = config.get("complex_key")?;
let value: TestStruct = config.get_param("complex_key")?;
assert_eq!(value.field1, "hello");
assert_eq!(value.field2, 42);
@@ -423,7 +441,7 @@ mod tests {
let temp_file = NamedTempFile::new().unwrap();
let config = Config::new(temp_file.path(), TEST_KEYRING_SERVICE).unwrap();
let result: Result<String, ConfigError> = config.get("nonexistent_key");
let result: Result<String, ConfigError> = config.get_param("nonexistent_key");
assert!(matches!(result, Err(ConfigError::NotFound(_))));
}
@@ -432,8 +450,8 @@ mod tests {
let temp_file = NamedTempFile::new().unwrap();
let config = Config::new(temp_file.path(), TEST_KEYRING_SERVICE)?;
config.set("key1", Value::String("value1".to_string()))?;
config.set("key2", Value::Number(42.into()))?;
config.set_param("key1", Value::String("value1".to_string()))?;
config.set_param("key2", Value::Number(42.into()))?;
// Read the file directly to check YAML formatting
let content = std::fs::read_to_string(temp_file.path())?;
@@ -448,14 +466,14 @@ mod tests {
let temp_file = NamedTempFile::new().unwrap();
let config = Config::new(temp_file.path(), TEST_KEYRING_SERVICE)?;
config.set("key", Value::String("value".to_string()))?;
config.set_param("key", Value::String("value".to_string()))?;
let value: String = config.get("key")?;
let value: String = config.get_param("key")?;
assert_eq!(value, "value");
config.delete("key")?;
let result: Result<String, ConfigError> = config.get("key");
let result: Result<String, ConfigError> = config.get_param("key");
assert!(matches!(result, Err(ConfigError::NotFound(_))));
Ok(())
+6 -4
View File
@@ -18,7 +18,8 @@ impl ExperimentManager {
/// - Removes experiments not in `ALL_EXPERIMENTS`.
pub fn get_all() -> Result<Vec<(String, bool)>> {
let config = Config::global();
let mut experiments: HashMap<String, bool> = config.get("experiments").unwrap_or_default();
let mut experiments: HashMap<String, bool> =
config.get_param("experiments").unwrap_or_default();
Self::refresh_experiments(&mut experiments);
Ok(experiments.into_iter().collect())
@@ -27,12 +28,13 @@ impl ExperimentManager {
/// Enable or disable an experiment
pub fn set_enabled(name: &str, enabled: bool) -> Result<()> {
let config = Config::global();
let mut experiments: HashMap<String, bool> =
config.get("experiments").unwrap_or_else(|_| HashMap::new());
let mut experiments: HashMap<String, bool> = config
.get_param("experiments")
.unwrap_or_else(|_| HashMap::new());
Self::refresh_experiments(&mut experiments);
experiments.insert(name.to_string(), enabled);
config.set("experiments", serde_json::to_value(experiments)?)?;
config.set_param("experiments", serde_json::to_value(experiments)?)?;
Ok(())
}
+19 -15
View File
@@ -23,7 +23,7 @@ impl ExtensionManager {
let config = Config::global();
// Try to get the extension entry
let extensions: HashMap<String, ExtensionEntry> = match config.get("extensions") {
let extensions: HashMap<String, ExtensionEntry> = match config.get_param("extensions") {
Ok(exts) => exts,
Err(super::ConfigError::NotFound(_)) => {
// Initialize with default developer extension
@@ -37,7 +37,7 @@ impl ExtensionManager {
},
},
)]);
config.set("extensions", serde_json::to_value(&defaults)?)?;
config.set_param("extensions", serde_json::to_value(&defaults)?)?;
defaults
}
Err(e) => return Err(e.into()),
@@ -56,11 +56,12 @@ impl ExtensionManager {
pub fn set(entry: ExtensionEntry) -> Result<()> {
let config = Config::global();
let mut extensions: HashMap<String, ExtensionEntry> =
config.get("extensions").unwrap_or_else(|_| HashMap::new());
let mut extensions: HashMap<String, ExtensionEntry> = config
.get_param("extensions")
.unwrap_or_else(|_| HashMap::new());
extensions.insert(entry.config.name().parse()?, entry);
config.set("extensions", serde_json::to_value(extensions)?)?;
config.set_param("extensions", serde_json::to_value(extensions)?)?;
Ok(())
}
@@ -68,11 +69,12 @@ impl ExtensionManager {
pub fn remove(name: &str) -> Result<()> {
let config = Config::global();
let mut extensions: HashMap<String, ExtensionEntry> =
config.get("extensions").unwrap_or_else(|_| HashMap::new());
let mut extensions: HashMap<String, ExtensionEntry> = config
.get_param("extensions")
.unwrap_or_else(|_| HashMap::new());
extensions.remove(name);
config.set("extensions", serde_json::to_value(extensions)?)?;
config.set_param("extensions", serde_json::to_value(extensions)?)?;
Ok(())
}
@@ -80,12 +82,13 @@ impl ExtensionManager {
pub fn set_enabled(name: &str, enabled: bool) -> Result<()> {
let config = Config::global();
let mut extensions: HashMap<String, ExtensionEntry> =
config.get("extensions").unwrap_or_else(|_| HashMap::new());
let mut extensions: HashMap<String, ExtensionEntry> = config
.get_param("extensions")
.unwrap_or_else(|_| HashMap::new());
if let Some(entry) = extensions.get_mut(name) {
entry.enabled = enabled;
config.set("extensions", serde_json::to_value(extensions)?)?;
config.set_param("extensions", serde_json::to_value(extensions)?)?;
}
Ok(())
}
@@ -94,7 +97,7 @@ impl ExtensionManager {
pub fn get_all() -> Result<Vec<ExtensionEntry>> {
let config = Config::global();
let extensions: HashMap<String, ExtensionEntry> =
config.get("extensions").unwrap_or_default();
config.get_param("extensions").unwrap_or_default();
Ok(Vec::from_iter(extensions.values().cloned()))
}
@@ -102,15 +105,16 @@ impl ExtensionManager {
pub fn get_all_names() -> Result<Vec<String>> {
let config = Config::global();
Ok(config
.get("extensions")
.get_param("extensions")
.unwrap_or_else(|_| get_keys(Default::default())))
}
/// Check if an extension is enabled
pub fn is_enabled(name: &str) -> Result<bool> {
let config = Config::global();
let extensions: HashMap<String, ExtensionEntry> =
config.get("extensions").unwrap_or_else(|_| HashMap::new());
let extensions: HashMap<String, ExtensionEntry> = config
.get_param("extensions")
.unwrap_or_else(|_| HashMap::new());
Ok(extensions.get(name).map(|e| e.enabled).unwrap_or(false))
}
+1 -1
View File
@@ -45,7 +45,7 @@ impl AnthropicProvider {
let config = crate::config::Config::global();
let api_key: String = config.get_secret("ANTHROPIC_API_KEY")?;
let host: String = config
.get("ANTHROPIC_HOST")
.get_param("ANTHROPIC_HOST")
.unwrap_or_else(|_| "https://api.anthropic.com".to_string());
let client = Client::builder()
+5 -15
View File
@@ -40,10 +40,10 @@ impl AzureProvider {
pub fn from_env(model: ModelConfig) -> Result<Self> {
let config = crate::config::Config::global();
let api_key: String = config.get_secret("AZURE_OPENAI_API_KEY")?;
let endpoint: String = config.get("AZURE_OPENAI_ENDPOINT")?;
let deployment_name: String = config.get("AZURE_OPENAI_DEPLOYMENT_NAME")?;
let endpoint: String = config.get_param("AZURE_OPENAI_ENDPOINT")?;
let deployment_name: String = config.get_param("AZURE_OPENAI_DEPLOYMENT_NAME")?;
let api_version: String = config
.get("AZURE_OPENAI_API_VERSION")
.get_param("AZURE_OPENAI_API_VERSION")
.unwrap_or_else(|_| AZURE_DEFAULT_API_VERSION.to_string());
let client = Client::builder()
@@ -109,18 +109,8 @@ impl Provider for AzureProvider {
vec![
ConfigKey::new("AZURE_OPENAI_API_KEY", true, true, None),
ConfigKey::new("AZURE_OPENAI_ENDPOINT", true, false, None),
ConfigKey::new(
"AZURE_OPENAI_DEPLOYMENT_NAME",
true,
false,
Some("Name of your Azure OpenAI deployment"),
),
ConfigKey::new(
"AZURE_OPENAI_API_VERSION",
false,
false,
Some("Azure OpenAI API version, default: 2024-10-21"),
),
ConfigKey::new("AZURE_OPENAI_DEPLOYMENT_NAME", true, false, None),
ConfigKey::new("AZURE_OPENAI_API_VERSION", false, false, Some("2024-10-21")),
],
)
}
+3 -2
View File
@@ -5,9 +5,10 @@ use super::errors::ProviderError;
use crate::message::Message;
use crate::model::ModelConfig;
use mcp_core::tool::Tool;
use utoipa::ToSchema;
/// Metadata about a provider's configuration requirements and capabilities
#[derive(Debug, Clone, Serialize, Deserialize)]
#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
pub struct ProviderMetadata {
/// The unique identifier for this provider
pub name: String,
@@ -60,7 +61,7 @@ impl ProviderMetadata {
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
pub struct ConfigKey {
pub name: String,
pub required: bool,
+1 -1
View File
@@ -83,7 +83,7 @@ impl DatabricksProvider {
// For compatibility for now we check both config and secret for databricks host
// but it is not actually a secret value
let mut host: Result<String, ConfigError> = config.get("DATABRICKS_HOST");
let mut host: Result<String, ConfigError> = config.get_param("DATABRICKS_HOST");
if host.is_err() {
host = config.get_secret("DATABRICKS_HOST")
+6 -6
View File
@@ -146,7 +146,7 @@ impl GcpVertexAIProvider {
/// * `model` - Configuration for the model to be used
async fn new_async(model: ModelConfig) -> Result<Self> {
let config = crate::config::Config::global();
let project_id = config.get("GCP_PROJECT_ID")?;
let project_id = config.get_param("GCP_PROJECT_ID")?;
let location = Self::determine_location(config)?;
let host = format!("https://{}-aiplatform.googleapis.com", location);
@@ -173,25 +173,25 @@ impl GcpVertexAIProvider {
/// Loads retry configuration from environment variables or uses defaults.
fn load_retry_config(config: &crate::config::Config) -> RetryConfig {
let max_retries = config
.get("GCP_MAX_RETRIES")
.get_param("GCP_MAX_RETRIES")
.ok()
.and_then(|v: String| v.parse::<usize>().ok())
.unwrap_or(DEFAULT_MAX_RETRIES);
let initial_interval_ms = config
.get("GCP_INITIAL_RETRY_INTERVAL_MS")
.get_param("GCP_INITIAL_RETRY_INTERVAL_MS")
.ok()
.and_then(|v: String| v.parse::<u64>().ok())
.unwrap_or(DEFAULT_INITIAL_RETRY_INTERVAL_MS);
let backoff_multiplier = config
.get("GCP_BACKOFF_MULTIPLIER")
.get_param("GCP_BACKOFF_MULTIPLIER")
.ok()
.and_then(|v: String| v.parse::<f64>().ok())
.unwrap_or(DEFAULT_BACKOFF_MULTIPLIER);
let max_interval_ms = config
.get("GCP_MAX_RETRY_INTERVAL_MS")
.get_param("GCP_MAX_RETRY_INTERVAL_MS")
.ok()
.and_then(|v: String| v.parse::<u64>().ok())
.unwrap_or(DEFAULT_MAX_RETRY_INTERVAL_MS);
@@ -211,7 +211,7 @@ impl GcpVertexAIProvider {
/// 2. Global default location (Iowa)
fn determine_location(config: &crate::config::Config) -> Result<String> {
Ok(config
.get("GCP_LOCATION")
.get_param("GCP_LOCATION")
.ok()
.filter(|location: &String| !location.trim().is_empty())
.unwrap_or_else(|| Iowa.to_string()))
+1 -1
View File
@@ -50,7 +50,7 @@ impl GoogleProvider {
let config = crate::config::Config::global();
let api_key: String = config.get_secret("GOOGLE_API_KEY")?;
let host: String = config
.get("GOOGLE_HOST")
.get_param("GOOGLE_HOST")
.unwrap_or_else(|_| GOOGLE_API_HOST.to_string());
let client = Client::builder()
+1 -1
View File
@@ -39,7 +39,7 @@ impl GroqProvider {
let config = crate::config::Config::global();
let api_key: String = config.get_secret("GROQ_API_KEY")?;
let host: String = config
.get("GROQ_HOST")
.get_param("GROQ_HOST")
.unwrap_or_else(|_| GROQ_API_HOST.to_string());
let client = Client::builder()
+1 -1
View File
@@ -39,7 +39,7 @@ impl OllamaProvider {
pub fn from_env(model: ModelConfig) -> Result<Self> {
let config = crate::config::Config::global();
let host: String = config
.get("OLLAMA_HOST")
.get_param("OLLAMA_HOST")
.unwrap_or_else(|_| OLLAMA_HOST.to_string());
let client = Client::builder()
+4 -4
View File
@@ -47,13 +47,13 @@ impl OpenAiProvider {
let config = crate::config::Config::global();
let api_key: String = config.get_secret("OPENAI_API_KEY")?;
let host: String = config
.get("OPENAI_HOST")
.get_param("OPENAI_HOST")
.unwrap_or_else(|_| "https://api.openai.com".to_string());
let base_path: String = config
.get("OPENAI_BASE_PATH")
.get_param("OPENAI_BASE_PATH")
.unwrap_or_else(|_| "v1/chat/completions".to_string());
let organization: Option<String> = config.get("OPENAI_ORGANIZATION").ok();
let project: Option<String> = config.get("OPENAI_PROJECT").ok();
let organization: Option<String> = config.get_param("OPENAI_ORGANIZATION").ok();
let project: Option<String> = config.get_param("OPENAI_PROJECT").ok();
let client = Client::builder()
.timeout(Duration::from_secs(600))
.build()?;
+1 -1
View File
@@ -44,7 +44,7 @@ impl OpenRouterProvider {
let config = crate::config::Config::global();
let api_key: String = config.get_secret("OPENROUTER_API_KEY")?;
let host: String = config
.get("OPENROUTER_HOST")
.get_param("OPENROUTER_HOST")
.unwrap_or_else(|_| "https://openrouter.ai".to_string());
let client = Client::builder()