diff --git a/crates/goose-cli/src/commands/configure.rs b/crates/goose-cli/src/commands/configure.rs index 71497f3c..a49d8216 100644 --- a/crates/goose-cli/src/commands/configure.rs +++ b/crates/goose-cli/src/commands/configure.rs @@ -266,7 +266,8 @@ pub async fn configure_provider_dialog() -> Result> { let spin = spinner(); spin.start("Checking your configuration..."); - let model_config = goose::model::ModelConfig::new(model.clone()); + // Use max tokens to speed up the provider test. + let model_config = goose::model::ModelConfig::new(model.clone()).with_max_tokens(Some(10)); let provider = create(provider_name, model_config)?; let message = Message::user().with_text( diff --git a/crates/goose-server/src/routes/secrets.rs b/crates/goose-server/src/routes/secrets.rs index 03278e17..a4a29be4 100644 --- a/crates/goose-server/src/routes/secrets.rs +++ b/crates/goose-server/src/routes/secrets.rs @@ -13,9 +13,11 @@ struct SecretResponse { } #[derive(Deserialize)] +#[serde(rename_all = "camelCase")] struct SecretRequest { key: String, value: String, + is_secret: bool, } async fn store_secret( @@ -33,7 +35,13 @@ async fn store_secret( return Err(StatusCode::UNAUTHORIZED); } - match Config::global().set_secret(&request.key, Value::String(request.value)) { + let config = Config::global(); + let result = if request.is_secret { + config.set_secret(&request.key, Value::String(request.value)) + } else { + config.set(&request.key, Value::String(request.value)) + }; + match result { Ok(_) => Ok(Json(SecretResponse { error: false })), Err(_) => Ok(Json(SecretResponse { error: true })), } diff --git a/crates/goose/src/providers/ollama.rs b/crates/goose/src/providers/ollama.rs index 63db1fec..6873e2fe 100644 --- a/crates/goose/src/providers/ollama.rs +++ b/crates/goose/src/providers/ollama.rs @@ -71,7 +71,7 @@ impl Provider for OllamaProvider { OLLAMA_DOC_URL, vec![ConfigKey::new( "OLLAMA_HOST", - false, + true, false, Some(OLLAMA_HOST), )], diff --git a/documentation/docs/getting-started/providers.md b/documentation/docs/getting-started/providers.md index a50b8478..6b375309 100644 --- a/documentation/docs/getting-started/providers.md +++ b/documentation/docs/getting-started/providers.md @@ -159,6 +159,9 @@ goose configure ◇ Which model provider should we use? │ Ollama │ +◇ Provider Ollama requires OLLAMA_HOST, please enter a value +│ http://localhost:11434 +│ ◇ Enter a model from that provider: │ qwen2.5 │ diff --git a/ui/desktop/src/components/settings/ProviderSetupModal.tsx b/ui/desktop/src/components/settings/ProviderSetupModal.tsx index 038ef2dc..7c493639 100644 --- a/ui/desktop/src/components/settings/ProviderSetupModal.tsx +++ b/ui/desktop/src/components/settings/ProviderSetupModal.tsx @@ -4,6 +4,7 @@ import { Lock } from 'lucide-react'; import { Input } from '../ui/input'; import { Button } from '../ui/button'; import { required_keys } from './models/hardcoded_stuff'; +import { isSecretKey } from './api_keys/utils'; // import UnionIcon from "../images/Union@2x.svg"; interface ProviderSetupModalProps { @@ -30,6 +31,7 @@ export function ProviderSetupModal({ e.preventDefault(); onSubmit(apiKey); }; + const inputType = isSecretKey(keyName) ? 'password' : 'text'; return (
@@ -49,7 +51,7 @@ export function ProviderSetupModal({
setApiKey(e.target.value)} placeholder={keyName} @@ -58,7 +60,7 @@ export function ProviderSetupModal({ />
- {`Your API key will be stored securely in the keychain and used only for making requests to ${provider}`} + {`Your API key or host will be stored securely in the keychain and used only for making requests to ${provider}`}
diff --git a/ui/desktop/src/components/settings/api_keys/utils.tsx b/ui/desktop/src/components/settings/api_keys/utils.tsx index 2140a99e..3c906ac6 100644 --- a/ui/desktop/src/components/settings/api_keys/utils.tsx +++ b/ui/desktop/src/components/settings/api_keys/utils.tsx @@ -2,6 +2,11 @@ import { Provider, ProviderResponse } from './types'; import { getApiUrl, getSecretKey } from '../../../config'; import { special_provider_cases } from '../providers/utils'; +export function isSecretKey(keyName: string): boolean { + // Ollama and Databricks use host name right now and it should not be stored as secret. + return keyName != 'DATABRICKS_HOST' && keyName != 'OLLAMA_HOST'; +} + export async function getActiveProviders(): Promise { try { // Fetch the secrets settings diff --git a/ui/desktop/src/components/settings/extensions/ConfigureBuiltInExtensionModal.tsx b/ui/desktop/src/components/settings/extensions/ConfigureBuiltInExtensionModal.tsx index 0649d583..05340f70 100644 --- a/ui/desktop/src/components/settings/extensions/ConfigureBuiltInExtensionModal.tsx +++ b/ui/desktop/src/components/settings/extensions/ConfigureBuiltInExtensionModal.tsx @@ -52,6 +52,7 @@ export function ConfigureBuiltInExtensionModal({ body: JSON.stringify({ key: envKey, value: value.trim(), + isSecret: true, }), }); diff --git a/ui/desktop/src/components/settings/extensions/ConfigureExtensionModal.tsx b/ui/desktop/src/components/settings/extensions/ConfigureExtensionModal.tsx index 63c44752..4a81d70b 100644 --- a/ui/desktop/src/components/settings/extensions/ConfigureExtensionModal.tsx +++ b/ui/desktop/src/components/settings/extensions/ConfigureExtensionModal.tsx @@ -54,6 +54,7 @@ export function ConfigureExtensionModal({ body: JSON.stringify({ key: envKey, value: value.trim(), + isSecret: true, }), }); diff --git a/ui/desktop/src/components/settings/extensions/ManualExtensionModal.tsx b/ui/desktop/src/components/settings/extensions/ManualExtensionModal.tsx index 04565401..92adda3b 100644 --- a/ui/desktop/src/components/settings/extensions/ManualExtensionModal.tsx +++ b/ui/desktop/src/components/settings/extensions/ManualExtensionModal.tsx @@ -68,6 +68,7 @@ export function ManualExtensionModal({ isOpen, onClose, onSubmit }: ManualExtens body: JSON.stringify({ key: envVar.key, value: envVar.value.trim(), + isSecret: true, }), }); diff --git a/ui/desktop/src/components/settings/models/hardcoded_stuff.tsx b/ui/desktop/src/components/settings/models/hardcoded_stuff.tsx index 4078cfc8..9aa8b47c 100644 --- a/ui/desktop/src/components/settings/models/hardcoded_stuff.tsx +++ b/ui/desktop/src/components/settings/models/hardcoded_stuff.tsx @@ -72,7 +72,7 @@ export const required_keys = { Anthropic: ['ANTHROPIC_API_KEY'], Databricks: ['DATABRICKS_HOST'], Groq: ['GROQ_API_KEY'], - Ollama: [], + Ollama: ['OLLAMA_HOST'], Google: ['GOOGLE_API_KEY'], OpenRouter: ['OPENROUTER_API_KEY'], }; diff --git a/ui/desktop/src/components/settings/providers/ConfigureProvidersGrid.tsx b/ui/desktop/src/components/settings/providers/ConfigureProvidersGrid.tsx index 12ffe1d1..f34e5aca 100644 --- a/ui/desktop/src/components/settings/providers/ConfigureProvidersGrid.tsx +++ b/ui/desktop/src/components/settings/providers/ConfigureProvidersGrid.tsx @@ -5,7 +5,7 @@ import { supported_providers, provider_aliases, required_keys } from '../models/ import { ProviderSetupModal } from '../ProviderSetupModal'; import { getApiUrl, getSecretKey } from '../../../config'; import { toast } from 'react-toastify'; -import { getActiveProviders } from '../api_keys/utils'; +import { getActiveProviders, isSecretKey } from '../api_keys/utils'; import { useModel } from '../models/ModelContext'; import { Button } from '../../ui/button'; @@ -107,6 +107,7 @@ export function ConfigureProvidersGrid() { } // Store new key + const isSecret = isSecretKey(keyName); const storeResponse = await fetch(getApiUrl('/secrets/store'), { method: 'POST', headers: { @@ -116,6 +117,7 @@ export function ConfigureProvidersGrid() { body: JSON.stringify({ key: keyName, value: apiKey.trim(), + isSecret, }), }); @@ -125,10 +127,11 @@ export function ConfigureProvidersGrid() { throw new Error('Failed to store new key'); } + const toastInfo = isSecret ? 'API key' : 'host'; toast.success( isUpdate - ? `Successfully updated API key for ${provider}` - : `Successfully added API key for ${provider}` + ? `Successfully updated ${toastInfo} for ${provider}` + : `Successfully added ${toastInfo} for ${provider}` ); const updatedKeys = await getActiveProviders(); diff --git a/ui/desktop/src/components/welcome_screen/ProviderGrid.tsx b/ui/desktop/src/components/welcome_screen/ProviderGrid.tsx index b2476a9b..48a9d640 100644 --- a/ui/desktop/src/components/welcome_screen/ProviderGrid.tsx +++ b/ui/desktop/src/components/welcome_screen/ProviderGrid.tsx @@ -14,7 +14,7 @@ import { getDefaultModel } from '../settings/models/hardcoded_stuff'; import { initializeSystem } from '../../utils/providerUtils'; import { getApiUrl, getSecretKey } from '../../config'; import { toast } from 'react-toastify'; -import { getActiveProviders } from '../settings/api_keys/utils'; +import { getActiveProviders, isSecretKey } from '../settings/api_keys/utils'; import { useNavigate } from 'react-router-dom'; import { BaseProviderGrid, getProviderDescription } from '../settings/providers/BaseProviderGrid'; @@ -99,6 +99,7 @@ export function ProviderGrid({ onSubmit }: ProviderGridProps) { } } + const isSecret = isSecretKey(keyName); const storeResponse = await fetch(getApiUrl('/secrets/store'), { method: 'POST', headers: { @@ -108,6 +109,7 @@ export function ProviderGrid({ onSubmit }: ProviderGridProps) { body: JSON.stringify({ key: keyName, value: apiKey.trim(), + isSecret, }), }); @@ -118,10 +120,11 @@ export function ProviderGrid({ onSubmit }: ProviderGridProps) { } const isUpdate = selectedId && providers.find((p) => p.id === selectedId)?.isConfigured; + const toastInfo = isSecret ? 'API key' : 'host'; toast.success( isUpdate - ? `Successfully updated API key for ${provider}` - : `Successfully added API key for ${provider}` + ? `Successfully updated ${toastInfo} for ${provider}` + : `Successfully added ${toastInfo} for ${provider}` ); const updatedKeys = await getActiveProviders();