Merge pull request #6255 from menloresearch/fix/remove-experimental-toggle
fix: remove experimental toggle
This commit is contained in:
commit
8e7378b70f
@ -57,7 +57,7 @@ const ChatInput = ({ model, className, initialMessage }: ChatInputProps) => {
|
||||
const { prompt, setPrompt } = usePrompt()
|
||||
const { currentThreadId } = useThreads()
|
||||
const { t } = useTranslation()
|
||||
const { spellCheckChatInput, experimentalFeatures } = useGeneralSetting()
|
||||
const { spellCheckChatInput } = useGeneralSetting()
|
||||
|
||||
const maxRows = 10
|
||||
|
||||
@ -586,8 +586,7 @@ const ChatInput = ({ model, className, initialMessage }: ChatInputProps) => {
|
||||
</TooltipProvider>
|
||||
)}
|
||||
|
||||
{experimentalFeatures &&
|
||||
selectedModel?.capabilities?.includes('tools') &&
|
||||
{selectedModel?.capabilities?.includes('tools') &&
|
||||
hasActiveMCPServers && (
|
||||
<TooltipProvider>
|
||||
<Tooltip
|
||||
|
||||
@ -11,7 +11,6 @@ import {
|
||||
import { useMatches, useNavigate } from '@tanstack/react-router'
|
||||
import { cn } from '@/lib/utils'
|
||||
|
||||
import { useGeneralSetting } from '@/hooks/useGeneralSetting'
|
||||
import { useModelProvider } from '@/hooks/useModelProvider'
|
||||
import { getProviderTitle } from '@/lib/utils'
|
||||
import ProvidersAvatar from '@/containers/ProvidersAvatar'
|
||||
@ -23,7 +22,6 @@ const SettingsMenu = () => {
|
||||
const matches = useMatches()
|
||||
const navigate = useNavigate()
|
||||
|
||||
const { experimentalFeatures } = useGeneralSetting()
|
||||
const { providers } = useModelProvider()
|
||||
|
||||
// Filter providers that have active API keys (or are llama.cpp which doesn't need one)
|
||||
@ -79,15 +77,10 @@ const SettingsMenu = () => {
|
||||
title: 'common:hardware',
|
||||
route: route.settings.hardware,
|
||||
},
|
||||
// Only show MCP Servers when experimental features are enabled
|
||||
...(experimentalFeatures
|
||||
? [
|
||||
{
|
||||
title: 'common:mcp-servers',
|
||||
route: route.settings.mcp_servers,
|
||||
},
|
||||
]
|
||||
: []),
|
||||
{
|
||||
title: 'common:local_api_server',
|
||||
route: route.settings.local_api_server,
|
||||
|
||||
@ -5,7 +5,6 @@ import SettingsMenu from '../SettingsMenu'
|
||||
import { useNavigate, useMatches } from '@tanstack/react-router'
|
||||
import { useGeneralSetting } from '@/hooks/useGeneralSetting'
|
||||
import { useModelProvider } from '@/hooks/useModelProvider'
|
||||
import { useAppState } from '@/hooks/useAppState'
|
||||
|
||||
// Mock dependencies
|
||||
vi.mock('@tanstack/react-router', () => ({
|
||||
@ -25,9 +24,7 @@ vi.mock('@/i18n/react-i18next-compat', () => ({
|
||||
}))
|
||||
|
||||
vi.mock('@/hooks/useGeneralSetting', () => ({
|
||||
useGeneralSetting: vi.fn(() => ({
|
||||
experimentalFeatures: false,
|
||||
})),
|
||||
useGeneralSetting: vi.fn(() => ({})),
|
||||
}))
|
||||
|
||||
vi.mock('@/hooks/useModelProvider', () => ({
|
||||
@ -88,21 +85,6 @@ describe('SettingsMenu', () => {
|
||||
expect(screen.getByText('common:local_api_server')).toBeInTheDocument()
|
||||
expect(screen.getByText('common:https_proxy')).toBeInTheDocument()
|
||||
expect(screen.getByText('common:extensions')).toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('does not show MCP Servers when experimental features disabled', () => {
|
||||
render(<SettingsMenu />)
|
||||
|
||||
expect(screen.queryByText('common:mcp-servers')).not.toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('shows MCP Servers when experimental features enabled', () => {
|
||||
vi.mocked(useGeneralSetting).mockReturnValue({
|
||||
experimentalFeatures: true,
|
||||
})
|
||||
|
||||
render(<SettingsMenu />)
|
||||
|
||||
expect(screen.getByText('common:mcp-servers')).toBeInTheDocument()
|
||||
})
|
||||
|
||||
@ -110,7 +92,7 @@ describe('SettingsMenu', () => {
|
||||
render(<SettingsMenu />)
|
||||
|
||||
const chevronButtons = screen.getAllByRole('button')
|
||||
const chevron = chevronButtons.find(button =>
|
||||
const chevron = chevronButtons.find((button) =>
|
||||
button.querySelector('svg.tabler-icon-chevron-right')
|
||||
)
|
||||
expect(chevron).toBeInTheDocument()
|
||||
@ -121,7 +103,7 @@ describe('SettingsMenu', () => {
|
||||
render(<SettingsMenu />)
|
||||
|
||||
const chevronButtons = screen.getAllByRole('button')
|
||||
const chevron = chevronButtons.find(button =>
|
||||
const chevron = chevronButtons.find((button) =>
|
||||
button.querySelector('svg.tabler-icon-chevron-right')
|
||||
)
|
||||
if (!chevron) throw new Error('Chevron button not found')
|
||||
@ -159,12 +141,14 @@ describe('SettingsMenu', () => {
|
||||
|
||||
// First expand the providers submenu
|
||||
const chevronButtons = screen.getAllByRole('button')
|
||||
const chevron = chevronButtons.find(button =>
|
||||
const chevron = chevronButtons.find((button) =>
|
||||
button.querySelector('svg.tabler-icon-chevron-right')
|
||||
)
|
||||
if (chevron) await user.click(chevron)
|
||||
|
||||
const openaiProvider = screen.getByTestId('provider-avatar-openai').closest('div')
|
||||
const openaiProvider = screen
|
||||
.getByTestId('provider-avatar-openai')
|
||||
.closest('div')
|
||||
expect(openaiProvider).toBeInTheDocument()
|
||||
})
|
||||
|
||||
@ -174,14 +158,16 @@ describe('SettingsMenu', () => {
|
||||
|
||||
// First expand the providers
|
||||
const chevronButtons = screen.getAllByRole('button')
|
||||
const chevron = chevronButtons.find(button =>
|
||||
const chevron = chevronButtons.find((button) =>
|
||||
button.querySelector('svg.tabler-icon-chevron-right')
|
||||
)
|
||||
if (!chevron) throw new Error('Chevron button not found')
|
||||
await user.click(chevron)
|
||||
|
||||
// Then click on a provider
|
||||
const openaiProvider = screen.getByTestId('provider-avatar-openai').closest('div')
|
||||
const openaiProvider = screen
|
||||
.getByTestId('provider-avatar-openai')
|
||||
.closest('div')
|
||||
await user.click(openaiProvider!)
|
||||
|
||||
expect(mockNavigate).toHaveBeenCalledWith({
|
||||
@ -193,7 +179,9 @@ describe('SettingsMenu', () => {
|
||||
it('shows mobile menu toggle button', () => {
|
||||
render(<SettingsMenu />)
|
||||
|
||||
const menuToggle = screen.getByRole('button', { name: 'Toggle settings menu' })
|
||||
const menuToggle = screen.getByRole('button', {
|
||||
name: 'Toggle settings menu',
|
||||
})
|
||||
expect(menuToggle).toBeInTheDocument()
|
||||
})
|
||||
|
||||
@ -201,7 +189,9 @@ describe('SettingsMenu', () => {
|
||||
const user = userEvent.setup()
|
||||
render(<SettingsMenu />)
|
||||
|
||||
const menuToggle = screen.getByRole('button', { name: 'Toggle settings menu' })
|
||||
const menuToggle = screen.getByRole('button', {
|
||||
name: 'Toggle settings menu',
|
||||
})
|
||||
await user.click(menuToggle)
|
||||
|
||||
// Menu should now be visible
|
||||
@ -214,7 +204,9 @@ describe('SettingsMenu', () => {
|
||||
render(<SettingsMenu />)
|
||||
|
||||
// Open menu first
|
||||
const menuToggle = screen.getByRole('button', { name: 'Toggle settings menu' })
|
||||
const menuToggle = screen.getByRole('button', {
|
||||
name: 'Toggle settings menu',
|
||||
})
|
||||
await user.click(menuToggle)
|
||||
|
||||
// Then close it
|
||||
@ -239,7 +231,7 @@ describe('SettingsMenu', () => {
|
||||
|
||||
// First expand the providers submenu
|
||||
const chevronButtons = screen.getAllByRole('button')
|
||||
const chevron = chevronButtons.find(button =>
|
||||
const chevron = chevronButtons.find((button) =>
|
||||
button.querySelector('svg.tabler-icon-chevron-right')
|
||||
)
|
||||
if (chevron) await user.click(chevron)
|
||||
@ -273,12 +265,14 @@ describe('SettingsMenu', () => {
|
||||
|
||||
// Expand providers
|
||||
const chevronButtons = screen.getAllByRole('button')
|
||||
const chevron = chevronButtons.find(button =>
|
||||
const chevron = chevronButtons.find((button) =>
|
||||
button.querySelector('svg.tabler-icon-chevron-right')
|
||||
)
|
||||
if (chevron) await user.click(chevron)
|
||||
|
||||
expect(screen.getByTestId('provider-avatar-openai')).toBeInTheDocument()
|
||||
expect(screen.queryByTestId('provider-avatar-anthropic')).not.toBeInTheDocument()
|
||||
expect(
|
||||
screen.queryByTestId('provider-avatar-anthropic')
|
||||
).not.toBeInTheDocument()
|
||||
})
|
||||
})
|
||||
@ -40,7 +40,6 @@ describe('useGeneralSetting', () => {
|
||||
useGeneralSetting.setState({
|
||||
currentLanguage: 'en',
|
||||
spellCheckChatInput: true,
|
||||
experimentalFeatures: false,
|
||||
huggingfaceToken: undefined,
|
||||
})
|
||||
|
||||
@ -60,11 +59,9 @@ describe('useGeneralSetting', () => {
|
||||
|
||||
expect(result.current.currentLanguage).toBe('en')
|
||||
expect(result.current.spellCheckChatInput).toBe(true)
|
||||
expect(result.current.experimentalFeatures).toBe(false)
|
||||
expect(result.current.huggingfaceToken).toBeUndefined()
|
||||
expect(typeof result.current.setCurrentLanguage).toBe('function')
|
||||
expect(typeof result.current.setSpellCheckChatInput).toBe('function')
|
||||
expect(typeof result.current.setExperimentalFeatures).toBe('function')
|
||||
expect(typeof result.current.setHuggingfaceToken).toBe('function')
|
||||
})
|
||||
|
||||
@ -155,42 +152,6 @@ describe('useGeneralSetting', () => {
|
||||
})
|
||||
})
|
||||
|
||||
describe('setExperimentalFeatures', () => {
|
||||
it('should enable experimental features', () => {
|
||||
const { result } = renderHook(() => useGeneralSetting())
|
||||
|
||||
act(() => {
|
||||
result.current.setExperimentalFeatures(true)
|
||||
})
|
||||
|
||||
expect(result.current.experimentalFeatures).toBe(true)
|
||||
})
|
||||
|
||||
it('should disable experimental features', () => {
|
||||
const { result } = renderHook(() => useGeneralSetting())
|
||||
|
||||
act(() => {
|
||||
result.current.setExperimentalFeatures(false)
|
||||
})
|
||||
|
||||
expect(result.current.experimentalFeatures).toBe(false)
|
||||
})
|
||||
|
||||
it('should toggle experimental features multiple times', () => {
|
||||
const { result } = renderHook(() => useGeneralSetting())
|
||||
|
||||
act(() => {
|
||||
result.current.setExperimentalFeatures(true)
|
||||
})
|
||||
expect(result.current.experimentalFeatures).toBe(true)
|
||||
|
||||
act(() => {
|
||||
result.current.setExperimentalFeatures(false)
|
||||
})
|
||||
expect(result.current.experimentalFeatures).toBe(false)
|
||||
})
|
||||
})
|
||||
|
||||
describe('setHuggingfaceToken', () => {
|
||||
it('should set huggingface token', () => {
|
||||
const { result } = renderHook(() => useGeneralSetting())
|
||||
@ -254,7 +215,7 @@ describe('useGeneralSetting', () => {
|
||||
expect(mockGetByName).toHaveBeenCalledWith('@janhq/download-extension')
|
||||
|
||||
// Wait for async operations
|
||||
await new Promise(resolve => setTimeout(resolve, 0))
|
||||
await new Promise((resolve) => setTimeout(resolve, 0))
|
||||
|
||||
expect(mockGetSettings).toHaveBeenCalled()
|
||||
expect(mockUpdateSettings).toHaveBeenCalledWith([
|
||||
@ -272,13 +233,11 @@ describe('useGeneralSetting', () => {
|
||||
act(() => {
|
||||
result1.current.setCurrentLanguage('id')
|
||||
result1.current.setSpellCheckChatInput(false)
|
||||
result1.current.setExperimentalFeatures(true)
|
||||
result1.current.setHuggingfaceToken('shared-token')
|
||||
})
|
||||
|
||||
expect(result2.current.currentLanguage).toBe('id')
|
||||
expect(result2.current.spellCheckChatInput).toBe(false)
|
||||
expect(result2.current.experimentalFeatures).toBe(true)
|
||||
expect(result2.current.huggingfaceToken).toBe('shared-token')
|
||||
})
|
||||
})
|
||||
@ -290,13 +249,11 @@ describe('useGeneralSetting', () => {
|
||||
act(() => {
|
||||
result.current.setCurrentLanguage('vn')
|
||||
result.current.setSpellCheckChatInput(false)
|
||||
result.current.setExperimentalFeatures(true)
|
||||
result.current.setHuggingfaceToken('complex-token-123')
|
||||
})
|
||||
|
||||
expect(result.current.currentLanguage).toBe('vn')
|
||||
expect(result.current.spellCheckChatInput).toBe(false)
|
||||
expect(result.current.experimentalFeatures).toBe(true)
|
||||
expect(result.current.huggingfaceToken).toBe('complex-token-123')
|
||||
})
|
||||
|
||||
@ -314,11 +271,9 @@ describe('useGeneralSetting', () => {
|
||||
|
||||
// Second update
|
||||
act(() => {
|
||||
result.current.setExperimentalFeatures(true)
|
||||
result.current.setHuggingfaceToken('sequential-token')
|
||||
})
|
||||
|
||||
expect(result.current.experimentalFeatures).toBe(true)
|
||||
expect(result.current.huggingfaceToken).toBe('sequential-token')
|
||||
|
||||
// Third update
|
||||
|
||||
@ -29,7 +29,6 @@ import { OUT_OF_CONTEXT_SIZE } from '@/utils/error'
|
||||
import { updateSettings } from '@/services/providers'
|
||||
import { useContextSizeApproval } from './useModelContextApproval'
|
||||
import { useModelLoad } from './useModelLoad'
|
||||
import { useGeneralSetting } from './useGeneralSetting'
|
||||
import {
|
||||
ReasoningProcessor,
|
||||
extractReasoningFromMessage,
|
||||
@ -37,7 +36,6 @@ import {
|
||||
|
||||
export const useChat = () => {
|
||||
const { prompt, setPrompt } = usePrompt()
|
||||
const { experimentalFeatures } = useGeneralSetting()
|
||||
const {
|
||||
tools,
|
||||
updateTokenSpeed,
|
||||
@ -247,8 +245,7 @@ export const useChat = () => {
|
||||
let isCompleted = false
|
||||
|
||||
// Filter tools based on model capabilities and available tools for this thread
|
||||
let availableTools =
|
||||
experimentalFeatures && selectedModel?.capabilities?.includes('tools')
|
||||
let availableTools = selectedModel?.capabilities?.includes('tools')
|
||||
? tools.filter((tool) => {
|
||||
const disabledTools = getDisabledToolsForThread(activeThread.id)
|
||||
return !disabledTools.includes(tool.name)
|
||||
@ -543,7 +540,6 @@ export const useChat = () => {
|
||||
setPrompt,
|
||||
selectedModel,
|
||||
currentAssistant,
|
||||
experimentalFeatures,
|
||||
tools,
|
||||
updateLoadingModel,
|
||||
getDisabledToolsForThread,
|
||||
|
||||
@ -6,10 +6,8 @@ import { ExtensionManager } from '@/lib/extension'
|
||||
type LeftPanelStoreState = {
|
||||
currentLanguage: Language
|
||||
spellCheckChatInput: boolean
|
||||
experimentalFeatures: boolean
|
||||
huggingfaceToken?: string
|
||||
setHuggingfaceToken: (token: string) => void
|
||||
setExperimentalFeatures: (value: boolean) => void
|
||||
setSpellCheckChatInput: (value: boolean) => void
|
||||
setCurrentLanguage: (value: Language) => void
|
||||
}
|
||||
@ -19,9 +17,7 @@ export const useGeneralSetting = create<LeftPanelStoreState>()(
|
||||
(set) => ({
|
||||
currentLanguage: 'en',
|
||||
spellCheckChatInput: true,
|
||||
experimentalFeatures: false,
|
||||
huggingfaceToken: undefined,
|
||||
setExperimentalFeatures: (value) => set({ experimentalFeatures: value }),
|
||||
setSpellCheckChatInput: (value) => set({ spellCheckChatInput: value }),
|
||||
setCurrentLanguage: (value) => set({ currentLanguage: value }),
|
||||
setHuggingfaceToken: (token) => {
|
||||
|
||||
@ -61,8 +61,6 @@ vi.mock('@/hooks/useGeneralSetting', () => ({
|
||||
useGeneralSetting: () => ({
|
||||
spellCheckChatInput: true,
|
||||
setSpellCheckChatInput: vi.fn(),
|
||||
experimentalFeatures: false,
|
||||
setExperimentalFeatures: vi.fn(),
|
||||
huggingfaceToken: 'test-token',
|
||||
setHuggingfaceToken: vi.fn(),
|
||||
}),
|
||||
@ -188,7 +186,9 @@ vi.mock('@tauri-apps/plugin-opener', () => ({
|
||||
}))
|
||||
|
||||
vi.mock('@tauri-apps/api/webviewWindow', () => {
|
||||
const MockWebviewWindow = vi.fn().mockImplementation((label: string, options: any) => ({
|
||||
const MockWebviewWindow = vi
|
||||
.fn()
|
||||
.mockImplementation((label: string, options: any) => ({
|
||||
once: vi.fn(),
|
||||
setFocus: vi.fn(),
|
||||
}))
|
||||
@ -299,16 +299,6 @@ describe('General Settings Route', () => {
|
||||
// expect(screen.getByTestId('language-switcher')).toBeInTheDocument()
|
||||
// })
|
||||
|
||||
it('should render switches for experimental features and spell check', async () => {
|
||||
const Component = GeneralRoute.component as React.ComponentType
|
||||
await act(async () => {
|
||||
render(<Component />)
|
||||
})
|
||||
|
||||
const switches = screen.getAllByTestId('switch')
|
||||
expect(switches.length).toBeGreaterThanOrEqual(2)
|
||||
})
|
||||
|
||||
it('should render huggingface token input', async () => {
|
||||
const Component = GeneralRoute.component as React.ComponentType
|
||||
await act(async () => {
|
||||
@ -336,24 +326,6 @@ describe('General Settings Route', () => {
|
||||
expect(switches[0]).toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('should handle experimental features toggle', async () => {
|
||||
const Component = GeneralRoute.component as React.ComponentType
|
||||
await act(async () => {
|
||||
render(<Component />)
|
||||
})
|
||||
|
||||
const switches = screen.getAllByTestId('switch')
|
||||
expect(switches.length).toBeGreaterThan(0)
|
||||
|
||||
// Test that switches are interactive
|
||||
if (switches.length > 1) {
|
||||
await act(async () => {
|
||||
fireEvent.click(switches[1])
|
||||
})
|
||||
expect(switches[1]).toBeInTheDocument()
|
||||
}
|
||||
})
|
||||
|
||||
it('should handle huggingface token change', async () => {
|
||||
const Component = GeneralRoute.component as React.ComponentType
|
||||
await act(async () => {
|
||||
|
||||
@ -46,9 +46,6 @@ import { stopAllModels } from '@/services/models'
|
||||
import { SystemEvent } from '@/types/events'
|
||||
import { Input } from '@/components/ui/input'
|
||||
import { useHardware } from '@/hooks/useHardware'
|
||||
import { getConnectedServers } from '@/services/mcp'
|
||||
import { invoke } from '@tauri-apps/api/core'
|
||||
import { useMCPServers } from '@/hooks/useMCPServers'
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
export const Route = createFileRoute(route.settings.general as any)({
|
||||
@ -60,8 +57,6 @@ function General() {
|
||||
const {
|
||||
spellCheckChatInput,
|
||||
setSpellCheckChatInput,
|
||||
experimentalFeatures,
|
||||
setExperimentalFeatures,
|
||||
huggingfaceToken,
|
||||
setHuggingfaceToken,
|
||||
} = useGeneralSetting()
|
||||
@ -209,38 +204,6 @@ function General() {
|
||||
}
|
||||
}, [t, checkForUpdate])
|
||||
|
||||
const handleStopAllMCPServers = async () => {
|
||||
try {
|
||||
const connectedServers = await getConnectedServers()
|
||||
|
||||
// Stop each connected server
|
||||
const stopPromises = connectedServers.map((serverName) =>
|
||||
invoke('deactivate_mcp_server', { name: serverName }).catch((error) => {
|
||||
console.error(`Error stopping MCP server ${serverName}:`, error)
|
||||
return Promise.resolve() // Continue with other servers even if one fails
|
||||
})
|
||||
)
|
||||
|
||||
await Promise.all(stopPromises)
|
||||
|
||||
// Update server configs to set active: false for stopped servers
|
||||
const { mcpServers, editServer } = useMCPServers.getState()
|
||||
connectedServers.forEach((serverName) => {
|
||||
const serverConfig = mcpServers[serverName]
|
||||
if (serverConfig) {
|
||||
editServer(serverName, { ...serverConfig, active: false })
|
||||
}
|
||||
})
|
||||
|
||||
if (connectedServers.length > 0) {
|
||||
toast.success(`Stopped ${connectedServers.length} MCP server(s)`)
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error stopping MCP servers:', error)
|
||||
toast.error('Failed to stop MCP servers')
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="flex flex-col h-full">
|
||||
<HeaderPage>
|
||||
@ -430,19 +393,6 @@ function General() {
|
||||
</Card>
|
||||
{/* Advanced */}
|
||||
<Card title="Advanced">
|
||||
<CardItem
|
||||
title="Experimental Features"
|
||||
description="Enable experimental features. They may be unstable or change at any time."
|
||||
actions={
|
||||
<Switch
|
||||
checked={experimentalFeatures}
|
||||
onCheckedChange={async (e) => {
|
||||
await handleStopAllMCPServers()
|
||||
setExperimentalFeatures(e)
|
||||
}}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
<CardItem
|
||||
title={t('settings:others.resetFactory', {
|
||||
ns: 'settings',
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user