Font Size:
Ask Joget AI

OEM Extension Guide for AI Central Config

This guide provides technical instructions on how to extend the AI Central Config plugin to integrate CustomLLM platform LLMs, customize the model list, and inject custom configuration fields.

1. Overview

The AI Central Config plugin uses a provider-based architecture. To add support for CustomLLM LLMs, you will implement a new Plugin class that implements the LLMProviderPlugin interface. This allows your implementation to be dynamically discovered by the Configuration Page.

2. Unregistering Existing LLM Implementations

The default LLM providers (e.g., OpenAI, Anthropic) are bundled within the ai-central-config-plugin JAR. You have two options to hide them:

Option A: System Property (Recommended)

This method allows you to hide providers without modifying source code. Add the following system property to your Joget startup script (tomcat-run.bat or setenv.sh):

-Dai.central.config.hidden.providers=OpenAI,Anthropic
Note
The value matches the provider name as implemented in getProviderName().

Option B: Source Code Modification Request

To permanently remove them from the compiled plugin, reach out the the Joget dev team.

3. Adding Custom LLM Implementation

To register CustomLLM as a new provider, create a new Java class (in a new Plugin project) that implements org.joget.ai.config.llm.LLMProviderPlugin.

Step 3.1: Implement the Plugin Interface

package com.customLLM.ai.plugin;


import org.joget.ai.config.llm.LLMProviderPlugin;
import org.joget.ai.config.config.LLMConfig;
import org.joget.ai.config.llm.LLMService;
import org.joget.plugin.base.ExtDefaultPlugin;
import java.util.Arrays;
import java.util.List;


public class CustomLLMProvider extends ExtDefaultPlugin implements LLMProviderPlugin {


    @Override
    public String getProviderName() {
        return "CustomLLM Platform";
    }


    @Override
    public List<String> getModelNames() {
        // Return your custom list of models
        return Arrays.asList("ev-titan-10b", "ev-code-assist", "ev-chat-pro");
    }


    @Override
    public LLMConfig createConfig() {
        // Return your custom configuration implementation
        return new CustomLLMConfig();
    }


    @Override
    public LLMService createService(LLMConfig config) {
        // Return your custom service implementation
        return new CustomLLMService(config, null); // Pass DAO if needed, or null
    }
    
    @Override
    public String getName() { return "CustomLLM Provider"; }
    
    @Override
    public String getVersion() { return "1.0.0"; }
    
    @Override
    public String getDescription() { return "CustomLLM Platform Integration"; }
    
    @Override
    public String getLabel() { return "CustomLLM"; }
    
    @Override
    public String getClassName() { return getClass().getName(); }


    @Override
    public String getPropertyOptions() {
        // Defined in Step 5
        return "";
    }
}

4. Customizing the Model List

The getModelNames() method in your provider class controls exactly what appears in the Model Name dropdown when your provider is selected.

@Override

public List<String> getModelNames() {

    return Arrays.asList("Model A", "Model B", "Custom Model C");

}

5. Injecting Custom Configuration Fields

To inject additional fields (e.g., "Tenant ID", "Project Code") into the configuration page, return a JSON definition string in getPropertyOptions(). These fields will automatically appear when CustomLLM Platform is selected.

@Override

public String getPropertyOptions() {

    return "[" +

        "{" +

            "\"name\": \"tenantId\"," +

            "\"label\": \"Tenant ID\"," +

            "\"type\": \"textfield\"," +

            "\"required\": \"true\"" +

        "}," +

        "{" +

            "\"name\": \"projectCode\"," +

            "\"label\": \"Project Code\"," +

            "\"type\": \"textfield\"" +

        "}" +

    "]";

}

5.1 Implementing Custom Configuration (LLMConfig)

Create a class extending AbstractLLMConfig to parse and store your custom fields. The fields defined in getPropertyOptions are stored within the apiKey configuration object.

package com.customLLM.ai.plugin;



import org.joget.ai.config.config.AbstractLLMConfig;

import org.json.JSONObject;



public class CustomLLMConfig extends AbstractLLMConfig {



    private String tenantId;

    private String projectCode;



    public CustomLLMConfig() {

        super("CustomLLM Platform"); // Provider Name matches getProviderName()

    }



    @Override

    protected void parseAdditionalConfig(JSONObject providerConfig) {

        // Custom fields injected via getPropertyOptions are stored in the 'apiKey' object

        Object apiKeyObj = providerConfig.opt("apiKey");

        

        if (apiKeyObj instanceof JSONObject) {

            JSONObject apiKeyJson = (JSONObject) apiKeyObj;

            this.tenantId = apiKeyJson.optString("tenantId");

            this.projectCode = apiKeyJson.optString("projectCode");

        }

    }



    // Standard defaults required by AbstractLLMConfig

    @Override protected String getDefaultApiEndpoint() { return "https://api.customllm.com/v1"; }

    @Override public String getDefaultModelName() { return "ev-titan-10b"; }

    @Override public float getDefaultTemperature() { return 0.7f; }

    @Override public int getDefaultMaxLength() { return 2048; }

    @Override public String getBasePrompt() { return "You are an CustomLLM assistant."; }

    @Override public void setBasePrompt(String s) { /* Local setter implementation */ }

    

    // Getters for your service to use

    public String getTenantId() { return tenantId; }

    public String getProjectCode() { return projectCode; }

}

5.2 Implementing Custom Service (LLMService)

Create a class extending AbstractLLMHelper to implement the actual API calls.

package com.customLLM.ai.plugin;



import org.joget.ai.config.llm.AbstractLLMHelper;

import org.joget.ai.config.config.LLMConfig;

import org.joget.ai.config.dao.LLMCallsDao;

import java.util.Map;

import java.util.List;

import java.util.HashMap;



public class CustomLLMService extends AbstractLLMHelper {



    private CustomLLMConfig evConfig;



    public CustomLLMService(LLMConfig config, LLMCallsDao dao) {

        super(config.getModelName(), null, null, config, dao);

        this.evConfig = (CustomLLMConfig) config;

    }



    @Override

    protected Map<String, Object> buildModelSpecificPayload(String prompt, List<String> imageBase64List) {

        // Build the JSON payload for CustomLLM API

        Map<String, Object> payload = new HashMap<>();

        payload.put("model", modelName);

        payload.put("prompt", prompt);

        payload.put("tenant_id", evConfig.getTenantId()); // Use custom config

        return payload;

    }



    @Override

    protected Map<String, Object> buildModelSpecificPayload(List<Map<String, String>> messageList) {

         // Chat history implementation

         Map<String, Object> payload = new HashMap<>();

         payload.put("messages", messageList);

         return payload;

    }



    @Override

    public Map<String, Object> generateText(String prompt, List<String> images, List<String> imageUrls) {

        // 1. Create payload

        // 2. Make HTTP request to evConfig.getApiEndpoint()

        // 3. Parse response

        // 4. this.response = extractedText;

        // 5. setTokenUsage(inputTokens, outputTokens);

        // 6. commitToDatabase(); // Automatically saves log

        

        Map<String, Object> result = new HashMap<>();

        result.put("text", this.response);

        return result;

    }



    @Override

    protected boolean shouldConvertUrlsToBase64() {

        return false; // Set true if API needs base64 images

    }

}

6. Customizing UI (CSS)

To Customize the CSS:

  1. Global CSS: Inject CSS via a separate system plugin or the Joget Resources Console to target .configuration-page classes.

  2. Plugin CSS: If you are modifying the source, edit src/main/resources/resources/ConfigurationPage.css.

  3. Dynamic Injection: In your CustomLLMProvider (if it were a UI plugin, which LLMProviderPlugin is not directly), you could inject scripts. Since ConfigurationPage controls the UI, the best non-intrusive way is to use Joget's global Custom CSS feature in the System Settings or App Builder.

Example CSS to verify targeting:

/* Update header color for OEM */

.configuration-page header {

    background-color: #00A9E0; /* Blue */

}

 

Created by Debanraj Ravindran Last modified by Debanraj Ravindran on Apr 24, 2026