diff --git a/packages/browseros-agent/apps/agent/entrypoints/app/ai-settings/AISettingsPage.tsx b/packages/browseros-agent/apps/agent/entrypoints/app/ai-settings/AISettingsPage.tsx index 05fc11483..df6016370 100644 --- a/packages/browseros-agent/apps/agent/entrypoints/app/ai-settings/AISettingsPage.tsx +++ b/packages/browseros-agent/apps/agent/entrypoints/app/ai-settings/AISettingsPage.tsx @@ -145,6 +145,7 @@ export const AISettingsPage: FC = () => { const [isNewDialogOpen, setIsNewDialogOpen] = useState(false) const [isEditDialogOpen, setIsEditDialogOpen] = useState(false) + const [isCloningAction, setIsCloningAction] = useState(false) const [templateValues, setTemplateValues] = useState< Partial | undefined >() @@ -210,9 +211,14 @@ export const AISettingsPage: FC = () => { }, } + const setIsNewDialogOpenAndCloning = (_isNew: boolean, _isCloning: boolean) => { + setIsCloningAction(_isCloning) + setIsNewDialogOpen(_isNew) + } + const handleAddProvider = () => { setTemplateValues(undefined) - setIsNewDialogOpen(true) + setIsNewDialogOpenAndCloning(true, false) } const handleUseTemplate = (template: ProviderTemplate) => { @@ -232,10 +238,25 @@ export const AISettingsPage: FC = () => { contextWindow: template.contextWindow, temperature: 0.2, }) - setIsNewDialogOpen(true) + setIsNewDialogOpenAndCloning(true, false) } - const handleEditProvider = (provider: LlmProviderConfig) => { + const handleEditProvider = ( + provider: LlmProviderConfig, + isCloning = false, + ) => { + if (isCloning) { + setTemplateValues({ + ...provider, + name: `${provider.name.replace(/\s+Copy$/g, '')} Copy`, + id: undefined, + createdAt: undefined, + updatedAt: undefined, + }) + setIsNewDialogOpenAndCloning(true, true) + return + } + setEditingProvider(provider) setIsEditDialogOpen(true) } @@ -275,7 +296,7 @@ export const AISettingsPage: FC = () => { createdAt: timestamp, updatedAt: timestamp, }) - setIsNewDialogOpen(true) + setIsNewDialogOpenAndCloning(true, false) } const handleDeleteIncompleteProvider = (provider: IncompleteProvider) => { @@ -381,9 +402,13 @@ export const AISettingsPage: FC = () => { { + setIsCloningAction(false) + setIsNewDialogOpen(open) + }} initialValues={templateValues} onSave={handleSaveProvider} + isCloningAction={isCloningAction} /> void onTestProvider: (provider: LlmProviderConfig) => void - onEditProvider: (provider: LlmProviderConfig) => void + onEditProvider: (provider: LlmProviderConfig, isCloning?: boolean) => void onDeleteProvider: (provider: LlmProviderConfig) => void } @@ -38,7 +38,7 @@ export const ConfiguredProvidersList: FC = ({ isTesting={testingProviderId === provider.id} onSelect={() => onSelectProvider(provider.id)} onTest={() => onTestProvider(provider)} - onEdit={() => onEditProvider(provider)} + onEdit={(isCloning) => onEditProvider(provider, isCloning)} onDelete={() => onDeleteProvider(provider)} /> ) diff --git a/packages/browseros-agent/apps/agent/entrypoints/app/ai-settings/NewProviderDialog.tsx b/packages/browseros-agent/apps/agent/entrypoints/app/ai-settings/NewProviderDialog.tsx index cc6021963..9e06286f8 100644 --- a/packages/browseros-agent/apps/agent/entrypoints/app/ai-settings/NewProviderDialog.tsx +++ b/packages/browseros-agent/apps/agent/entrypoints/app/ai-settings/NewProviderDialog.tsx @@ -57,6 +57,7 @@ import { useCapabilities } from '@/lib/browseros/useCapabilities' import { AI_PROVIDER_ADDED_EVENT, AI_PROVIDER_UPDATED_EVENT, + AI_PROVIDER_CLONED_EVENT, KIMI_API_KEY_CONFIGURED_EVENT, KIMI_API_KEY_GUIDE_CLICKED_EVENT, MODEL_SELECTED_EVENT, @@ -207,6 +208,8 @@ export interface NewProviderDialogProps { initialValues?: Partial /** Callback when provider is saved */ onSave: (provider: LlmProviderConfig) => Promise + /** Whether the dialog is for cloning an existing provider */ + isCloningAction?: boolean } /** @@ -218,6 +221,7 @@ export const NewProviderDialog: FC = ({ onOpenChange, initialValues, onSave, + isCloningAction, }) => { const [isTesting, setIsTesting] = useState(false) const [testResult, setTestResult] = useState(null) @@ -400,7 +404,7 @@ export const NewProviderDialog: FC = ({ await onSave(provider) if (isNewProvider) { - track(AI_PROVIDER_ADDED_EVENT, { + track(isCloningAction ? AI_PROVIDER_CLONED_EVENT : AI_PROVIDER_ADDED_EVENT, { provider_type: values.type, model: values.modelId, }) diff --git a/packages/browseros-agent/apps/agent/entrypoints/app/ai-settings/ProviderCard.tsx b/packages/browseros-agent/apps/agent/entrypoints/app/ai-settings/ProviderCard.tsx index 273621b1c..12bfd99b4 100644 --- a/packages/browseros-agent/apps/agent/entrypoints/app/ai-settings/ProviderCard.tsx +++ b/packages/browseros-agent/apps/agent/entrypoints/app/ai-settings/ProviderCard.tsx @@ -13,7 +13,7 @@ interface ProviderCardProps { isBuiltIn: boolean onSelect: () => void onTest?: () => void - onEdit?: () => void + onEdit?: (isCloning?: boolean) => void onDelete?: () => void isTesting?: boolean } @@ -112,6 +112,9 @@ export const ProviderCard: FC = ({ {!isBuiltIn && (
+