feat: auto-enable local providers when configured via environment variables (#1881) (#2002)

* fix: update Docker workflow to use correct target stage name

- Change target from bolt-ai-production to runtime
- Matches the actual stage name in the new Dockerfile structure
- Fixes CI failure: target stage 'bolt-ai-production' could not be found

* feat: auto-enable local providers when configured via environment variables (#1881)

- Add server-side API endpoint `/api/configured-providers` to detect environment-configured providers
- Auto-enable Ollama, LMStudio, and OpenAILike providers when their environment variables are set
- Filter out placeholder values (like "your_*_here") to only detect real configuration
- Preserve user preferences: auto-enabling only applies on first load or previously auto-enabled providers
- Track auto-enabled vs manually-enabled providers in localStorage for proper user choice handling
- Solve issue where Ollama appears configured server-side but disabled in UI

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>

---------

Co-authored-by: Claude <noreply@anthropic.com>
This commit is contained in:
Stijnus
2025-09-16 16:10:12 +02:00
committed by GitHub
parent 437d110e37
commit cb3c536c5d
2 changed files with 244 additions and 1 deletions

View File

@@ -52,11 +52,37 @@ export const shortcutsStore = map<Shortcuts>({
// Create a single key for provider settings
const PROVIDER_SETTINGS_KEY = 'provider_settings';
const AUTO_ENABLED_KEY = 'auto_enabled_providers';
// Add this helper function at the top of the file
const isBrowser = typeof window !== 'undefined';
// Initialize provider settings from both localStorage and defaults
// Interface for configured provider info from server
interface ConfiguredProvider {
name: string;
isConfigured: boolean;
configMethod: 'environment' | 'none';
}
// Fetch configured providers from server
const fetchConfiguredProviders = async (): Promise<ConfiguredProvider[]> => {
try {
const response = await fetch('/api/configured-providers');
if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
}
const data = (await response.json()) as { providers?: ConfiguredProvider[] };
return data.providers || [];
} catch (error) {
console.error('Error fetching configured providers:', error);
return [];
}
};
// Initialize provider settings from both localStorage and server-detected configuration
const getInitialProviderSettings = (): ProviderSetting => {
const initialSettings: ProviderSetting = {};
@@ -92,8 +118,84 @@ const getInitialProviderSettings = (): ProviderSetting => {
return initialSettings;
};
// Auto-enable providers that are configured on the server
const autoEnableConfiguredProviders = async () => {
if (!isBrowser) {
return;
}
try {
const configuredProviders = await fetchConfiguredProviders();
const currentSettings = providersStore.get();
const savedSettings = localStorage.getItem(PROVIDER_SETTINGS_KEY);
const autoEnabledProviders = localStorage.getItem(AUTO_ENABLED_KEY);
// Track which providers were auto-enabled to avoid overriding user preferences
const previouslyAutoEnabled = autoEnabledProviders ? JSON.parse(autoEnabledProviders) : [];
const newlyAutoEnabled: string[] = [];
let hasChanges = false;
configuredProviders.forEach(({ name, isConfigured, configMethod }) => {
if (isConfigured && configMethod === 'environment' && LOCAL_PROVIDERS.includes(name)) {
const currentProvider = currentSettings[name];
if (currentProvider) {
/*
* Only auto-enable if:
* 1. Provider is not already enabled, AND
* 2. Either we haven't saved settings yet (first time) OR provider was previously auto-enabled
*/
const hasUserSettings = savedSettings !== null;
const wasAutoEnabled = previouslyAutoEnabled.includes(name);
const shouldAutoEnable = !currentProvider.settings.enabled && (!hasUserSettings || wasAutoEnabled);
if (shouldAutoEnable) {
currentSettings[name] = {
...currentProvider,
settings: {
...currentProvider.settings,
enabled: true,
},
};
newlyAutoEnabled.push(name);
hasChanges = true;
}
}
}
});
if (hasChanges) {
// Update the store
providersStore.set(currentSettings);
// Save to localStorage
localStorage.setItem(PROVIDER_SETTINGS_KEY, JSON.stringify(currentSettings));
// Update the auto-enabled providers list
const allAutoEnabled = [...new Set([...previouslyAutoEnabled, ...newlyAutoEnabled])];
localStorage.setItem(AUTO_ENABLED_KEY, JSON.stringify(allAutoEnabled));
console.log(`Auto-enabled providers: ${newlyAutoEnabled.join(', ')}`);
}
} catch (error) {
console.error('Error auto-enabling configured providers:', error);
}
};
export const providersStore = map<ProviderSetting>(getInitialProviderSettings());
// Export the auto-enable function for use in components
export const initializeProviders = autoEnableConfiguredProviders;
// Initialize providers when the module loads (in browser only)
if (isBrowser) {
// Use a small delay to ensure DOM and other resources are ready
setTimeout(() => {
autoEnableConfiguredProviders();
}, 100);
}
// Create a function to update provider settings that handles both store and persistence
export const updateProviderSettings = (provider: string, settings: ProviderSetting) => {
const currentSettings = providersStore.get();
@@ -113,6 +215,37 @@ export const updateProviderSettings = (provider: string, settings: ProviderSetti
// Save to localStorage
const allSettings = providersStore.get();
localStorage.setItem(PROVIDER_SETTINGS_KEY, JSON.stringify(allSettings));
// If this is a local provider, update the auto-enabled tracking
if (LOCAL_PROVIDERS.includes(provider) && updatedProvider.settings.enabled !== undefined) {
updateAutoEnabledTracking(provider, updatedProvider.settings.enabled);
}
};
// Update auto-enabled tracking when user manually changes provider settings
const updateAutoEnabledTracking = (providerName: string, isEnabled: boolean) => {
if (!isBrowser) {
return;
}
try {
const autoEnabledProviders = localStorage.getItem(AUTO_ENABLED_KEY);
const currentAutoEnabled = autoEnabledProviders ? JSON.parse(autoEnabledProviders) : [];
if (isEnabled) {
// If user enables provider, add to auto-enabled list (for future detection)
if (!currentAutoEnabled.includes(providerName)) {
currentAutoEnabled.push(providerName);
localStorage.setItem(AUTO_ENABLED_KEY, JSON.stringify(currentAutoEnabled));
}
} else {
// If user disables provider, remove from auto-enabled list (respect user choice)
const updatedAutoEnabled = currentAutoEnabled.filter((name: string) => name !== providerName);
localStorage.setItem(AUTO_ENABLED_KEY, JSON.stringify(updatedAutoEnabled));
}
} catch (error) {
console.error('Error updating auto-enabled tracking:', error);
}
};
export const isDebugMode = atom(false);