-
Notifications
You must be signed in to change notification settings - Fork 41
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge branch 'refs/heads/v1.0' into mnovich/session-management
* refs/heads/v1.0: small fix (#416) fix: configure allows selecting the suggested option with return (#414) only publish default bundle of goose.app (#381) better icns standard size for macos updating goosed for dev time fix: Clippy for memory.rs fix: quick format fix: Remove insert command Lifei/configure key chain (#389) feat: Add welcome screen (#410) # Conflicts: # ui/desktop/src/ChatWindow.tsx # ui/desktop/src/main.ts
- Loading branch information
Showing
17 changed files
with
304 additions
and
387 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,88 +1,153 @@ | ||
use crate::commands::expected_config::{get_recommended_models, RecommendedModels}; | ||
use crate::inputs::get_user_input; | ||
use crate::profile::{ | ||
find_existing_profile, profile_path, save_profile, select_provider_lists, set_provider_config, | ||
Profile, PROVIDER_OPEN_AI, | ||
find_existing_profile, get_provider_config, profile_path, save_profile, Profile, | ||
}; | ||
use cliclack::spinner; | ||
use console::style; | ||
use goose::key_manager::{get_keyring_secret, save_to_keyring, KeyRetrievalStrategy}; | ||
use goose::models::message::Message; | ||
use goose::providers::configs::ProviderConfig; | ||
use goose::providers::factory; | ||
use goose::providers::ollama::OLLAMA_MODEL; | ||
use std::error::Error; | ||
|
||
pub async fn handle_configure(provided_profile_name: Option<String>) -> Result<(), Box<dyn Error>> { | ||
cliclack::intro(style(" configure-goose ").on_cyan().black())?; | ||
println!("We are helping you configure your Goose CLI profile."); | ||
let profile_name = provided_profile_name | ||
.unwrap_or_else(|| get_user_input("Enter profile name:", "default").unwrap()); | ||
let existing_profile_result = get_existing_profile(&profile_name); | ||
|
||
let profile_name = if let Some(name) = provided_profile_name { | ||
name | ||
} else { | ||
cliclack::input("Which profile should we configure?") | ||
.default_input("default") | ||
.interact()? | ||
}; | ||
|
||
// Use default values from existing profile | ||
let existing_profile_result = find_existing_profile(&profile_name); | ||
let existing_profile = existing_profile_result.as_ref(); | ||
|
||
let provider_name = select_provider(existing_profile); | ||
let recommended_models = get_recommended_models(provider_name); | ||
let model = set_model(existing_profile, &recommended_models)?; | ||
let provider_config = set_provider_config(provider_name, model.clone()); | ||
if existing_profile.is_some() { | ||
let _ = cliclack::log::info(format!( | ||
"We are updating the existing profile for {}", | ||
profile_name | ||
)); | ||
} | ||
|
||
let default_provider = existing_profile.map_or("openai", |profile| profile.provider.as_str()); | ||
let provider_name = cliclack::select("Which model provider should we use?") | ||
.initial_value(default_provider) | ||
.items(&[ | ||
("openai", "OpenAI", "GPT-4o etc"), | ||
("databricks", "Databricks", "Models on AI Gateway"), | ||
("ollama", "Ollama", "Local open source models"), | ||
]) | ||
.interact()?; | ||
|
||
// Depending on the provider, we now want to look for any required keys and check or set them in the keychain | ||
for key in get_required_keys(provider_name).iter() { | ||
// If the key is in the keyring, ask if we want to overwrite | ||
if get_keyring_secret(key, KeyRetrievalStrategy::KeyringOnly).is_ok() { | ||
let _ = cliclack::log::info(format!("{} is already available in the keyring", key)); | ||
if cliclack::confirm("Would you like to overwrite this value?").interact()? { | ||
let value = cliclack::password(format!("Enter the value for {}", key)) | ||
.mask('▪') | ||
.interact()?; | ||
|
||
save_to_keyring(key, &value)?; | ||
} | ||
} | ||
// If the key is in the env, ask if we want to save to keyring | ||
else if let Ok(value) = get_keyring_secret(key, KeyRetrievalStrategy::EnvironmentOnly) { | ||
let _ = cliclack::log::info(format!("Detected {} in env, we can use this from your environment.\nIt will need to continue to be set in future goose usage.", key)); | ||
if cliclack::confirm("Would you like to save it to your kerying?").interact()? { | ||
save_to_keyring(key, &value)?; | ||
} | ||
} | ||
// We don't have a value, so we prompt for one | ||
else { | ||
let value = cliclack::password(format!( | ||
"Provider {} requires {}, please enter a value. (Will be saved to your keyring)", | ||
provider_name, key | ||
)) | ||
.mask('▪') | ||
.interact()?; | ||
|
||
save_to_keyring(key, &value)?; | ||
} | ||
} | ||
|
||
let recommended_model = get_recommended_model(provider_name); | ||
let default_model_value = | ||
existing_profile.map_or(recommended_model, |profile| profile.model.as_str()); | ||
let model: String = cliclack::input("Enter a model from that provider:") | ||
.default_input(default_model_value) | ||
.interact()?; | ||
|
||
// Forward any existing systems from the profile if present | ||
let additional_systems = | ||
existing_profile.map_or(Vec::new(), |profile| profile.additional_systems.clone()); | ||
|
||
if !additional_systems.is_empty() { | ||
let _ = cliclack::log::info( | ||
format!("We kept the existing systems from your {} profile. You can edit this with `goose system`", profile_name) | ||
); | ||
} | ||
|
||
let profile = Profile { | ||
provider: provider_name.to_string(), | ||
model: model.clone(), | ||
additional_systems, | ||
}; | ||
match save_profile(profile_name.as_str(), profile) { | ||
Ok(()) => println!("\nProfile saved to: {:?}", profile_path()?), | ||
Err(e) => println!("Failed to save profile: {}", e), | ||
} | ||
check_configuration(provider_config).await?; | ||
Ok(()) | ||
} | ||
|
||
async fn check_configuration(provider_config: ProviderConfig) -> Result<(), Box<dyn Error>> { | ||
// Confirm everything is configured correctly by calling a model! | ||
let provider_config = get_provider_config(provider_name, model.clone()); | ||
let spin = spinner(); | ||
spin.start("Now let's check your configuration..."); | ||
spin.start("Checking your configuration..."); | ||
let provider = factory::get_provider(provider_config).unwrap(); | ||
let message = Message::user().with_text("Please give a nice welcome messsage (one sentence) and let them know they are all set to use this agent"); | ||
let result = provider.complete( | ||
"You are an AI agent called Goose. You use tools of connected systems to solve problems.", | ||
&[message], &[]).await?; | ||
spin.stop( | ||
result | ||
.0 | ||
.content | ||
.first() | ||
.and_then(|c| c.as_text()) | ||
.unwrap_or("No response"), | ||
); | ||
let result = provider.complete("You are an AI agent called Goose. You use tools of connected systems to solve problems.", &[message], &[]).await; | ||
|
||
match result { | ||
Ok((message, _usage)) => { | ||
if let Some(content) = message.content.first() { | ||
if let Some(text) = content.as_text() { | ||
spin.stop(text); | ||
} else { | ||
spin.stop("No response text available"); | ||
} | ||
} else { | ||
spin.stop("No response content available"); | ||
} | ||
|
||
let _ = match save_profile(&profile_name, profile) { | ||
Ok(()) => cliclack::outro(format!("Profile saved to: {:?}", profile_path()?)), | ||
Err(e) => cliclack::outro(format!("Failed to save profile: {}", e)), | ||
}; | ||
} | ||
Err(_) => { | ||
spin.stop("We could not connect!"); | ||
let _ = cliclack::outro("Try rerunning configure and check your credentials."); | ||
} | ||
} | ||
|
||
Ok(()) | ||
} | ||
|
||
fn get_existing_profile(profile_name: &str) -> Option<Profile> { | ||
let existing_profile_result = find_existing_profile(profile_name); | ||
if existing_profile_result.is_some() { | ||
println!("Profile already exists. We are going to overwriting the existing profile..."); | ||
pub fn get_recommended_model(provider_name: &str) -> &str { | ||
if provider_name == "openai" { | ||
"gpt-4o" | ||
} else if provider_name == "databricks" { | ||
"claude-3-5-sonnet-2" | ||
} else if provider_name == "ollama" { | ||
OLLAMA_MODEL | ||
} else { | ||
println!("We are creating a new profile..."); | ||
panic!("Invalid provider name"); | ||
} | ||
existing_profile_result | ||
} | ||
|
||
fn set_model( | ||
existing_profile: Option<&Profile>, | ||
recommended_models: &RecommendedModels, | ||
) -> Result<String, Box<dyn Error>> { | ||
let default_model_value = | ||
existing_profile.map_or(recommended_models.model, |profile| profile.model.as_str()); | ||
let model = get_user_input("Enter model:", default_model_value)?; | ||
Ok(model) | ||
} | ||
|
||
fn select_provider(existing_profile: Option<&Profile>) -> &str { | ||
let default_value = | ||
existing_profile.map_or(PROVIDER_OPEN_AI, |profile| profile.provider.as_str()); | ||
cliclack::select("Select provider:") | ||
.initial_value(default_value) | ||
.items(&select_provider_lists()) | ||
.interact() | ||
.unwrap() | ||
pub fn get_required_keys(provider_name: &str) -> Vec<&'static str> { | ||
match provider_name { | ||
"openai" => vec!["OPENAI_API_KEY"], | ||
"databricks" => vec!["DATABRICKS_HOST"], | ||
"ollama" => vec!["OLLAMA_HOST"], | ||
_ => panic!("Invalid provider name"), | ||
} | ||
} |
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file was deleted.
Oops, something went wrong.
Oops, something went wrong.