diff --git a/packages/vsce/e2e/demo/model-dialog.demo.ts b/packages/vsce/e2e/demo/model-dialog.demo.ts index 35e820b6..17ad855a 100644 --- a/packages/vsce/e2e/demo/model-dialog.demo.ts +++ b/packages/vsce/e2e/demo/model-dialog.demo.ts @@ -34,14 +34,13 @@ test.describe('Model Dialog Demo', () => { // Verify model select is pre-selected await expect(webviewPage.locator('#model-select')).toHaveValue('claude-sonnet-4-20250514'); - await expect(webviewPage.locator('#fast-model-select')).toHaveValue('claude-haiku-4-20250514'); // Take screenshot const dialog = webviewPage.locator('.configuration-dialog'); await dialog.screenshot({ path: '../../docs/public/screenshots/spec-model-dialog.png' }); }); - test('should show model dialog with empty values and env placeholders', async ({ webviewPage }) => { + test('should show model dialog with empty values', async ({ webviewPage }) => { await webviewPage.evaluate(() => { window.simulateExtensionMessage({ command: 'showDialog', @@ -54,14 +53,12 @@ test.describe('Model Dialog Demo', () => { command: 'configurationResponse', configurationData: { model: '', - fastModel: '', - envModel: 'WAVE_MODEL', - envFastModel: 'WAVE_FAST_MODEL' + fastModel: '' } }); }); - // Simulate configured models (even with env vars, models list comes from SDK) + // Simulate configured models await webviewPage.evaluate(() => { window.simulateExtensionMessage({ command: 'configuredModelsResponse', diff --git a/packages/vsce/src/chatProvider.ts b/packages/vsce/src/chatProvider.ts index c9a2169e..05027e64 100644 --- a/packages/vsce/src/chatProvider.ts +++ b/packages/vsce/src/chatProvider.ts @@ -91,6 +91,11 @@ export class ChatProvider implements vscode.WebviewViewProvider { this.sidebarSession.updateConfig(cfg, this.context.extensionMode); this.tabSessions.forEach(session => session.updateConfig(cfg, this.context.extensionMode)); this.windowSessions.forEach(session => session.updateConfig(cfg, this.context.extensionMode)); + }, + updateAllSessionsModel: (model: string) => { + this.sidebarSession.setModel(model); + this.tabSessions.forEach(session => session.setModel(model)); + this.windowSessions.forEach(session => session.setModel(model)); } } ); diff --git a/packages/vsce/src/services/configurationService.ts b/packages/vsce/src/services/configurationService.ts index 47a94de3..c81536e6 100644 --- a/packages/vsce/src/services/configurationService.ts +++ b/packages/vsce/src/services/configurationService.ts @@ -8,19 +8,6 @@ export interface ConfigurationData { model?: string; fastModel?: string; language?: string; - /** Environment variable values (read-only, for placeholder display) */ - envServerUrl?: string; - envApiKey?: string; - envHeaders?: string; - envBaseUrl?: string; - envModel?: string; - envFastModel?: string; -} - -/** Mask a secret value, keeping first 4 and last 4 characters visible */ -function maskSecret(value: string): string { - if (value.length <= 10) return '****'; - return value.slice(0, 4) + '****' + value.slice(-4); } export class ConfigurationService { @@ -34,13 +21,7 @@ export class ConfigurationService { baseURL: this.context.globalState.get('baseURL') || '', model: this.context.globalState.get('model') || '', fastModel: this.context.globalState.get('fastModel') || '', - language: this.context.globalState.get('language') || 'Chinese', - envServerUrl: process.env.WAVE_SERVER_URL || undefined, - envApiKey: process.env.WAVE_API_KEY ? maskSecret(process.env.WAVE_API_KEY) : undefined, - envHeaders: process.env.WAVE_CUSTOM_HEADERS || undefined, - envBaseUrl: process.env.WAVE_BASE_URL || undefined, - envModel: process.env.WAVE_MODEL || undefined, - envFastModel: process.env.WAVE_FAST_MODEL || undefined + language: this.context.globalState.get('language') || 'Chinese' }; } diff --git a/packages/vsce/src/session/chatSession.ts b/packages/vsce/src/session/chatSession.ts index 4414a596..eabfecd7 100644 --- a/packages/vsce/src/session/chatSession.ts +++ b/packages/vsce/src/session/chatSession.ts @@ -274,6 +274,10 @@ export class ChatSession { this.clearQueue(); } + public setModel(model: string): void { + this.agent?.setModel(model); + } + public async updateConfig(config: ConfigurationData, extensionMode: vscode.ExtensionMode) { if (this.agent) { const currentSessionId = this.sessionId; diff --git a/packages/vsce/src/session/messageHandler.ts b/packages/vsce/src/session/messageHandler.ts index 73f4f212..6897db7c 100644 --- a/packages/vsce/src/session/messageHandler.ts +++ b/packages/vsce/src/session/messageHandler.ts @@ -15,6 +15,7 @@ export interface MessageHandlerContext { initializeAgent: (viewType: 'sidebar' | 'tab' | 'window', windowId?: string, restoreSessionId?: string) => Promise; listSessions: (viewType?: 'sidebar' | 'tab' | 'window', windowId?: string) => Promise; updateAllSessionsConfig: (config: unknown) => void; + updateAllSessionsModel: (model: string) => void; } export class MessageHandler { @@ -738,12 +739,8 @@ export class MessageHandler { private async handleSetModel(configData: unknown, viewType?: 'sidebar' | 'tab' | 'window', windowId?: string) { try { - const currentConfig = await this.configService.loadConfiguration(); - const mergedConfig = { ...currentConfig, ...(configData as Record) }; - await this.configService.saveConfiguration(mergedConfig); - const config = await this.configService.loadConfiguration(); - - this.context.updateAllSessionsConfig(config); + const { model } = configData as { model: string }; + this.context.updateAllSessionsModel(model); this.context.postMessage({ command: 'configurationUpdated' }, viewType, windowId); this.context.postMessage({ command: 'focusInput' }, viewType, windowId); @@ -759,24 +756,19 @@ export class MessageHandler { private async handleGetConfiguredModels(viewType?: 'sidebar' | 'tab' | 'window', windowId?: string) { try { const session = this.context.getChatSession(viewType || 'tab', windowId); - // Get models from the agent instance (like wave-agent does) - // The agent's getConfiguredModels() reads from SDK's ConfigurationService which has remote models const models = session.agent?.getConfiguredModels() || []; - // Also get the current model values from the agent (these include remote config) - const modelConfig = session.agent?.getModelConfig() || { model: '', fastModel: '' }; + const modelConfig = session.agent?.getModelConfig() || { model: '' }; this.context.postMessage({ command: 'configuredModelsResponse', models, - currentModel: modelConfig.model || '', - currentFastModel: modelConfig.fastModel || '' + currentModel: modelConfig.model || '' }, viewType, windowId); } catch (error) { console.error(`Failed to get configured models for ${viewType}:`, error); this.context.postMessage({ command: 'configuredModelsResponse', models: [], - currentModel: '', - currentFastModel: '' + currentModel: '' }, viewType, windowId); } } diff --git a/packages/vsce/tests/webview/modelDropdown.test.tsx b/packages/vsce/tests/webview/modelDropdown.test.tsx index 8e50f3eb..8001bb95 100644 --- a/packages/vsce/tests/webview/modelDropdown.test.tsx +++ b/packages/vsce/tests/webview/modelDropdown.test.tsx @@ -19,12 +19,11 @@ describe('Model Dialog', () => { await act(async () => { sendCommand('configurationResponse', { configurationData: { - model: 'gpt-4', - fastModel: 'gpt-4-mini' + model: 'gpt-4' } }); sendCommand('configuredModelsResponse', { - models: ['gpt-4', 'gpt-3.5-turbo', 'claude-3-opus', 'gpt-4-mini'], + models: ['gpt-4', 'gpt-3.5-turbo', 'claude-3-opus'], }); }); @@ -43,12 +42,8 @@ describe('Model Dialog', () => { expect(modelSelect.value).toBe('gpt-4'); }); - // Verify fast model select exists and has the current value - const fastModelSelect = document.querySelector('#fast-model-select') as HTMLSelectElement; - expect(fastModelSelect).toBeInTheDocument(); - await waitFor(() => { - expect(fastModelSelect.value).toBe('gpt-4-mini'); - }); + // Fast model select should not exist + expect(document.querySelector('#fast-model-select')).not.toBeInTheDocument(); }); it('should populate select options from configured models response', async () => { @@ -57,8 +52,7 @@ describe('Model Dialog', () => { await act(async () => { sendCommand('configurationResponse', { configurationData: { - model: 'gpt-4', - fastModel: 'gpt-4-mini' + model: 'gpt-4' } }); sendCommand('configuredModelsResponse', { @@ -78,13 +72,6 @@ describe('Model Dialog', () => { expect(modelValues).toContain('gpt-4'); expect(modelValues).toContain('gpt-3.5-turbo'); expect(modelValues).toContain('claude-3-opus'); - - const fastModelSelect = document.querySelector('#fast-model-select') as HTMLSelectElement; - const fastOptions = Array.from(fastModelSelect.querySelectorAll('option')); - const fastValues = fastOptions.map(o => o.value); - expect(fastValues).toContain('gpt-4'); - expect(fastValues).toContain('gpt-3.5-turbo'); - expect(fastValues).toContain('claude-3-opus'); }); it('should send setModel message when save is clicked', async () => { @@ -93,13 +80,11 @@ describe('Model Dialog', () => { await act(async () => { sendCommand('configurationResponse', { configurationData: { - model: 'gpt-4', - fastModel: 'gpt-4-mini' + model: 'gpt-4' } }); sendCommand('configuredModelsResponse', { - models: ['gpt-4', 'gpt-3.5-turbo'], - fastModels: ['gpt-4-mini', 'gpt-3.5-turbo'] + models: ['gpt-4', 'gpt-3.5-turbo'] }); }); @@ -138,13 +123,11 @@ describe('Model Dialog', () => { await act(async () => { sendCommand('configurationResponse', { configurationData: { - model: 'gpt-4', - fastModel: 'gpt-4-mini' + model: 'gpt-4' } }); sendCommand('configuredModelsResponse', { - models: ['gpt-4'], - fastModels: ['gpt-4-mini'] + models: ['gpt-4'] }); }); diff --git a/packages/vsce/webview/src/components/ChatApp.tsx b/packages/vsce/webview/src/components/ChatApp.tsx index 319d5bad..f398b214 100644 --- a/packages/vsce/webview/src/components/ChatApp.tsx +++ b/packages/vsce/webview/src/components/ChatApp.tsx @@ -105,14 +105,10 @@ export const ChatApp: React.FC = ({ vscode }) => { type: 'SET_CONFIGURED_MODELS', payload: message.models || [] }); - // Also update current model values from agent - if (message.currentModel !== undefined || message.currentFastModel !== undefined) { + if (message.currentModel !== undefined) { dispatch({ - type: 'SET_CURRENT_MODELS', - payload: { - model: message.currentModel || '', - fastModel: message.currentFastModel || '' - } + type: 'SET_CURRENT_MODEL', + payload: message.currentModel || '' }); } break; diff --git a/packages/vsce/webview/src/components/ConfigDialog.tsx b/packages/vsce/webview/src/components/ConfigDialog.tsx index 7ba152de..c742c7dc 100644 --- a/packages/vsce/webview/src/components/ConfigDialog.tsx +++ b/packages/vsce/webview/src/components/ConfigDialog.tsx @@ -89,7 +89,7 @@ const ConfigDialog: React.FC = ({ type="url" value={formData.baseURL || ''} onChange={(e) => handleInputChange('baseURL', e.target.value)} - placeholder={configurationData?.envBaseUrl || 'https://api.example.com/v1 (或设置 WAVE_BASE_URL)'} + placeholder="https://api.example.com/v1" disabled={isLoading} /> @@ -101,7 +101,7 @@ const ConfigDialog: React.FC = ({ type="password" value={formData.apiKey || ''} onChange={(e) => handleInputChange('apiKey', e.target.value)} - placeholder={configurationData?.envApiKey || '输入 API Key (或设置 WAVE_API_KEY 环境变量)'} + placeholder="输入 API Key" disabled={isLoading} /> @@ -112,7 +112,7 @@ const ConfigDialog: React.FC = ({ id="headers" value={formData.headers || ''} onChange={(e) => handleInputChange('headers', e.target.value)} - placeholder={configurationData?.envHeaders || `Authorization: Bearer ...\n(或设置 WAVE_CUSTOM_HEADERS)`} + placeholder="Authorization: Bearer ..." disabled={isLoading} className="configuration-textarea" rows={3} @@ -126,7 +126,7 @@ const ConfigDialog: React.FC = ({ type="text" value={formData.model || ''} onChange={(e) => handleInputChange('model', e.target.value)} - placeholder={configurationData?.envModel || '请输入模型名称 (或设置 WAVE_MODEL)'} + placeholder="请输入模型名称" disabled={isLoading} /> @@ -138,7 +138,7 @@ const ConfigDialog: React.FC = ({ type="text" value={formData.fastModel || ''} onChange={(e) => handleInputChange('fastModel', e.target.value)} - placeholder={configurationData?.envFastModel || '请输入快速模型名称 (或设置 WAVE_FAST_MODEL)'} + placeholder="请输入快速模型名称" disabled={isLoading} /> diff --git a/packages/vsce/webview/src/components/LoginDialog.tsx b/packages/vsce/webview/src/components/LoginDialog.tsx index e5826eb1..9a0d59cd 100644 --- a/packages/vsce/webview/src/components/LoginDialog.tsx +++ b/packages/vsce/webview/src/components/LoginDialog.tsx @@ -105,7 +105,7 @@ const LoginDialog: React.FC = ({ }; }, [onClose]); - const serverUrl = serverUrlSaved ? serverUrlInput : (configurationData?.serverUrl || configurationData?.envServerUrl || ''); + const serverUrl = serverUrlSaved ? serverUrlInput : (configurationData?.serverUrl || ''); return (
@@ -124,7 +124,7 @@ const LoginDialog: React.FC = ({ type="url" value={serverUrlInput} onChange={(e) => { setServerUrlInput(e.target.value); setServerUrlSaved(false); }} - placeholder={configurationData?.envServerUrl || 'WAVE_SERVER_URL'} + placeholder="https://wave.example.com" />