mirror of
https://github.com/aljazceru/goose.git
synced 2025-12-18 14:44:21 +01:00
feat: allow setting ollama host (#874)
This commit is contained in:
@@ -266,7 +266,8 @@ pub async fn configure_provider_dialog() -> Result<bool, Box<dyn Error>> {
|
|||||||
let spin = spinner();
|
let spin = spinner();
|
||||||
spin.start("Checking your configuration...");
|
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 provider = create(provider_name, model_config)?;
|
||||||
|
|
||||||
let message = Message::user().with_text(
|
let message = Message::user().with_text(
|
||||||
|
|||||||
@@ -13,9 +13,11 @@ struct SecretResponse {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
struct SecretRequest {
|
struct SecretRequest {
|
||||||
key: String,
|
key: String,
|
||||||
value: String,
|
value: String,
|
||||||
|
is_secret: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn store_secret(
|
async fn store_secret(
|
||||||
@@ -33,7 +35,13 @@ async fn store_secret(
|
|||||||
return Err(StatusCode::UNAUTHORIZED);
|
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 })),
|
Ok(_) => Ok(Json(SecretResponse { error: false })),
|
||||||
Err(_) => Ok(Json(SecretResponse { error: true })),
|
Err(_) => Ok(Json(SecretResponse { error: true })),
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -71,7 +71,7 @@ impl Provider for OllamaProvider {
|
|||||||
OLLAMA_DOC_URL,
|
OLLAMA_DOC_URL,
|
||||||
vec![ConfigKey::new(
|
vec![ConfigKey::new(
|
||||||
"OLLAMA_HOST",
|
"OLLAMA_HOST",
|
||||||
false,
|
true,
|
||||||
false,
|
false,
|
||||||
Some(OLLAMA_HOST),
|
Some(OLLAMA_HOST),
|
||||||
)],
|
)],
|
||||||
|
|||||||
@@ -159,6 +159,9 @@ goose configure
|
|||||||
◇ Which model provider should we use?
|
◇ Which model provider should we use?
|
||||||
│ Ollama
|
│ Ollama
|
||||||
│
|
│
|
||||||
|
◇ Provider Ollama requires OLLAMA_HOST, please enter a value
|
||||||
|
│ http://localhost:11434
|
||||||
|
│
|
||||||
◇ Enter a model from that provider:
|
◇ Enter a model from that provider:
|
||||||
│ qwen2.5
|
│ qwen2.5
|
||||||
│
|
│
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import { Lock } from 'lucide-react';
|
|||||||
import { Input } from '../ui/input';
|
import { Input } from '../ui/input';
|
||||||
import { Button } from '../ui/button';
|
import { Button } from '../ui/button';
|
||||||
import { required_keys } from './models/hardcoded_stuff';
|
import { required_keys } from './models/hardcoded_stuff';
|
||||||
|
import { isSecretKey } from './api_keys/utils';
|
||||||
// import UnionIcon from "../images/Union@2x.svg";
|
// import UnionIcon from "../images/Union@2x.svg";
|
||||||
|
|
||||||
interface ProviderSetupModalProps {
|
interface ProviderSetupModalProps {
|
||||||
@@ -30,6 +31,7 @@ export function ProviderSetupModal({
|
|||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
onSubmit(apiKey);
|
onSubmit(apiKey);
|
||||||
};
|
};
|
||||||
|
const inputType = isSecretKey(keyName) ? 'password' : 'text';
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="fixed inset-0 bg-black/20 dark:bg-white/20 backdrop-blur-sm transition-colors animate-[fadein_200ms_ease-in_forwards]">
|
<div className="fixed inset-0 bg-black/20 dark:bg-white/20 backdrop-blur-sm transition-colors animate-[fadein_200ms_ease-in_forwards]">
|
||||||
@@ -49,7 +51,7 @@ export function ProviderSetupModal({
|
|||||||
<div className="mt-[24px]">
|
<div className="mt-[24px]">
|
||||||
<div>
|
<div>
|
||||||
<Input
|
<Input
|
||||||
type="password"
|
type={inputType}
|
||||||
value={apiKey}
|
value={apiKey}
|
||||||
onChange={(e) => setApiKey(e.target.value)}
|
onChange={(e) => setApiKey(e.target.value)}
|
||||||
placeholder={keyName}
|
placeholder={keyName}
|
||||||
@@ -58,7 +60,7 @@ export function ProviderSetupModal({
|
|||||||
/>
|
/>
|
||||||
<div className="flex mt-4 text-gray-600 dark:text-gray-300">
|
<div className="flex mt-4 text-gray-600 dark:text-gray-300">
|
||||||
<Lock className="w-6 h-6" />
|
<Lock className="w-6 h-6" />
|
||||||
<span className="text-sm font-light ml-4 mt-[2px]">{`Your API key will be stored securely in the keychain and used only for making requests to ${provider}`}</span>
|
<span className="text-sm font-light ml-4 mt-[2px]">{`Your API key or host will be stored securely in the keychain and used only for making requests to ${provider}`}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -2,6 +2,11 @@ import { Provider, ProviderResponse } from './types';
|
|||||||
import { getApiUrl, getSecretKey } from '../../../config';
|
import { getApiUrl, getSecretKey } from '../../../config';
|
||||||
import { special_provider_cases } from '../providers/utils';
|
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<string[]> {
|
export async function getActiveProviders(): Promise<string[]> {
|
||||||
try {
|
try {
|
||||||
// Fetch the secrets settings
|
// Fetch the secrets settings
|
||||||
|
|||||||
@@ -52,6 +52,7 @@ export function ConfigureBuiltInExtensionModal({
|
|||||||
body: JSON.stringify({
|
body: JSON.stringify({
|
||||||
key: envKey,
|
key: envKey,
|
||||||
value: value.trim(),
|
value: value.trim(),
|
||||||
|
isSecret: true,
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -54,6 +54,7 @@ export function ConfigureExtensionModal({
|
|||||||
body: JSON.stringify({
|
body: JSON.stringify({
|
||||||
key: envKey,
|
key: envKey,
|
||||||
value: value.trim(),
|
value: value.trim(),
|
||||||
|
isSecret: true,
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -68,6 +68,7 @@ export function ManualExtensionModal({ isOpen, onClose, onSubmit }: ManualExtens
|
|||||||
body: JSON.stringify({
|
body: JSON.stringify({
|
||||||
key: envVar.key,
|
key: envVar.key,
|
||||||
value: envVar.value.trim(),
|
value: envVar.value.trim(),
|
||||||
|
isSecret: true,
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -72,7 +72,7 @@ export const required_keys = {
|
|||||||
Anthropic: ['ANTHROPIC_API_KEY'],
|
Anthropic: ['ANTHROPIC_API_KEY'],
|
||||||
Databricks: ['DATABRICKS_HOST'],
|
Databricks: ['DATABRICKS_HOST'],
|
||||||
Groq: ['GROQ_API_KEY'],
|
Groq: ['GROQ_API_KEY'],
|
||||||
Ollama: [],
|
Ollama: ['OLLAMA_HOST'],
|
||||||
Google: ['GOOGLE_API_KEY'],
|
Google: ['GOOGLE_API_KEY'],
|
||||||
OpenRouter: ['OPENROUTER_API_KEY'],
|
OpenRouter: ['OPENROUTER_API_KEY'],
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import { supported_providers, provider_aliases, required_keys } from '../models/
|
|||||||
import { ProviderSetupModal } from '../ProviderSetupModal';
|
import { ProviderSetupModal } from '../ProviderSetupModal';
|
||||||
import { getApiUrl, getSecretKey } from '../../../config';
|
import { getApiUrl, getSecretKey } from '../../../config';
|
||||||
import { toast } from 'react-toastify';
|
import { toast } from 'react-toastify';
|
||||||
import { getActiveProviders } from '../api_keys/utils';
|
import { getActiveProviders, isSecretKey } from '../api_keys/utils';
|
||||||
import { useModel } from '../models/ModelContext';
|
import { useModel } from '../models/ModelContext';
|
||||||
import { Button } from '../../ui/button';
|
import { Button } from '../../ui/button';
|
||||||
|
|
||||||
@@ -107,6 +107,7 @@ export function ConfigureProvidersGrid() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Store new key
|
// Store new key
|
||||||
|
const isSecret = isSecretKey(keyName);
|
||||||
const storeResponse = await fetch(getApiUrl('/secrets/store'), {
|
const storeResponse = await fetch(getApiUrl('/secrets/store'), {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: {
|
headers: {
|
||||||
@@ -116,6 +117,7 @@ export function ConfigureProvidersGrid() {
|
|||||||
body: JSON.stringify({
|
body: JSON.stringify({
|
||||||
key: keyName,
|
key: keyName,
|
||||||
value: apiKey.trim(),
|
value: apiKey.trim(),
|
||||||
|
isSecret,
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -125,10 +127,11 @@ export function ConfigureProvidersGrid() {
|
|||||||
throw new Error('Failed to store new key');
|
throw new Error('Failed to store new key');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const toastInfo = isSecret ? 'API key' : 'host';
|
||||||
toast.success(
|
toast.success(
|
||||||
isUpdate
|
isUpdate
|
||||||
? `Successfully updated API key for ${provider}`
|
? `Successfully updated ${toastInfo} for ${provider}`
|
||||||
: `Successfully added API key for ${provider}`
|
: `Successfully added ${toastInfo} for ${provider}`
|
||||||
);
|
);
|
||||||
|
|
||||||
const updatedKeys = await getActiveProviders();
|
const updatedKeys = await getActiveProviders();
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ import { getDefaultModel } from '../settings/models/hardcoded_stuff';
|
|||||||
import { initializeSystem } from '../../utils/providerUtils';
|
import { initializeSystem } from '../../utils/providerUtils';
|
||||||
import { getApiUrl, getSecretKey } from '../../config';
|
import { getApiUrl, getSecretKey } from '../../config';
|
||||||
import { toast } from 'react-toastify';
|
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 { useNavigate } from 'react-router-dom';
|
||||||
import { BaseProviderGrid, getProviderDescription } from '../settings/providers/BaseProviderGrid';
|
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'), {
|
const storeResponse = await fetch(getApiUrl('/secrets/store'), {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: {
|
headers: {
|
||||||
@@ -108,6 +109,7 @@ export function ProviderGrid({ onSubmit }: ProviderGridProps) {
|
|||||||
body: JSON.stringify({
|
body: JSON.stringify({
|
||||||
key: keyName,
|
key: keyName,
|
||||||
value: apiKey.trim(),
|
value: apiKey.trim(),
|
||||||
|
isSecret,
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -118,10 +120,11 @@ export function ProviderGrid({ onSubmit }: ProviderGridProps) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const isUpdate = selectedId && providers.find((p) => p.id === selectedId)?.isConfigured;
|
const isUpdate = selectedId && providers.find((p) => p.id === selectedId)?.isConfigured;
|
||||||
|
const toastInfo = isSecret ? 'API key' : 'host';
|
||||||
toast.success(
|
toast.success(
|
||||||
isUpdate
|
isUpdate
|
||||||
? `Successfully updated API key for ${provider}`
|
? `Successfully updated ${toastInfo} for ${provider}`
|
||||||
: `Successfully added API key for ${provider}`
|
: `Successfully added ${toastInfo} for ${provider}`
|
||||||
);
|
);
|
||||||
|
|
||||||
const updatedKeys = await getActiveProviders();
|
const updatedKeys = await getActiveProviders();
|
||||||
|
|||||||
Reference in New Issue
Block a user