mirror of
https://github.com/aljazceru/goose.git
synced 2026-01-06 07:54:23 +01:00
feat: update ui for ollama host (#912)
This commit is contained in:
@@ -8,23 +8,23 @@ use serde_json::Value;
|
||||
use std::collections::HashMap;
|
||||
|
||||
#[derive(Serialize)]
|
||||
struct SecretResponse {
|
||||
struct ConfigResponse {
|
||||
error: bool,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
struct SecretRequest {
|
||||
struct ConfigRequest {
|
||||
key: String,
|
||||
value: String,
|
||||
is_secret: bool,
|
||||
}
|
||||
|
||||
async fn store_secret(
|
||||
async fn store_config(
|
||||
State(state): State<AppState>,
|
||||
headers: HeaderMap,
|
||||
Json(request): Json<SecretRequest>,
|
||||
) -> Result<Json<SecretResponse>, StatusCode> {
|
||||
Json(request): Json<ConfigRequest>,
|
||||
) -> Result<Json<ConfigResponse>, StatusCode> {
|
||||
// Verify secret key
|
||||
let secret_key = headers
|
||||
.get("X-Secret-Key")
|
||||
@@ -42,18 +42,18 @@ async fn store_secret(
|
||||
config.set(&request.key, Value::String(request.value))
|
||||
};
|
||||
match result {
|
||||
Ok(_) => Ok(Json(SecretResponse { error: false })),
|
||||
Err(_) => Ok(Json(SecretResponse { error: true })),
|
||||
Ok(_) => Ok(Json(ConfigResponse { error: false })),
|
||||
Err(_) => Ok(Json(ConfigResponse { error: true })),
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct ProviderSecretRequest {
|
||||
pub struct ProviderConfigRequest {
|
||||
pub providers: Vec<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct SecretStatus {
|
||||
pub struct ConfigStatus {
|
||||
pub is_set: bool,
|
||||
pub location: Option<String>,
|
||||
}
|
||||
@@ -64,7 +64,7 @@ pub struct ProviderResponse {
|
||||
pub name: Option<String>,
|
||||
pub description: Option<String>,
|
||||
pub models: Option<Vec<String>>,
|
||||
pub secret_status: HashMap<String, SecretStatus>,
|
||||
pub config_status: HashMap<String, ConfigStatus>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
@@ -80,30 +80,33 @@ static PROVIDER_ENV_REQUIREMENTS: Lazy<HashMap<String, ProviderConfig>> = Lazy::
|
||||
serde_json::from_str(contents).expect("Failed to parse providers_and_keys.json")
|
||||
});
|
||||
|
||||
fn check_key_status(key: &str) -> (bool, Option<String>) {
|
||||
fn check_key_status(config: &Config, key: &str) -> (bool, Option<String>) {
|
||||
if let Ok(_value) = std::env::var(key) {
|
||||
(true, Some("env".to_string()))
|
||||
} else if Config::global().get_secret::<String>(key).is_ok() {
|
||||
} else if config.get::<String>(key).is_ok() {
|
||||
(true, Some("yaml".to_string()))
|
||||
} else if config.get_secret::<String>(key).is_ok() {
|
||||
(true, Some("keyring".to_string()))
|
||||
} else {
|
||||
(false, None)
|
||||
}
|
||||
}
|
||||
|
||||
async fn check_provider_secrets(
|
||||
Json(request): Json<ProviderSecretRequest>,
|
||||
async fn check_provider_configs(
|
||||
Json(request): Json<ProviderConfigRequest>,
|
||||
) -> Result<Json<HashMap<String, ProviderResponse>>, StatusCode> {
|
||||
let mut response = HashMap::new();
|
||||
let config = Config::global();
|
||||
|
||||
for provider_name in request.providers {
|
||||
if let Some(provider_config) = PROVIDER_ENV_REQUIREMENTS.get(&provider_name) {
|
||||
let mut secret_status = HashMap::new();
|
||||
let mut config_status = HashMap::new();
|
||||
|
||||
for key in &provider_config.required_keys {
|
||||
let (key_set, key_location) = check_key_status(key);
|
||||
secret_status.insert(
|
||||
let (key_set, key_location) = check_key_status(config, key);
|
||||
config_status.insert(
|
||||
key.to_string(),
|
||||
SecretStatus {
|
||||
ConfigStatus {
|
||||
is_set: key_set,
|
||||
location: key_location,
|
||||
},
|
||||
@@ -117,7 +120,7 @@ async fn check_provider_secrets(
|
||||
name: Some(provider_config.name.clone()),
|
||||
description: Some(provider_config.description.clone()),
|
||||
models: Some(provider_config.models.clone()),
|
||||
secret_status,
|
||||
config_status,
|
||||
},
|
||||
);
|
||||
} else {
|
||||
@@ -128,7 +131,7 @@ async fn check_provider_secrets(
|
||||
name: None,
|
||||
description: None,
|
||||
models: None,
|
||||
secret_status: HashMap::new(),
|
||||
config_status: HashMap::new(),
|
||||
},
|
||||
);
|
||||
}
|
||||
@@ -138,14 +141,16 @@ async fn check_provider_secrets(
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
struct DeleteSecretRequest {
|
||||
#[serde(rename_all = "camelCase")]
|
||||
struct DeleteConfigRequest {
|
||||
key: String,
|
||||
is_secret: bool,
|
||||
}
|
||||
|
||||
async fn delete_secret(
|
||||
async fn delete_config(
|
||||
State(state): State<AppState>,
|
||||
headers: HeaderMap,
|
||||
Json(request): Json<DeleteSecretRequest>,
|
||||
Json(request): Json<DeleteConfigRequest>,
|
||||
) -> Result<StatusCode, StatusCode> {
|
||||
// Verify secret key
|
||||
let secret_key = headers
|
||||
@@ -158,7 +163,13 @@ async fn delete_secret(
|
||||
}
|
||||
|
||||
// Attempt to delete the key
|
||||
match Config::global().delete_secret(&request.key) {
|
||||
let config = Config::global();
|
||||
let result = if request.is_secret {
|
||||
config.delete_secret(&request.key)
|
||||
} else {
|
||||
config.delete(&request.key)
|
||||
};
|
||||
match result {
|
||||
Ok(_) => Ok(StatusCode::NO_CONTENT),
|
||||
Err(_) => Err(StatusCode::NOT_FOUND),
|
||||
}
|
||||
@@ -166,9 +177,9 @@ async fn delete_secret(
|
||||
|
||||
pub fn routes(state: AppState) -> Router {
|
||||
Router::new()
|
||||
.route("/secrets/providers", post(check_provider_secrets))
|
||||
.route("/secrets/store", post(store_secret))
|
||||
.route("/secrets/delete", delete(delete_secret))
|
||||
.route("/configs/providers", post(check_provider_configs))
|
||||
.route("/configs/store", post(store_config))
|
||||
.route("/configs/delete", delete(delete_config))
|
||||
.with_state(state)
|
||||
}
|
||||
|
||||
@@ -179,12 +190,12 @@ mod tests {
|
||||
#[tokio::test]
|
||||
async fn test_unsupported_provider() {
|
||||
// Setup
|
||||
let request = ProviderSecretRequest {
|
||||
let request = ProviderConfigRequest {
|
||||
providers: vec!["unsupported_provider".to_string()],
|
||||
};
|
||||
|
||||
// Execute
|
||||
let result = check_provider_secrets(Json(request)).await;
|
||||
let result = check_provider_configs(Json(request)).await;
|
||||
|
||||
// Assert
|
||||
assert!(result.is_ok());
|
||||
@@ -194,6 +205,6 @@ mod tests {
|
||||
.get("unsupported_provider")
|
||||
.expect("Provider should exist");
|
||||
assert!(!provider_status.supported);
|
||||
assert!(provider_status.secret_status.is_empty());
|
||||
assert!(provider_status.config_status.is_empty());
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,9 @@
|
||||
// Export route modules
|
||||
pub mod agent;
|
||||
pub mod configs;
|
||||
pub mod extension;
|
||||
pub mod health;
|
||||
pub mod reply;
|
||||
pub mod secrets;
|
||||
|
||||
use axum::Router;
|
||||
|
||||
@@ -14,5 +14,5 @@ pub fn configure(state: crate::state::AppState) -> Router {
|
||||
.merge(reply::routes(state.clone()))
|
||||
.merge(agent::routes(state.clone()))
|
||||
.merge(extension::routes(state.clone()))
|
||||
.merge(secrets::routes(state))
|
||||
.merge(configs::routes(state))
|
||||
}
|
||||
|
||||
@@ -33,7 +33,7 @@
|
||||
"name": "Ollama",
|
||||
"description": "Lorem ipsum",
|
||||
"models": ["qwen2.5"],
|
||||
"required_keys": []
|
||||
"required_keys": ["OLLAMA_HOST"]
|
||||
},
|
||||
"openrouter": {
|
||||
"name": "OpenRouter",
|
||||
|
||||
@@ -165,6 +165,21 @@ impl Config {
|
||||
}
|
||||
}
|
||||
|
||||
// Save current values to the config file
|
||||
fn save_values(&self, values: HashMap<String, Value>) -> Result<(), ConfigError> {
|
||||
// Convert to YAML for storage
|
||||
let yaml_value = serde_yaml::to_string(&values)?;
|
||||
|
||||
// Ensure the directory exists
|
||||
if let Some(parent) = self.config_path.parent() {
|
||||
std::fs::create_dir_all(parent)
|
||||
.map_err(|e| ConfigError::DirectoryError(e.to_string()))?;
|
||||
}
|
||||
|
||||
std::fs::write(&self.config_path, yaml_value)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// Load current secrets from the keyring
|
||||
fn load_secrets(&self) -> Result<HashMap<String, Value>, ConfigError> {
|
||||
let entry = Entry::new(&self.keyring_service, KEYRING_USERNAME)?;
|
||||
@@ -231,17 +246,27 @@ impl Config {
|
||||
let mut values = self.load_values()?;
|
||||
values.insert(key.to_string(), value);
|
||||
|
||||
// Convert to YAML for storage
|
||||
let yaml_value = serde_yaml::to_string(&values)?;
|
||||
self.save_values(values)
|
||||
}
|
||||
|
||||
// Ensure the directory exists
|
||||
if let Some(parent) = self.config_path.parent() {
|
||||
std::fs::create_dir_all(parent)
|
||||
.map_err(|e| ConfigError::DirectoryError(e.to_string()))?;
|
||||
}
|
||||
/// Delete a configuration value in the config file.
|
||||
///
|
||||
/// This will immediately write the value to the config file. The value
|
||||
/// can be any type that can be serialized to JSON/YAML.
|
||||
///
|
||||
/// Note that this does not affect environment variables - those can only
|
||||
/// be set through the system environment.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Returns a ConfigError if:
|
||||
/// - There is an error reading or writing the config file
|
||||
/// - There is an error serializing the value
|
||||
pub fn delete(&self, key: &str) -> Result<(), ConfigError> {
|
||||
let mut values = self.load_values()?;
|
||||
values.remove(key);
|
||||
|
||||
std::fs::write(&self.config_path, yaml_value)?;
|
||||
Ok(())
|
||||
self.save_values(values)
|
||||
}
|
||||
|
||||
/// Get a secret value.
|
||||
@@ -408,6 +433,24 @@ mod tests {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_value_management() -> Result<(), ConfigError> {
|
||||
let temp_file = NamedTempFile::new().unwrap();
|
||||
let config = Config::new(temp_file.path(), TEST_KEYRING_SERVICE)?;
|
||||
|
||||
config.set("key", Value::String("value".to_string()))?;
|
||||
|
||||
let value: String = config.get("key")?;
|
||||
assert_eq!(value, "value");
|
||||
|
||||
config.delete("key")?;
|
||||
|
||||
let result: Result<String, ConfigError> = config.get("key");
|
||||
assert!(matches!(result, Err(ConfigError::NotFound(_))));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[serial]
|
||||
fn test_secret_management() -> Result<(), ConfigError> {
|
||||
|
||||
@@ -385,7 +385,7 @@ export default function ChatWindow() {
|
||||
}, []);
|
||||
|
||||
const storeSecret = async (key: string, value: string) => {
|
||||
const response = await fetch(getApiUrl('/secrets/store'), {
|
||||
const response = await fetch(getApiUrl('/configs/store'), {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
|
||||
@@ -3,10 +3,10 @@ export interface ProviderResponse {
|
||||
name?: string;
|
||||
description?: string;
|
||||
models?: string[];
|
||||
secret_status: Record<string, SecretDetails>;
|
||||
config_status: Record<string, ConfigDetails>;
|
||||
}
|
||||
|
||||
export interface SecretDetails {
|
||||
export interface ConfigDetails {
|
||||
key: string;
|
||||
is_set: boolean;
|
||||
location?: string;
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
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.
|
||||
@@ -10,21 +9,12 @@ export function isSecretKey(keyName: string): boolean {
|
||||
export async function getActiveProviders(): Promise<string[]> {
|
||||
try {
|
||||
// Fetch the secrets settings
|
||||
const secretsSettings = await getSecretsSettings();
|
||||
|
||||
// Check for special provider cases (e.g. ollama needs to be installed in Applications folder)
|
||||
const specialCasesResults = await Promise.all(
|
||||
Object.entries(special_provider_cases).map(async ([providerName, checkFunction]) => {
|
||||
const isActive = await checkFunction(); // Dynamically re-check status
|
||||
console.log(`Special case result for ${providerName}:`, isActive);
|
||||
return isActive ? providerName : null;
|
||||
})
|
||||
);
|
||||
const configSettings = await getConfigSettings();
|
||||
|
||||
// Extract active providers based on `is_set` in `secret_status` or providers with no keys
|
||||
const activeProviders = Object.values(secretsSettings) // Convert object to array
|
||||
const activeProviders = Object.values(configSettings) // Convert object to array
|
||||
.filter((provider) => {
|
||||
const apiKeyStatus = Object.values(provider.secret_status || {}); // Get all key statuses
|
||||
const apiKeyStatus = Object.values(provider.config_status || {}); // Get all key statuses
|
||||
|
||||
// Include providers if:
|
||||
// - They have at least one key set (`is_set: true`)
|
||||
@@ -32,25 +22,20 @@ export async function getActiveProviders(): Promise<string[]> {
|
||||
})
|
||||
.map((provider) => provider.name || 'Unknown Provider'); // Extract provider name
|
||||
|
||||
// Combine active providers from secrets settings and special cases
|
||||
const allActiveProviders = [
|
||||
...activeProviders,
|
||||
...specialCasesResults.filter((provider) => provider !== null), // Filter out null results
|
||||
];
|
||||
return allActiveProviders;
|
||||
return activeProviders;
|
||||
} catch (error) {
|
||||
console.error('Failed to get active providers:', error);
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
export async function getSecretsSettings(): Promise<Record<string, ProviderResponse>> {
|
||||
export async function getConfigSettings(): Promise<Record<string, ProviderResponse>> {
|
||||
const providerList = await getProvidersList();
|
||||
// Extract the list of IDs
|
||||
const providerIds = providerList.map((provider) => provider.id);
|
||||
|
||||
// Fetch secrets state (set/unset) using the provider IDs
|
||||
const response = await fetch(getApiUrl('/secrets/providers'), {
|
||||
// Fetch configs state (set/unset) using the provider IDs
|
||||
const response = await fetch(getApiUrl('/configs/providers'), {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
|
||||
@@ -43,7 +43,7 @@ export function ConfigureBuiltInExtensionModal({
|
||||
const value = envValues[envKey];
|
||||
if (!value) continue;
|
||||
|
||||
const storeResponse = await fetch(getApiUrl('/secrets/store'), {
|
||||
const storeResponse = await fetch(getApiUrl('/configs/store'), {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
|
||||
@@ -45,7 +45,7 @@ export function ConfigureExtensionModal({
|
||||
const value = envValues[envKey];
|
||||
if (!value) continue;
|
||||
|
||||
const storeResponse = await fetch(getApiUrl('/secrets/store'), {
|
||||
const storeResponse = await fetch(getApiUrl('/configs/store'), {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
|
||||
@@ -59,7 +59,7 @@ export function ManualExtensionModal({ isOpen, onClose, onSubmit }: ManualExtens
|
||||
try {
|
||||
// Store environment variables as secrets
|
||||
for (const envVar of envVars) {
|
||||
const storeResponse = await fetch(getApiUrl('/secrets/store'), {
|
||||
const storeResponse = await fetch(getApiUrl('/configs/store'), {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
|
||||
@@ -112,35 +112,6 @@ function BaseProviderCard({
|
||||
</Tooltip>
|
||||
</TooltipProvider>
|
||||
)}
|
||||
|
||||
{/* Not Configured state: Red exclamation mark for Ollama */}
|
||||
{!isConfigured && name === 'Ollama' && (
|
||||
<TooltipProvider>
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<div className="flex items-center justify-center w-5 h-5 rounded-full bg-bgApp hover:bg-bgApp shadow-none text-textSubtle border border-borderSubtle hover:border-borderStandard hover:text-textStandard transition-colors">
|
||||
!
|
||||
</div>
|
||||
</TooltipTrigger>
|
||||
<Portal>
|
||||
<TooltipContent side="top" align="center" className="z-[9999]">
|
||||
<p>
|
||||
To use, the{' '}
|
||||
<a
|
||||
href="https://ollama.com/download"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="text-blue-600 underline hover:text-blue-800"
|
||||
>
|
||||
Ollama app
|
||||
</a>{' '}
|
||||
must be installed on your machine and open.
|
||||
</p>
|
||||
</TooltipContent>
|
||||
</Portal>
|
||||
</Tooltip>
|
||||
</TooltipProvider>
|
||||
)}
|
||||
</div>
|
||||
<p className="text-xs text-textSubtle mt-1.5 mb-3 leading-normal overflow-y-auto max-h-[54px] ">
|
||||
{description}
|
||||
@@ -149,41 +120,6 @@ function BaseProviderCard({
|
||||
|
||||
<div className="space-x-2 text-center flex items-center justify-between">
|
||||
<div className="space-x-2">
|
||||
{!isConfigured && name === 'Ollama' && (
|
||||
<TooltipProvider>
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<Button
|
||||
variant="default"
|
||||
size="sm"
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
// Trigger a refresh of active keys
|
||||
const refreshActiveKeys = async () => {
|
||||
try {
|
||||
const providers = await getActiveProviders(); // Re-fetch active providers
|
||||
setActiveKeys(providers); // Update the context state
|
||||
} catch (error) {
|
||||
console.error('Error refreshing active providers:', error);
|
||||
}
|
||||
};
|
||||
|
||||
refreshActiveKeys(); // Call the refresh function
|
||||
}}
|
||||
className="rounded-full h-7 w-7 p-0 bg-bgApp hover:bg-bgApp shadow-none text-textSubtle border border-borderSubtle hover:border-borderStandard hover:text-textStandard transition-colors"
|
||||
>
|
||||
<RefreshCw className="!size-4" /> {/* Refresh icon */}
|
||||
</Button>
|
||||
</TooltipTrigger>
|
||||
<Portal>
|
||||
<TooltipContent side="top" align="center" className="z-[9999]">
|
||||
<p>Re-check for active Ollama app running in the background.</p>
|
||||
</TooltipContent>
|
||||
</Portal>
|
||||
</Tooltip>
|
||||
</TooltipProvider>
|
||||
)}
|
||||
|
||||
{/* Default "Add Keys" Button for other providers */}
|
||||
{!isConfigured && onAddKeys && hasRequiredKeys && (
|
||||
<TooltipProvider>
|
||||
@@ -251,7 +187,7 @@ function BaseProviderCard({
|
||||
</TooltipTrigger>
|
||||
<Portal>
|
||||
<TooltipContent side="top" align="center" className="z-[9999]">
|
||||
<p>Remove {name} API Key</p>
|
||||
<p>Remove {name} API Key or Host</p>
|
||||
</TooltipContent>
|
||||
</Portal>
|
||||
</Tooltip>
|
||||
|
||||
@@ -90,7 +90,7 @@ export function ConfigureProvidersGrid() {
|
||||
// Delete existing key if provider is already configured
|
||||
const isUpdate = providers.find((p) => p.id === selectedForSetup)?.isConfigured;
|
||||
if (isUpdate) {
|
||||
const deleteResponse = await fetch(getApiUrl('/secrets/delete'), {
|
||||
const deleteResponse = await fetch(getApiUrl('/configs/delete'), {
|
||||
method: 'DELETE',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
@@ -108,7 +108,7 @@ export function ConfigureProvidersGrid() {
|
||||
|
||||
// Store new key
|
||||
const isSecret = isSecretKey(keyName);
|
||||
const storeResponse = await fetch(getApiUrl('/secrets/store'), {
|
||||
const storeResponse = await fetch(getApiUrl('/configs/store'), {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
@@ -162,23 +162,28 @@ export function ConfigureProvidersGrid() {
|
||||
return;
|
||||
}
|
||||
|
||||
const isSecret = isSecretKey(keyName);
|
||||
const toastInfo = isSecret ? 'API key' : 'host';
|
||||
try {
|
||||
// Check if the selected provider is currently active
|
||||
if (currentModel?.provider === providerToDelete.name) {
|
||||
toast.error(
|
||||
`Cannot delete the API key for ${providerToDelete.name} because it's the provider of the current model (${currentModel.name}). Please switch to a different model first.`
|
||||
`Cannot delete the ${toastInfo} for ${providerToDelete.name} because it's the provider of the current model (${currentModel.name}). Please switch to a different model first.`
|
||||
);
|
||||
setIsConfirmationOpen(false);
|
||||
return;
|
||||
}
|
||||
|
||||
const deleteResponse = await fetch(getApiUrl('/secrets/delete'), {
|
||||
const deleteResponse = await fetch(getApiUrl('/configs/delete'), {
|
||||
method: 'DELETE',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'X-Secret-Key': getSecretKey(),
|
||||
},
|
||||
body: JSON.stringify({ key: keyName }),
|
||||
body: JSON.stringify({
|
||||
key: keyName,
|
||||
isSecret,
|
||||
}),
|
||||
});
|
||||
|
||||
if (!deleteResponse.ok) {
|
||||
@@ -188,13 +193,13 @@ export function ConfigureProvidersGrid() {
|
||||
}
|
||||
|
||||
console.log('Key deleted successfully.');
|
||||
toast.success(`Successfully deleted API key for ${providerToDelete.name}`);
|
||||
toast.success(`Successfully deleted ${toastInfo} for ${providerToDelete.name}`);
|
||||
|
||||
const updatedKeys = await getActiveProviders();
|
||||
setActiveKeys(updatedKeys);
|
||||
} catch (error) {
|
||||
console.error('Error deleting key:', error);
|
||||
toast.error(`Unable to delete API key for ${providerToDelete.name}`);
|
||||
toast.error(`Unable to delete ${toastInfo} for ${providerToDelete.name}`);
|
||||
}
|
||||
setIsConfirmationOpen(false);
|
||||
};
|
||||
@@ -233,7 +238,7 @@ export function ConfigureProvidersGrid() {
|
||||
|
||||
{isConfirmationOpen && providerToDelete && (
|
||||
<ConfirmationModal
|
||||
message={`Are you sure you want to delete the API key for ${providerToDelete.name}? This action cannot be undone.`}
|
||||
message={`Are you sure you want to delete the API key or host for ${providerToDelete.name}? This action cannot be undone.`}
|
||||
onConfirm={confirmDelete}
|
||||
onCancel={() => setIsConfirmationOpen(false)}
|
||||
/>
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
export const special_provider_cases = {
|
||||
Ollama: async () => await checkForOllama(), // Dynamically re-check
|
||||
};
|
||||
|
||||
export async function checkForOllama() {
|
||||
console.log('Invoking check-ollama IPC handler...');
|
||||
try {
|
||||
const ollamaInstalled = await window.electron.checkForOllama();
|
||||
return ollamaInstalled;
|
||||
} catch (error) {
|
||||
console.error('Error invoking check-ollama:', error);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -83,7 +83,7 @@ export function ProviderGrid({ onSubmit }: ProviderGridProps) {
|
||||
|
||||
try {
|
||||
if (selectedId && providers.find((p) => p.id === selectedId)?.isConfigured) {
|
||||
const deleteResponse = await fetch(getApiUrl('/secrets/delete'), {
|
||||
const deleteResponse = await fetch(getApiUrl('/configs/delete'), {
|
||||
method: 'DELETE',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
@@ -100,7 +100,7 @@ export function ProviderGrid({ onSubmit }: ProviderGridProps) {
|
||||
}
|
||||
|
||||
const isSecret = isSecretKey(keyName);
|
||||
const storeResponse = await fetch(getApiUrl('/secrets/store'), {
|
||||
const storeResponse = await fetch(getApiUrl('/configs/store'), {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
|
||||
Reference in New Issue
Block a user