LlmClientFactory.java

/*******************************************************************************
 * Copyright (c) 2025 Carsten Hammer.
 *
 * This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License 2.0
 * which accompanies this distribution, and is available at
 * https://www.eclipse.org/legal/epl-2.0/
 *
 * SPDX-License-Identifier: EPL-2.0
 *
 * Contributors:
 *     Carsten Hammer
 *******************************************************************************/
package org.sandbox.jdt.triggerpattern.llm;

/**
 * Factory for creating {@link LlmClient} instances.
 */
public class LlmClientFactory {

private LlmClientFactory() {
// utility class
}

/**
 * Creates an {@link LlmClient} for the given provider.
 *
 * @param provider the LLM provider to use
 * @return the configured client
 */
public static LlmClient create(LlmProvider provider) {
return switch (provider) {
case GEMINI -> new GeminiClient();
case OPENAI -> new OpenAiClient();
case DEEPSEEK -> new DeepSeekClient();
case QWEN -> new QwenClient();
case LLAMA -> new LlamaClient();
case MISTRAL -> new MistralClient();
};
}

/**
 * Creates an {@link LlmClient} for the given provider with an explicit API key.
 *
 * <p>Use this overload when the API key is available from Eclipse preferences
 * or another non-environment source. If {@code apiKey} is {@code null} or blank,
 * the client falls back to reading from its provider-specific environment variable.</p>
 *
 * @param provider the LLM provider to use
 * @param apiKey   explicit API key (may be {@code null} or blank to fall back to env)
 * @return the configured client
 */
public static LlmClient create(LlmProvider provider, String apiKey) {
if (apiKey == null || apiKey.isBlank()) {
return create(provider);
}
return switch (provider) {
case GEMINI -> new GeminiClient(apiKey);
case OPENAI -> new OpenAiClient(apiKey);
case DEEPSEEK -> new DeepSeekClient(apiKey);
case QWEN -> new QwenClient(apiKey);
case LLAMA -> new LlamaClient(apiKey);
case MISTRAL -> new MistralClient(apiKey);
};
}

/**
 * Creates an {@link LlmClient} by auto-detecting the provider.
 *
 * <p>Priority order:
 * <ol>
 *   <li>Explicit CLI argument ({@code explicitProvider} parameter, non-blank)</li>
 *   <li>Environment variable {@code LLM_PROVIDER}</li>
 *   <li>Auto-detect from available API keys:
 *     <ul>
 *       <li>If {@code OPENAI_API_KEY} is set → {@link LlmProvider#OPENAI}</li>
 *       <li>If {@code GEMINI_API_KEY} is set → {@link LlmProvider#GEMINI}</li>
 *     </ul>
 *   </li>
 *   <li>Default: {@link LlmProvider#GEMINI}</li>
 * </ol>
 *
 * @param explicitProvider explicit provider name from CLI (may be null or blank)
 * @return the configured client
 */
public static LlmClient createFromEnvironment(String explicitProvider) {
// 1. Explicit CLI argument
if (explicitProvider != null && !explicitProvider.isBlank()) {
return create(LlmProvider.fromString(explicitProvider));
}

// 2. Environment variable
String envProvider = System.getenv("LLM_PROVIDER"); //$NON-NLS-1$
if (envProvider != null && !envProvider.isBlank()) {
return create(LlmProvider.fromString(envProvider));
}

// 3. Auto-detect from available API keys
String openAiKey = System.getenv("OPENAI_API_KEY"); //$NON-NLS-1$
if (openAiKey != null && !openAiKey.isBlank()) {
return create(LlmProvider.OPENAI);
}
String geminiKey = System.getenv("GEMINI_API_KEY"); //$NON-NLS-1$
if (geminiKey != null && !geminiKey.isBlank()) {
return create(LlmProvider.GEMINI);
}
String deepSeekKey = System.getenv("DEEPSEEK_API_KEY"); //$NON-NLS-1$
if (deepSeekKey != null && !deepSeekKey.isBlank()) {
return create(LlmProvider.DEEPSEEK);
}
String dashScopeKey = System.getenv("DASHSCOPE_API_KEY"); //$NON-NLS-1$
if (dashScopeKey != null && !dashScopeKey.isBlank()) {
return create(LlmProvider.QWEN);
}
String llamaKey = System.getenv("LLAMA_API_KEY"); //$NON-NLS-1$
if (llamaKey != null && !llamaKey.isBlank()) {
return create(LlmProvider.LLAMA);
}
String mistralKey = System.getenv("MISTRAL_API_KEY"); //$NON-NLS-1$
if (mistralKey != null && !mistralKey.isBlank()) {
return create(LlmProvider.MISTRAL);
}

// 4. Default: GEMINI
return create(LlmProvider.GEMINI);
}
}