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;
import java.net.http.HttpClient;
import java.time.Duration;
/** Factory for creating {@link LlmClient} instances. */
public class LlmClientFactory {
private LlmClientFactory() {
}
public static LlmClient create(LlmProvider provider) {
return create(provider, null, LlmInferenceSettings.unspecified());
}
public static LlmClient create(LlmProvider provider, String apiKey) {
return create(provider, apiKey, LlmInferenceSettings.unspecified());
}
/**
* Creates a provider client and applies the provider-independent inference
* settings. A blank API key still falls back to the provider-specific environment
* variable.
*/
public static LlmClient create(LlmProvider provider, String apiKey, LlmInferenceSettings settings) {
LlmInferenceSettings effectiveSettings = settings != null ? settings : LlmInferenceSettings.unspecified();
String effectiveApiKey = apiKey == null || apiKey.isBlank() ? environmentApiKey(provider) : apiKey;
LlmClient client = switch (provider) {
case GEMINI -> effectiveSettings.modelName() == null
? new GeminiClient(effectiveApiKey)
: new GeminiClient(effectiveApiKey, newHttpClient(), effectiveSettings.modelName());
case OPENAI -> new OpenAiClient(effectiveApiKey);
case DEEPSEEK -> new DeepSeekClient(effectiveApiKey);
case QWEN -> new QwenClient(effectiveApiKey);
case LLAMA -> new LlamaClient(effectiveApiKey);
case MISTRAL -> new MistralClient(effectiveApiKey);
};
client.applyInferenceSettings(effectiveSettings);
return client;
}
public static LlmClient createFromEnvironment(String explicitProvider) {
return createFromEnvironment(explicitProvider, LlmInferenceSettings.unspecified());
}
/** Creates a client by auto-detecting the provider while preserving inference settings. */
public static LlmClient createFromEnvironment(String explicitProvider, LlmInferenceSettings settings) {
if (explicitProvider != null && !explicitProvider.isBlank()) {
return create(LlmProvider.fromString(explicitProvider), null, settings);
}
String envProvider = System.getenv("LLM_PROVIDER"); //$NON-NLS-1$
if (envProvider != null && !envProvider.isBlank()) {
return create(LlmProvider.fromString(envProvider), null, settings);
}
for (LlmProvider provider : new LlmProvider[] {
LlmProvider.OPENAI, LlmProvider.GEMINI, LlmProvider.DEEPSEEK,
LlmProvider.QWEN, LlmProvider.LLAMA, LlmProvider.MISTRAL }) {
String key = environmentApiKey(provider);
if (key != null && !key.isBlank()) {
return create(provider, key, settings);
}
}
return create(LlmProvider.GEMINI, null, settings);
}
private static String environmentApiKey(LlmProvider provider) {
return System.getenv(switch (provider) {
case GEMINI -> "GEMINI_API_KEY"; //$NON-NLS-1$
case OPENAI -> "OPENAI_API_KEY"; //$NON-NLS-1$
case DEEPSEEK -> "DEEPSEEK_API_KEY"; //$NON-NLS-1$
case QWEN -> "DASHSCOPE_API_KEY"; //$NON-NLS-1$
case LLAMA -> "LLAMA_API_KEY"; //$NON-NLS-1$
case MISTRAL -> "MISTRAL_API_KEY"; //$NON-NLS-1$
});
}
private static HttpClient newHttpClient() {
return HttpClient.newBuilder().connectTimeout(Duration.ofSeconds(30)).build();
}
}