* 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>
111 lines
3.8 KiB
TypeScript
111 lines
3.8 KiB
TypeScript
import type { LoaderFunction } from '@remix-run/cloudflare';
|
|
import { json } from '@remix-run/cloudflare';
|
|
import { LLMManager } from '~/lib/modules/llm/manager';
|
|
import { LOCAL_PROVIDERS } from '~/lib/stores/settings';
|
|
|
|
interface ConfiguredProvider {
|
|
name: string;
|
|
isConfigured: boolean;
|
|
configMethod: 'environment' | 'none';
|
|
}
|
|
|
|
interface ConfiguredProvidersResponse {
|
|
providers: ConfiguredProvider[];
|
|
}
|
|
|
|
/**
|
|
* API endpoint that detects which providers are configured via environment variables
|
|
* This helps auto-enable providers that have been set up by the user
|
|
*/
|
|
export const loader: LoaderFunction = async ({ context }) => {
|
|
try {
|
|
const llmManager = LLMManager.getInstance(context?.cloudflare?.env as any);
|
|
const configuredProviders: ConfiguredProvider[] = [];
|
|
|
|
// Check each local provider for environment configuration
|
|
for (const providerName of LOCAL_PROVIDERS) {
|
|
const providerInstance = llmManager.getProvider(providerName);
|
|
let isConfigured = false;
|
|
let configMethod: 'environment' | 'none' = 'none';
|
|
|
|
if (providerInstance) {
|
|
const config = providerInstance.config;
|
|
|
|
/*
|
|
* Check if required environment variables are set
|
|
* For providers with baseUrlKey (Ollama, LMStudio, OpenAILike)
|
|
*/
|
|
if (config.baseUrlKey) {
|
|
const baseUrlEnvVar = config.baseUrlKey;
|
|
const cloudflareEnv = (context?.cloudflare?.env as Record<string, any>)?.[baseUrlEnvVar];
|
|
const processEnv = process.env[baseUrlEnvVar];
|
|
const managerEnv = llmManager.env[baseUrlEnvVar];
|
|
|
|
const envBaseUrl = cloudflareEnv || processEnv || managerEnv;
|
|
|
|
/*
|
|
* Only consider configured if environment variable is explicitly set
|
|
* Don't count default config.baseUrl values or placeholder values
|
|
*/
|
|
const isValidEnvValue =
|
|
envBaseUrl &&
|
|
typeof envBaseUrl === 'string' &&
|
|
envBaseUrl.trim().length > 0 &&
|
|
!envBaseUrl.includes('your_') && // Filter out placeholder values like "your_openai_like_base_url_here"
|
|
!envBaseUrl.includes('_here') &&
|
|
envBaseUrl.startsWith('http'); // Must be a valid URL
|
|
|
|
if (isValidEnvValue) {
|
|
isConfigured = true;
|
|
configMethod = 'environment';
|
|
}
|
|
}
|
|
|
|
// For providers that might need API keys as well (check this separately, not as fallback)
|
|
if (config.apiTokenKey && !isConfigured) {
|
|
const apiTokenEnvVar = config.apiTokenKey;
|
|
const envApiToken =
|
|
(context?.cloudflare?.env as Record<string, any>)?.[apiTokenEnvVar] ||
|
|
process.env[apiTokenEnvVar] ||
|
|
llmManager.env[apiTokenEnvVar];
|
|
|
|
// Only consider configured if API key is set and not a placeholder
|
|
const isValidApiToken =
|
|
envApiToken &&
|
|
typeof envApiToken === 'string' &&
|
|
envApiToken.trim().length > 0 &&
|
|
!envApiToken.includes('your_') && // Filter out placeholder values
|
|
!envApiToken.includes('_here') &&
|
|
envApiToken.length > 10; // API keys are typically longer than 10 chars
|
|
|
|
if (isValidApiToken) {
|
|
isConfigured = true;
|
|
configMethod = 'environment';
|
|
}
|
|
}
|
|
}
|
|
|
|
configuredProviders.push({
|
|
name: providerName,
|
|
isConfigured,
|
|
configMethod,
|
|
});
|
|
}
|
|
|
|
return json<ConfiguredProvidersResponse>({
|
|
providers: configuredProviders,
|
|
});
|
|
} catch (error) {
|
|
console.error('Error detecting configured providers:', error);
|
|
|
|
// Return default state on error
|
|
return json<ConfiguredProvidersResponse>({
|
|
providers: LOCAL_PROVIDERS.map((name) => ({
|
|
name,
|
|
isConfigured: false,
|
|
configMethod: 'none' as const,
|
|
})),
|
|
});
|
|
}
|
|
};
|