diff --git a/src/web-ui/src/app/scenes/settings/pages/ai/MagpieSourceSection.scss b/src/web-ui/src/app/scenes/settings/pages/ai/MagpieSourceSection.scss
index dac151a12b..77d0c853e3 100644
--- a/src/web-ui/src/app/scenes/settings/pages/ai/MagpieSourceSection.scss
+++ b/src/web-ui/src/app/scenes/settings/pages/ai/MagpieSourceSection.scss
@@ -39,4 +39,8 @@
flex: 0 0 auto;
max-inline-size: 100%;
}
+
+ > [data-openbitfun-component='tooltip-trigger'] {
+ flex: 0 0 auto;
+ }
}
diff --git a/src/web-ui/src/app/scenes/settings/pages/ai/MagpieSourceSection.tsx b/src/web-ui/src/app/scenes/settings/pages/ai/MagpieSourceSection.tsx
index 4078b22423..c5b25774c5 100644
--- a/src/web-ui/src/app/scenes/settings/pages/ai/MagpieSourceSection.tsx
+++ b/src/web-ui/src/app/scenes/settings/pages/ai/MagpieSourceSection.tsx
@@ -1,5 +1,5 @@
import { useEffect, useId, useMemo, useRef, useState, useSyncExternalStore } from 'react';
-import { Button, Checkbox, Input, SearchField, StatusPill } from '@openbitfun/ui';
+import { Button, Checkbox, Icon, IconButton, Input, SearchField, StatusPill, Tooltip } from '@openbitfun/ui';
import { ConfigPageRow } from '@/infrastructure/config/components/common';
import { configManager } from '@/infrastructure/config/services/ConfigManager';
import { useSettingsDraft } from '@/infrastructure/config/settingsDraftRegistry';
@@ -16,6 +16,7 @@ interface Props {
ready: boolean;
onModelsChange: (models: AIModelConfig[]) => void;
onManual: () => void;
+ onRemoveModel: (model: AIModelConfig, trigger?: HTMLElement) => void;
}
export function MagpieSourceSection(props: Props) {
@@ -23,7 +24,7 @@ export function MagpieSourceSection(props: Props) {
return ;
}
-function MagpieSourceEditor({ models, ready, onModelsChange, onManual }: Props) {
+function MagpieSourceEditor({ models, ready, onModelsChange, onManual, onRemoveModel }: Props) {
const { t, formatNumber } = useI18n('settings/models');
const [scope] = useState(getActiveSurfaceScope);
const panelId = useId();
@@ -46,10 +47,12 @@ function MagpieSourceEditor({ models, ready, onModelsChange, onManual }: Props)
const supported = useSyncExternalStore(
listener => peerConnectionManager.subscribe(listener), supportsMagpieDiscovery, supportsMagpieDiscovery,
);
- const imported = new Set((connection?.models ?? models.filter(model =>
+ const activeConnection = connection ? connections.find(item => item.id === connection.id) : undefined;
+ const importedModels = activeConnection?.models ?? (connection ? [] : models.filter(model =>
magpieGatewayUrl(model.base_url) === magpieGatewayUrl(baseUrl)
&& (model.api_key?.trim() || MAGPIE_APP_KEY) === (apiKey.trim() || MAGPIE_APP_KEY)
- && (!model.auth || model.auth.type === 'api_key'))).map(model => model.model_name));
+ && (!model.auth || model.auth.type === 'api_key')));
+ const imported = new Set(importedModels.map(model => model.model_name));
const catalogReady = result?.status === 'ready' || result?.status === 'empty';
const credentialsChanged = baseUrl !== baseline.baseUrl || apiKey !== baseline.apiKey;
const dirty = selected.length > 0 || credentialsChanged;
@@ -173,7 +176,27 @@ function MagpieSourceEditor({ models, ready, onModelsChange, onManual }: Props)
label={model.display_name || model.id} description={model.id}
onChange={event => setSelected(previous => event.target.checked
? [...previous, model.id] : previous.filter(id => id !== model.id))} />
- {imported.has(model.id) && {t('magpie.imported')}}
+ {imported.has(model.id) && <>
+ {t('magpie.imported')}
+
+ }
+ onClick={event => {
+ event.stopPropagation();
+ const config = importedModels.find(item => item.model_name === model.id);
+ if (config) onRemoveModel(config, event.currentTarget);
+ }}
+ />
+
+ >}
{model.id.startsWith('group/') && {t('magpie.routingGroup')}}
{issue && {importIssues[issue]}}
;
diff --git a/src/web-ui/src/app/scenes/settings/pages/ai/ModelSettingsPage.loading.test.tsx b/src/web-ui/src/app/scenes/settings/pages/ai/ModelSettingsPage.loading.test.tsx
index 52b36e4d94..2e8c7a4b4a 100644
--- a/src/web-ui/src/app/scenes/settings/pages/ai/ModelSettingsPage.loading.test.tsx
+++ b/src/web-ui/src/app/scenes/settings/pages/ai/ModelSettingsPage.loading.test.tsx
@@ -170,6 +170,8 @@ describe('ModelSettingsPage loading and provider actions', () => {
base_url: base, api_key: key, provider: 'openai', capabilities: ['text_chat', 'function_calling'] });
expect(inference).not.toHaveBeenCalled();
expect(panel().querySelector('input[type="checkbox"]')!.disabled).toBe(true);
+ expect(panel().querySelector('[data-testid="settings-magpie-remove-model"]')?.dataset.modelId)
+ .toBe('provider/model-with-tools');
});
it.each(['empty', 'unreachable', 'access_denied', 'unrecognized', 'catalog_error'] as const)(
diff --git a/src/web-ui/src/app/scenes/settings/pages/ai/ModelSettingsPage.tsx b/src/web-ui/src/app/scenes/settings/pages/ai/ModelSettingsPage.tsx
index 0fe6987b4d..ed1fccaabb 100644
--- a/src/web-ui/src/app/scenes/settings/pages/ai/ModelSettingsPage.tsx
+++ b/src/web-ui/src/app/scenes/settings/pages/ai/ModelSettingsPage.tsx
@@ -4864,7 +4864,8 @@ const ModelSettingsPage: React.FC = () => {
) : {t('acquisition.noneConfigured')}}
requestEditorOpen('new-provider:magpie', () => handleSelectProvider('magpie'))} />
+ onManual={() => requestEditorOpen('new-provider:magpie', () => handleSelectProvider('magpie'))}
+ onRemoveModel={(config, trigger) => { void requestDelete(config, trigger); }} />