Font Size:
Ask Joget AI

Add a new LLM provider in AI Central Config

1. Project Overview

AI Central Config is a unified configuration and abstraction layer for integrating multiple LLM providers (OpenAI, Anthropic, Google Gemini, etc.) into the Joget platform.

What it does:

  • Manages multiple LLM provider credentials
  • Provides a single factory-based API 
  • Persists every LLM API call for auditing and analytics
  • Exposes LLM API for other plugin usage(eg., AI Designer)

2. How to Add a New LLM Provider

Adding a new provider requires custom plugin that extending central config abstract and interface

create Config class > create Helper class (HTTP logic) > create Provider plugin > create AI Config Provider(additional) > register in OSGi Activator.

Step 1 Config Class

This class defines and initializes the provider configuration.

public class CustomLLMConfig extends AbstractLLMConfig {
  
    public static final String PROVIDER_NAME = "Custom";
  
    // From LLMCredentialData
    public CustomLLMConfig(LLMCredentialData data) {
        super();
	 // mapped all the require configuration 
        this.providerName    = PROVIDER_NAME;
        this.apiKey          = data.getApiKey();
        this.modelName       = data.getModelName() != null ? data.getModelName() : getDefaultModelName();
        this.customEndpointUrl = data.getCustomLlmUrl() != null ? data.getCustomLlmUrl() : "";
    }
  
    @Override protected String getDefaultApiEndpoint() { return "https://api.Custom.ai/v1"; }
    @Override public String  getDefaultModelName()     { return "Custom-model-1"; }
    @Override public float   getDefaultTemperature()   { return 0.7f; }
    @Override public int     getDefaultMaxLength()     { return 4096; }
    @Override public String  getBasePrompt()           { return "You are a helpful assistant."; }
    @Override public void    setBasePrompt(String s)   { this.basePrompt = s; }
}

Step 2 Helper/Service Class

This class implements the actual LLM API communication logic.

public class CustomLLMHelper extends AbstractLLMHelper {
  
    private final String apiKey;
    private final Gson gson = new Gson();
  
    public CustomLLMHelper(CustomLLMConfig config, LLMCallsDao dao) {
        super(config.getModelName(), config.getDefaultTemperature(),
              config.getDefaultMaxLength(), config, dao);
        this.apiKey = config.getApiKey();
    }
  
    // Build the JSON body for the Custom API
    @Override
    protected Map<String, Object> buildModelSpecificPayload(String prompt, List<String> imageBase64List) {
        Map<String, Object> payload = new LinkedHashMap<>();
        payload.put("model", modelName);
        payload.put("max_tokens", maxLength);
        payload.put("temperature", temperature);
  
        List<Map<String, Object>> messages = new ArrayList<>();
        if (basePrompt != null && !basePrompt.isEmpty()) {
            messages.add(Map.of("role", "system", "content", basePrompt));
        }
        messages.add(Map.of("role", "user", "content", prompt));
        payload.put("messages", messages);
        return payload;
    }
  
    @Override
    protected boolean shouldConvertUrlsToBase64() {
        return false;  // set true if provider requires base64 images
    }
  
    @Override
    public Map<String, Object> generateText(String prompt,
                                             List<String> imageBase64List,
                                             List<String> imageUrlList) throws Exception {
	 // custom llm implmentation call
	 // create payload
        Map<String, Object> payload = createPayload(prompt, imageBase64List, imageUrlList);

        // LLM call
	 JsonObject responseJson = new JsonObject()
	 // extract content from json response
	 String text = extractTextFromResponse(responseJson);
 this.response = text;
	
	 // persist to database
        commitToDatabase();
  	 
	 // return result in this format
        return Map.of("text", text, "data", json);
    }
}

Step 3 Provider Plugin

This plugin connects the configuration and service implementations.

public class CustomProvider extends LLMProviderAbstract {
  
    @Override public String getProviderName() { return CustomLLMConfig.PROVIDER_NAME; }
  
    @Override
    public List<String> getModelNames() {
        return List.of("Custom-model-1", "Custom-model-2");
    }
  
    @Override
    public LLMConfig createConfig(LLMCredentialData data) {
        return new CustomLLMConfig(data);
    }
  
    @Override
    public LLMService createService(LLMConfig config, LLMCallsDao dao) {
        return new CustomLLMHelper((CustomLLMConfig) config, dao);
    }
  
    @Override public String getName()        { return "Custom LLM Provider"; }
    @Override public String getVersion()     { return Activator.VERSION; }
    @Override public String getDescription() { return "Custom AI integration"; }
    @Override public String getLabel()       { return "Custom"; }
    @Override public String getClassName()   { return getClass().getName(); }
    // property for the provider in json format, this will get as additional config in llmCredentialData model
    // [ { "name": "customField", "label": "Custom Field", "type": "textfield", "required": true } ]
    @Override public String getPropertyOptions() { return ""; }
    @Override public Object execute(Map props)    { return null; }
}

Step 4 AI Config Provider (additional)

This class defines configuration UI fields for the provider in the AI config settings to centralize and reuse credentials.

public class CustomConfigProvider extends AIConfigProviderAbstract {
  
    @Override public String getProviderName() { return CustomLLMConfig.PROVIDER_NAME; }
  
    @Override
    public List<String> getSupportedCategories() {
        return List.of("LLM");  // add "EMBEDDING" if Custom supports it
    }
  
    @Override
    public String getCreateFormFields() {
        JSONArray fields = new JSONArray();
        fields.put(buildApiKeyField(true));          // required API key field
        fields.put(buildBaseUrlField(false));        // optional self-hosted URL
        return fields.toString();
    }
  
    @Override public String getVersion()   { return Activator.VERSION; }
    @Override public String getClassName() { return getClass().getName(); }
}

Step 5 Register in Activator

Register the provider in Activator.java.

// LLM Provider
registrationList.add(context.registerService(
    CustomProvider.class.getName(), new CustomProvider(), null));

That's it. The factories discover the provider automatically via PluginManager and all the plugin that supported by the central config will use it.

3. How to call the service

// create llm config, pass null to get current active config in ai services or serviceName to create spesific config
LLMConfig llmConfig = LLMConfigFactory.createByServiceOrActive(null);
// create service based on llm config
llmService = LLMServiceFactory.create(llmConfig);
// call custom llm implmentation
String answer = (String) service.generateText("Explain OSGi", null, null).get("text");
Created by Debanraj Ravindran Last modified by Debanraj Ravindran on Apr 24, 2026