OpenAI compatible model factory for the Embabel Agent Framework
Java-specific examples and patterns for using Embabel Agent OpenAI.
import com.embabel.agent.openai.OpenAiCompatibleModelFactory;
import com.embabel.agent.openai.OpenAiChatOptionsConverter;
import com.embabel.agent.openai.Gpt5ChatOptionsConverter;
import com.embabel.agent.openai.StandardOpenAiOptionsConverter;
import com.embabel.common.ai.model.PricingModel;
import com.embabel.agent.api.LlmService;
import com.embabel.common.ai.embedding.EmbeddingService;
import io.micrometer.observation.ObservationRegistry;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.beans.factory.ObjectProviders;
import org.springframework.retry.support.RetryTemplate;
import java.time.LocalDate;import io.micrometer.observation.ObservationRegistry;
ObservationRegistry observationRegistry = ObservationRegistry.create();
OpenAiCompatibleModelFactory factory = new OpenAiCompatibleModelFactory(
null, // baseUrl - null for OpenAI default
"sk-...", // apiKey
null, // completionsPath
null, // embeddingsPath
observationRegistry, // observationRegistry
ObjectProviders.empty() // requestFactory - empty for default
);OpenAiCompatibleModelFactory customFactory = new OpenAiCompatibleModelFactory(
"http://localhost:8000", // baseUrl
null, // apiKey - null if no auth
"/v1/chat/completions", // completionsPath
"/v1/embeddings", // embeddingsPath
observationRegistry,
ObjectProviders.empty()
);LlmService<?> gpt4Service = factory.openAiCompatibleLlm(
"gpt-4", // model
PricingModel.usdPer1MTokens(30.0, 60.0), // pricingModel
"OpenAI", // provider
LocalDate.of(2023, 4, 1), // knowledgeCutoffDate
OpenAiChatOptionsConverter.INSTANCE, // optionsConverter - note .INSTANCE
RetryUtils.DEFAULT_RETRY_TEMPLATE // retryTemplate
);Important: Kotlin singleton objects require .INSTANCE in Java.
// OpenAiChatOptionsConverter is the default
LlmService<?> service = factory.openAiCompatibleLlm(
"gpt-3.5-turbo",
PricingModel.usdPer1MTokens(0.5, 1.5),
"OpenAI",
LocalDate.of(2021, 9, 1),
OpenAiChatOptionsConverter.INSTANCE, // Explicitly pass default
RetryUtils.DEFAULT_RETRY_TEMPLATE
);LlmService<?> gpt5Service = factory.openAiCompatibleLlm(
"gpt-5-turbo",
PricingModel.usdPer1MTokens(10.0, 30.0),
"OpenAI",
LocalDate.of(2024, 10, 1),
Gpt5ChatOptionsConverter.INSTANCE, // Special converter for GPT-5
RetryUtils.DEFAULT_RETRY_TEMPLATE
);LlmService<?> service = factory.openAiCompatibleLlm(
"gpt-4",
PricingModel.usdPer1MTokens(30.0, 60.0),
"OpenAI",
LocalDate.of(2023, 4, 1),
StandardOpenAiOptionsConverter.INSTANCE, // Explicit parameter support
RetryUtils.DEFAULT_RETRY_TEMPLATE
);EmbeddingService embeddingService = factory.openAiCompatibleEmbeddingService(
"text-embedding-3-small", // model
"OpenAI" // provider
);import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.beans.factory.annotation.Value;
@Configuration
public class LlmConfiguration {
private final ObservationRegistry observationRegistry;
public LlmConfiguration(ObservationRegistry observationRegistry) {
this.observationRegistry = observationRegistry;
}
@Bean
public OpenAiCompatibleModelFactory openAiModelFactory(
@Value("${openai.api.key}") String apiKey) {
return new OpenAiCompatibleModelFactory(
null,
apiKey,
null,
null,
observationRegistry,
ObjectProviders.empty()
);
}
@Bean
public LlmService<?> gpt4Service(OpenAiCompatibleModelFactory factory) {
return factory.openAiCompatibleLlm(
"gpt-4",
PricingModel.usdPer1MTokens(30.0, 60.0),
"OpenAI",
LocalDate.of(2023, 4, 1),
OpenAiChatOptionsConverter.INSTANCE,
RetryUtils.DEFAULT_RETRY_TEMPLATE
);
}
@Bean
public EmbeddingService embeddingService(OpenAiCompatibleModelFactory factory) {
return factory.openAiCompatibleEmbeddingService(
"text-embedding-3-small",
"OpenAI"
);
}
}@Configuration
public class MultiModelConfiguration {
private final ObservationRegistry observationRegistry;
public MultiModelConfiguration(ObservationRegistry observationRegistry) {
this.observationRegistry = observationRegistry;
}
@Bean
public OpenAiCompatibleModelFactory openAiModelFactory(
@Value("${openai.api.key}") String apiKey) {
return new OpenAiCompatibleModelFactory(
null, apiKey, null, null,
observationRegistry, ObjectProviders.empty()
);
}
@Bean(name = "cheapModel")
public LlmService<?> cheapModel(OpenAiCompatibleModelFactory factory) {
return factory.openAiCompatibleLlm(
"gpt-3.5-turbo",
PricingModel.usdPer1MTokens(0.5, 1.5),
"OpenAI",
LocalDate.of(2021, 9, 1),
OpenAiChatOptionsConverter.INSTANCE,
RetryUtils.DEFAULT_RETRY_TEMPLATE
);
}
@Bean(name = "powerfulModel")
public LlmService<?> powerfulModel(OpenAiCompatibleModelFactory factory) {
return factory.openAiCompatibleLlm(
"gpt-4",
PricingModel.usdPer1MTokens(30.0, 60.0),
"OpenAI",
LocalDate.of(2023, 4, 1),
OpenAiChatOptionsConverter.INSTANCE,
RetryUtils.DEFAULT_RETRY_TEMPLATE
);
}
}import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;
@Service
public class MyService {
private final LlmService<?> cheapModel;
private final LlmService<?> powerfulModel;
public MyService(
@Qualifier("cheapModel") LlmService<?> cheapModel,
@Qualifier("powerfulModel") LlmService<?> powerfulModel) {
this.cheapModel = cheapModel;
this.powerfulModel = powerfulModel;
}
public void processSimpleTask() {
// Use cheap model
var result = cheapModel.createMessageSender(options).send(prompt);
}
public void processComplexTask() {
// Use powerful model
var result = powerfulModel.createMessageSender(options).send(prompt);
}
}import org.springframework.http.client.SimpleClientHttpRequestFactory;
SimpleClientHttpRequestFactory requestFactory = new SimpleClientHttpRequestFactory();
requestFactory.setConnectTimeout(10000); // 10 seconds
requestFactory.setReadTimeout(300000); // 5 minutes
OpenAiCompatibleModelFactory factory = new OpenAiCompatibleModelFactory(
null,
"your-api-key",
null,
null,
observationRegistry,
ObjectProvider.of(requestFactory) // Custom request factory
);import org.springframework.retry.support.RetryTemplate;
import org.springframework.retry.backoff.ExponentialBackOffPolicy;
import org.springframework.retry.policy.SimpleRetryPolicy;
// Create custom retry template
ExponentialBackOffPolicy backOffPolicy = new ExponentialBackOffPolicy();
backOffPolicy.setInitialInterval(1000);
backOffPolicy.setMultiplier(2.0);
backOffPolicy.setMaxInterval(10000);
SimpleRetryPolicy retryPolicy = new SimpleRetryPolicy(3); // 3 retries
RetryTemplate retryTemplate = new RetryTemplate();
retryTemplate.setBackOffPolicy(backOffPolicy);
retryTemplate.setRetryPolicy(retryPolicy);
// Use custom retry template
LlmService<?> service = factory.openAiCompatibleLlm(
"gpt-4",
PricingModel.usdPer1MTokens(30.0, 60.0),
"OpenAI",
LocalDate.of(2023, 4, 1),
OpenAiChatOptionsConverter.INSTANCE,
retryTemplate // Custom retry configuration
);OpenAiCompatibleModelFactory azureFactory = new OpenAiCompatibleModelFactory(
"https://your-resource.openai.azure.com",
System.getenv("AZURE_OPENAI_API_KEY"),
"/openai/deployments/gpt-4/chat/completions?api-version=2024-02-15-preview",
"/openai/deployments/embeddings/embeddings?api-version=2024-02-15-preview",
observationRegistry,
ObjectProviders.empty()
);
LlmService<?> azureService = azureFactory.openAiCompatibleLlm(
"gpt-4",
PricingModel.usdPer1MTokens(30.0, 60.0),
"Azure OpenAI",
LocalDate.of(2023, 4, 1),
OpenAiChatOptionsConverter.INSTANCE,
RetryUtils.DEFAULT_RETRY_TEMPLATE
);OpenAiCompatibleModelFactory localFactory = new OpenAiCompatibleModelFactory(
"http://localhost:11434",
null, // No API key
null,
null,
observationRegistry,
ObjectProviders.empty()
);
LlmService<?> localService = localFactory.openAiCompatibleLlm(
"llama3:70b",
PricingModel.ALL_YOU_CAN_EAT,
"Ollama",
null, // No knowledge cutoff date
OpenAiChatOptionsConverter.INSTANCE,
RetryUtils.DEFAULT_RETRY_TEMPLATE
);OpenAiCompatibleModelFactory factory = new OpenAiCompatibleModelFactory(
System.getenv("OPENAI_BASE_URL"), // Can be null
System.getenv("OPENAI_API_KEY"),
null,
null,
observationRegistry,
ObjectProviders.empty()
);PricingModel gpt4Pricing = PricingModel.usdPer1MTokens(30.0, 60.0);
PricingModel gpt35Pricing = PricingModel.usdPer1MTokens(0.5, 1.5);PricingModel freePricing = PricingModel.ALL_YOU_CAN_EAT;import com.embabel.agent.openai.OpenAiCompatibleModelFactory;
import com.embabel.agent.openai.OpenAiChatOptionsConverter;
import com.embabel.common.ai.model.PricingModel;
import com.embabel.agent.api.LlmService;
import com.embabel.common.ai.embedding.EmbeddingService;
import io.micrometer.observation.ObservationRegistry;
import org.springframework.beans.factory.ObjectProviders;
import java.time.LocalDate;
public class OpenAiExample {
public static void main(String[] args) {
// Create observation registry
ObservationRegistry observationRegistry = ObservationRegistry.create();
// Create factory
OpenAiCompatibleModelFactory factory = new OpenAiCompatibleModelFactory(
null,
System.getenv("OPENAI_API_KEY"),
null,
null,
observationRegistry,
ObjectProviders.empty()
);
// Create LLM service
LlmService<?> llmService = factory.openAiCompatibleLlm(
"gpt-4",
PricingModel.usdPer1MTokens(30.0, 60.0),
"OpenAI",
LocalDate.of(2023, 4, 1),
OpenAiChatOptionsConverter.INSTANCE,
RetryUtils.DEFAULT_RETRY_TEMPLATE
);
// Create embedding service
EmbeddingService embeddingService = factory.openAiCompatibleEmbeddingService(
"text-embedding-3-small",
"OpenAI"
);
// Use services...
System.out.println("Services created successfully!");
}
}import com.embabel.agent.openai.OpenAiCompatibleModelFactory;
import io.micrometer.observation.ObservationRegistry;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.http.client.ClientHttpRequestFactory;
public class CustomOpenAiFactory extends OpenAiCompatibleModelFactory {
public CustomOpenAiFactory(
String baseUrl,
String apiKey,
String completionsPath,
String embeddingsPath,
ObservationRegistry observationRegistry,
ObjectProvider<ClientHttpRequestFactory> requestFactory) {
super(baseUrl, apiKey, completionsPath, embeddingsPath,
observationRegistry, requestFactory);
// Access protected logger
logger.info("Custom factory initialized");
}
// Add custom methods
public LlmService<?> createGpt4() {
return openAiCompatibleLlm(
"gpt-4",
PricingModel.usdPer1MTokens(30.0, 60.0),
"OpenAI",
LocalDate.of(2023, 4, 1),
OpenAiChatOptionsConverter.INSTANCE,
RetryUtils.DEFAULT_RETRY_TEMPLATE
);
}
public LlmService<?> createGpt35Turbo() {
return openAiCompatibleLlm(
"gpt-3.5-turbo",
PricingModel.usdPer1MTokens(0.5, 1.5),
"OpenAI",
LocalDate.of(2021, 9, 1),
OpenAiChatOptionsConverter.INSTANCE,
RetryUtils.DEFAULT_RETRY_TEMPLATE
);
}
}public class LlmServiceFactory {
private final OpenAiCompatibleModelFactory factory;
public LlmServiceFactory(OpenAiCompatibleModelFactory factory) {
this.factory = factory;
}
public LlmService<?> createCheapModel() {
return factory.openAiCompatibleLlm(
"gpt-3.5-turbo",
PricingModel.usdPer1MTokens(0.5, 1.5),
"OpenAI",
LocalDate.of(2021, 9, 1),
OpenAiChatOptionsConverter.INSTANCE,
RetryUtils.DEFAULT_RETRY_TEMPLATE
);
}
public LlmService<?> createPowerfulModel() {
return factory.openAiCompatibleLlm(
"gpt-4",
PricingModel.usdPer1MTokens(30.0, 60.0),
"OpenAI",
LocalDate.of(2023, 4, 1),
OpenAiChatOptionsConverter.INSTANCE,
RetryUtils.DEFAULT_RETRY_TEMPLATE
);
}
}public class LlmServiceBuilder {
private final OpenAiCompatibleModelFactory factory;
private String model = "gpt-4";
private PricingModel pricingModel = PricingModel.usdPer1MTokens(30.0, 60.0);
private String provider = "OpenAI";
private LocalDate knowledgeCutoffDate = LocalDate.of(2023, 4, 1);
private OptionsConverter<?> optionsConverter = OpenAiChatOptionsConverter.INSTANCE;
private RetryTemplate retryTemplate = RetryUtils.DEFAULT_RETRY_TEMPLATE;
public LlmServiceBuilder(OpenAiCompatibleModelFactory factory) {
this.factory = factory;
}
public LlmServiceBuilder model(String model) {
this.model = model;
return this;
}
public LlmServiceBuilder pricingModel(PricingModel pricingModel) {
this.pricingModel = pricingModel;
return this;
}
public LlmServiceBuilder provider(String provider) {
this.provider = provider;
return this;
}
public LlmServiceBuilder knowledgeCutoffDate(LocalDate date) {
this.knowledgeCutoffDate = date;
return this;
}
public LlmServiceBuilder optionsConverter(OptionsConverter<?> converter) {
this.optionsConverter = converter;
return this;
}
public LlmServiceBuilder retryTemplate(RetryTemplate template) {
this.retryTemplate = template;
return this;
}
public LlmService<?> build() {
return factory.openAiCompatibleLlm(
model,
pricingModel,
provider,
knowledgeCutoffDate,
optionsConverter,
retryTemplate
);
}
}
// Usage
LlmService<?> service = new LlmServiceBuilder(factory)
.model("gpt-4-turbo")
.pricingModel(PricingModel.usdPer1MTokens(10.0, 30.0))
.knowledgeCutoffDate(LocalDate.of(2023, 12, 1))
.build();Singleton Objects: Use .INSTANCE to access Kotlin singleton objects:
OpenAiChatOptionsConverter.INSTANCE
Gpt5ChatOptionsConverter.INSTANCE
StandardOpenAiOptionsConverter.INSTANCENamed Parameters: Java doesn't have named parameters, so pass arguments in order
Nullable Types: Java uses null where Kotlin uses null for nullable types
Default Arguments: Java doesn't have default arguments, so you must pass all parameters explicitly
ObjectProvider: Use ObjectProviders.empty() for empty providers:
ObjectProviders.empty()Object Instantiation: Use ObjectProvider.of(instance) to wrap instances:
ObjectProvider.of(requestFactory)Install with Tessl CLI
npx tessl i tessl/maven-com-embabel-agent--embabel-agent-openai