Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -86,8 +86,12 @@ export default function ExtensionsSection() {
extensionConfig: extensionConfig,
addToConfig: addExtension,
});
handleModalClose();

// First refresh the extensions list
await fetchExtensions();

// Then close the modal after data is refreshed
handleModalClose();
};

const handleDeleteExtension = async (name: string) => {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type { ExtensionConfig } from '../../../api/types.gen';
import { ToastServiceOptions } from '../../../toasts';
import { toastService, ToastServiceOptions } from '../../../toasts';
import { addToAgent, removeFromAgent } from './agent-api';

interface ActivateExtensionProps {
Expand Down Expand Up @@ -147,6 +147,11 @@ export async function updateExtension({
console.error('[updateExtension]: Failed to update disabled extension in config:', error);
throw error;
}
// show a toast that it was successfully updated
toastService.success({
title: `Update extension`,
msg: `Successfully updated ${extensionConfig.name} extension`,
});
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ export default function EnvVarsSection({
onAdd,
onRemove,
onChange,
submitAttempted,
}: EnvVarsSectionProps) {
const [newKey, setNewKey] = React.useState('');
const [newValue, setNewValue] = React.useState('');
Expand Down Expand Up @@ -51,12 +52,22 @@ export default function EnvVarsSection({
setInvalidFields({ key: false, value: false });
};

const isFieldInvalid = (index: number, field: 'key' | 'value') => {
if (!submitAttempted) return false;
const value = envVars[index][field].trim();
return value === '';
};

return (
<div>
<div className="relative mb-2">
<label className="text-sm font-medium text-textStandard mb-2 block">
Environment Variables
</label>
<p className="text-xs text-textSubtle mb-2">
Add key-value pairs for environment variables. Click the "+" button to add after filling
both fields.
</p>
</div>
<div className="grid grid-cols-[1fr_1fr_auto] gap-2 items-center">
{/* Existing environment variables */}
Expand All @@ -67,17 +78,21 @@ export default function EnvVarsSection({
value={envVar.key}
onChange={(e) => onChange(index, 'key', e.target.value)}
placeholder="Variable name"
className={`w-full border-borderSubtle text-textStandard`}
disabled
className={cn(
'w-full border-borderSubtle text-textStandard',
isFieldInvalid(index, 'key') && 'border-red-500 focus:border-red-500'
)}
/>
</div>
<div className="relative">
<Input
value={envVar.value}
onChange={(e) => onChange(index, 'value', e.target.value)}
placeholder="Value"
className={`w-full border-borderSubtle text-textStandard`}
disabled
className={cn(
'w-full border-borderSubtle text-textStandard',
isFieldInvalid(index, 'value') && 'border-red-500 focus:border-red-500'
)}
/>
</div>
<Button
Expand Down Expand Up @@ -120,7 +135,7 @@ export default function EnvVarsSection({
variant="ghost"
className="flex items-center justify-start gap-1 px-2 pr-4 text-s font-medium rounded-full dark:bg-slate-400 dark:text-gray-300 bg-gray-300 dark:bg-slate text-slate-400 dark:hover:bg-slate-300 hover:bg-gray-500 hover:text-white dark:hover:text-gray-900 transition-colors min-w-[60px] h-9"
>
<Plus className="h-3 w-3" /> Submit
<Plus className="h-3 w-3" /> Add
</Button>
</div>
{validationError && <div className="mt-2 text-red-500 text-sm">{validationError}</div>}
Expand Down
79 changes: 33 additions & 46 deletions ui/desktop/src/components/settings_v2/models/ModelsSection.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { useEffect, useRef, useState } from 'react';
import React, { useEffect, useState } from 'react';
import type { View } from '../../../App';
import ModelSettingsButtons from './subcomponents/ModelSettingsButtons';
import { useConfig } from '../../ConfigContext';
Expand All @@ -15,59 +15,46 @@ export default function ModelsSection({ setView }: ModelsSectionProps) {
const [model, setModel] = useState<string>('');
const { read, getProviders } = useConfig();

// Use a ref to prevent multiple loads
const isLoadingRef = useRef(false);
const isLoadedRef = useRef(false);
// Function to load model data
const loadModelData = async () => {
try {
const gooseModel = (await read('GOOSE_MODEL', false)) as string;
const gooseProvider = (await read('GOOSE_PROVIDER', false)) as string;
const providers = await getProviders(true);

useEffect(() => {
// Prevent the effect from running again if it's already loading or loaded
if (isLoadingRef.current || isLoadedRef.current) return;

// Mark as loading
isLoadingRef.current = true;

const loadModelData = async () => {
try {
const gooseModel = (await read('GOOSE_MODEL', false)) as string;
const gooseProvider = (await read('GOOSE_PROVIDER', false)) as string;
const providers = await getProviders(true);

// lookup display name
const providerDetailsList = providers.filter((provider) => provider.name === gooseProvider);

if (providerDetailsList.length != 1) {
toastError({
title: UNKNOWN_PROVIDER_TITLE,
msg: UNKNOWN_PROVIDER_MSG,
});
setModel(gooseModel);
setProvider(gooseProvider);
} else {
const providerDisplayName = providerDetailsList[0].metadata.display_name;
setModel(gooseModel);
setProvider(providerDisplayName);
}
// lookup display name
const providerDetailsList = providers.filter((provider) => provider.name === gooseProvider);

// Mark as loaded and not loading
isLoadedRef.current = true;
isLoadingRef.current = false;
} catch (error) {
console.error('Error loading model data:', error);
isLoadingRef.current = false;
if (providerDetailsList.length != 1) {
toastError({
title: UNKNOWN_PROVIDER_TITLE,
msg: UNKNOWN_PROVIDER_MSG,
});
setModel(gooseModel);
setProvider(gooseProvider);
} else {
const providerDisplayName = providerDetailsList[0].metadata.display_name;
setModel(gooseModel);
setProvider(providerDisplayName);
}
};
} catch (error) {
console.error('Error loading model data:', error);
}
};

useEffect(() => {
// Initial load
loadModelData();

// Clean up function
// Set up polling interval to check for changes
const interval = setInterval(() => {
loadModelData();
}, 1000); // Check every second

// Clean up interval on unmount
return () => {
isLoadingRef.current = false;
isLoadedRef.current = false;
clearInterval(interval);
};

// Run this effect only once when the component mounts
// We're using refs to control the actual execution, so we don't need dependencies
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);

return (
Expand Down
Loading