feat: separated efficient agent mode (lite mode) for small language models

This commit is contained in:
Xiaohan-Tian
2026-06-04 16:45:06 -07:00
parent a3a0f2f4ad
commit 5c5a07b839
18 changed files with 411 additions and 20 deletions
@@ -14,6 +14,7 @@ const { localSeparatorModelCacheMock } = vi.hoisted(() => ({
const configState = new Map<string, unknown>([
['general.language', 'auto'],
['general.agent_mode', 'regular'],
['general.llm_provider', 'local_browser'],
['general.persist_api_keys_non_localhost', false],
['general.auto_compact_threshold_percent', 90],
@@ -107,6 +108,8 @@ describe('GeneralSettings', () => {
beforeEach(() => {
configState.set('general.language', 'auto');
configState.set('general.agent_mode', 'regular');
configState.set('general.llm_provider', 'local_browser');
configState.set('general.local_browser.context_length', 65536);
configState.set('general.auto_compact_threshold_percent', 90);
configManagerMock.get.mockClear();
@@ -196,6 +199,41 @@ describe('GeneralSettings', () => {
).toBeTruthy();
});
it('renders the music assistant section and initializes the agent mode selector', async () => {
configState.set('general.llm_provider', 'openai');
configState.set('general.agent_mode', 'efficient');
renderSettings();
expect(await screen.findByText('K.G.Studio Music Assistant')).toBeTruthy();
const select = screen.getByLabelText('Agent Mode');
expect((select as HTMLSelectElement).value).toBe('efficient');
});
it('persists agent mode changes for non-local providers', async () => {
configState.set('general.llm_provider', 'openai');
renderSettings();
const select = await screen.findByLabelText('Agent Mode');
fireEvent.change(select, { target: { value: 'efficient' } });
await waitFor(() => {
expect(configManagerMock.set).toHaveBeenCalledWith('general.agent_mode', 'efficient');
});
});
it('disables the agent mode selector for the local browser provider and shows override help', async () => {
configState.set('general.llm_provider', 'local_browser');
configState.set('general.agent_mode', 'regular');
renderSettings();
const select = await screen.findByLabelText('Agent Mode');
expect(select).toBeDisabled();
expect(screen.getByText('Local LLM (Browser) always runs the assistant in Efficient Mode.')).toBeTruthy();
});
it('renders and persists local runtime download URLs', async () => {
renderSettings();
@@ -18,6 +18,13 @@ import {
LOCAL_SEPARATOR_MODEL_CONFIGS,
LOCAL_SEPARATOR_MODEL_IDS,
} from '../../../util/local-separator/config';
import {
DEFAULT_AGENT_MODE,
getEffectiveAgentMode,
isAgentModeForcedByProvider,
normalizeAgentMode,
type AgentMode,
} from '../../../util/agentMode';
const LANGUAGE_OPTION_LABELS: Record<Exclude<LanguageSetting, 'auto'>, string> = {
en_us: 'English',
@@ -29,6 +36,7 @@ const LANGUAGE_OPTION_LABELS: Record<Exclude<LanguageSetting, 'auto'>, string> =
const GeneralSettings: React.FC = () => {
const { t, setLanguageSetting } = useI18n();
const [language, setLanguage] = useState<LanguageSetting>('auto');
const [agentMode, setAgentMode] = useState<AgentMode>(DEFAULT_AGENT_MODE);
const [llmProvider, setLlmProvider] = useState<string>(LOCAL_LLM_PROVIDER_KEY);
const [openaiKey, setOpenaiKey] = useState<string>('');
const [openaiModel, setOpenaiModel] = useState<string>('');
@@ -103,6 +111,7 @@ const GeneralSettings: React.FC = () => {
}
setLanguage(((configManager.get('general.language') as LanguageSetting | undefined) ?? 'auto'));
setAgentMode(normalizeAgentMode(configManager.get('general.agent_mode')));
setLlmProvider((configManager.get('general.llm_provider') as string) || LOCAL_LLM_PROVIDER_KEY);
setOpenaiKey((configManager.get('general.openai.api_key') as string) || '');
setOpenaiModel((configManager.get('general.openai.model') as string) || '');
@@ -169,6 +178,16 @@ const GeneralSettings: React.FC = () => {
}
};
const handleAgentModeChange = async (value: AgentMode) => {
setAgentMode(value);
try {
await configManager.set('general.agent_mode', value);
console.log('Agent mode changed to:', value);
} catch (error) {
console.error('Failed to save agent mode:', error);
}
};
const handleLanguageChange = async (value: LanguageSetting) => {
setLanguage(value);
try {
@@ -354,6 +373,8 @@ const GeneralSettings: React.FC = () => {
const localRuntimeMessage = localModelState.runtimeSupport.reason;
const hasLocalRuntimeHardFailure = !localModelState.runtimeSupport.supported;
const isAgentModeOverriddenByLocalProvider = isAgentModeForcedByProvider(llmProvider);
const effectiveAgentMode = getEffectiveAgentMode(configManager);
// NOTE: Gemini and Claude are not supported yet due to CORS issues.
return (
@@ -444,6 +465,38 @@ const GeneralSettings: React.FC = () => {
</div>
</div>
<div className="settings-group">
<h4>{t('settings.general.musicAssistant.section')}</h4>
<div className="settings-item">
<label className="settings-label" htmlFor="general-agent-mode-select">
{t('settings.general.musicAssistant.agentMode.label')}
</label>
<select
id="general-agent-mode-select"
className="settings-select"
value={agentMode}
onChange={(e) => void handleAgentModeChange(e.target.value as AgentMode)}
disabled={isAgentModeOverriddenByLocalProvider}
>
<option value="regular">{t('settings.general.musicAssistant.agentMode.regular')}</option>
<option value="efficient">{t('settings.general.musicAssistant.agentMode.efficient')}</option>
</select>
<div className="settings-help" style={{ fontSize: '12px', color: '#888', marginTop: '4px' }}>
{isAgentModeOverriddenByLocalProvider
? t('settings.general.musicAssistant.agentMode.localOverride')
: t('settings.general.musicAssistant.agentMode.help')}
</div>
{effectiveAgentMode !== agentMode && (
<div className="settings-help" style={{ fontSize: '12px', color: '#888', marginTop: '4px' }}>
{t('settings.general.musicAssistant.agentMode.effectiveValue', {
mode: t('settings.general.musicAssistant.agentMode.efficient'),
})}
</div>
)}
</div>
</div>
<div className="settings-group">
<h4>{LOCAL_LLM_DISPLAY_NAME} Local Runtime</h4>