Interface BBjAdminAI

All Superinterfaces:
Remote, Serializable

public interface BBjAdminAI extends Serializable, Remote

Provides access to the Basis AI natural-language-to-SQL library from within the BBj Admin API. Use this interface to configure an LLM provider, index a BBj database schema, and convert plain-English questions into SQL SELECT statements. It also exposes a general-purpose LLM prompting API (sendPrompt(String) / sendPrompt(BBjAdminAIPromptRequest)) for any other AI-assisted feature - explanations, summaries, multi-turn chat - that does not need to go through the SQL-generation pipeline.

Obtain an instance via BBjAdminBase.getAI(). The instance is not yet configured on creation; call configure(BBjAdminAIConfig), loadDefaultConfig(), or loadConfig(String) before invoking any schema or query methods.

Typical usage pattern

Most client applications obtain a single BBjAdminAI instance when a screen, session, or job starts and reuse it for that lifetime. The instance is loaded with whatever configuration was last saved via loadDefaultConfig(); if none has been saved yet, that call throws BBjAdminException - which simply means AI-driven functionality should stay disabled until the user configures a provider:


 BBjAdminAI ai = api.getAI();
 try {
     ai.loadDefaultConfig();
 } catch (BBjAdminException e) {
     // No configuration saved yet - leave AI-driven features disabled
     // until the user configures a provider via configure(BBjAdminAIConfig).
 }
 

Once a provider is configured, prefer fetching a snapshot with getConfig(), mutating it, and handing it back to saveConfig(BBjAdminAIConfig) in a single call, rather than reconfiguring the whole engine via configure(BBjAdminAIConfig):


 BBjAdminAIConfig config = ai.getConfig();
 config.getOpenai().setApiKey(newApiKey);
 config.setActiveProvider(BBjAdminAIConfig.PROVIDER_OPENAI);
 ai.saveConfig(config);
 

Java Sample: configure, index a schema, and ask a question


 BBjAdminBase api = BBjAdminFactory.getBBjAdmin(InetAddress.getByName("myserver"), 2002, true, "admin", "admin123");
 BBjAdminAI ai = api.getAI();

 BBjAdminAIConfig cfg = new BBjAdminAIConfig();
 cfg.setActiveProvider(BBjAdminAIConfig.PROVIDER_OPENAI);
 cfg.getOpenai().setApiKey("sk-...");
 ai.configure(cfg);

 ai.indexSchema("jdbc:basis://localhost?database=ChileCompany", "admin", "admin123", null);
 BBjAdminAIQueryResult result = ai.buildQuery("Show all invoices for customer ACME Corp");
 if (result.isSuccessful()) {
     System.out.println(result.getGeneratedSql());
 } else {
     System.err.println(result.getErrorMessage());
 }
 ai.close();
 

Java Sample: general-purpose prompting alongside SQL generation


 // A one-shot explanation, independent of any indexed schema
 BBjAdminAIPromptResponse explanation = ai.sendPrompt("Explain what a correlated subquery is.");
 System.out.println(explanation.getContent());

 // A multi-turn conversation that references an earlier answer
 BBjAdminAIPromptRequest followUp = new BBjAdminAIPromptRequest("Which of those tables stores addresses?");
 followUp.addHistory(BBjAdminAIChatMessage.user("What tables are in the database?"));
 followUp.addHistory(BBjAdminAIChatMessage.assistant("CUSTOMER, INVOICE, ADDRESS"));
 BBjAdminAIPromptResponse answer = ai.sendPrompt(followUp);
 System.out.println(answer.getContent());
 

BBj Sample

use com.basis.api.admin.BBjAdminAI
use com.basis.api.admin.BBjAdminAIConfig
...
ai! = api!.getAI()
cfg! = new BBjAdminAIConfig()
cfg!.setActiveProvider(BBjAdminAIConfig.PROVIDER_OPENAI)
cfg!.getOpenai().setApiKey("sk-...")
ai!.configure(cfg!)
ai!.indexSchema("jdbc:basis://localhost?database=ChileCompany", "admin", "admin123", BBjAPI.NULL)
result! = ai!.buildQuery("Show all invoices for customer ACME Corp")
if result!.isSuccessful()
  print result!.getGeneratedSql()
endif
ai!.close()
See Also:
  • Method Summary

    Modifier and Type
    Method
    Description
    buildQuery(String p_naturalLanguageQuery)
    Converts a plain-English question into a SQL SELECT statement using a Retrieval-Augmented Generation (RAG) pipeline.
    buildQuery(String p_naturalLanguageQuery, String p_databaseName)
    Converts a plain-English question into a SQL SELECT statement, restricting schema retrieval to documents indexed under p_databaseName.
    buildQuery(String p_naturalLanguageQuery, String p_databaseName, String p_provider, String p_model)
    Converts a plain-English question into a SQL SELECT statement using an explicitly requested provider/model instead of the configured default, restricting schema retrieval to documents indexed under p_databaseName.
    void
    Deletes all documents from the schema vector index.
    void
    Closes the underlying AI engine and releases all resources (schema vector store, embedding model, etc.).
    void
    Configures the AI instance with the supplied settings and activates the specified LLM provider.
    Returns a snapshot of the currently active configuration, or null if no configuration has been set yet.
    long
    Returns the number of documents currently stored in the schema index.
    void
    indexSchema(String p_jdbcUrl, String p_username, String p_password, List<String> p_tables)
    Opens a new JDBC connection, reads the database schema via DatabaseMetaData, embeds the schema as dense vectors, and stores them in a JSON-backed vector store.
    void
    indexSchema(Connection p_connection, List<String> p_tables)
    Indexes the schema using an already-open Connection.
    Returns an alphabetically sorted, immutable list of table names that have been indexed in the schema vector store for the database identified by the given JDBC URL.
    Returns every document in the schema index, sorted by type (TABLE first, then RELATIONSHIP) then by table name.
    listTables(String p_jdbcUrl, String p_username, String p_password)
    Opens a JDBC connection and returns the names of all user tables visible to the given credentials, without modifying the schema index.
    void
    loadConfig(String p_configPath)
    Loads configuration from the specified JSON file path and activates it.
    void
    Loads configuration from the default location (~/.basis-ai/config.json) and activates it.
    void
    Encrypts sensitive fields and writes the current configuration to the default location (~/.basis-ai/config.json).
    void
    Updates the active configuration from p_config and writes it to the default location (~/.basis-ai/config.json).
    void
    saveConfig(BBjAdminAIConfig p_config, String p_configPath)
    Updates the active configuration from p_config and writes it to the specified path.
    void
    saveConfig(String p_configPath)
    Encrypts sensitive fields and writes the current configuration to the specified path.
    Sends a fully configured prompt to the LLM, supporting an explicit system prompt, conversation history, temperature override, and max-token override.
    sendPrompt(String p_prompt)
    Sends a simple one-shot prompt to the configured LLM using the configured generalSystemPrompt.
  • Method Details

    • configure

      void configure(BBjAdminAIConfig p_config) throws BBjAdminException
      Configures the AI instance with the supplied settings and activates the specified LLM provider. Any previously active configuration is discarded.

      Use this when building a configuration from scratch (e.g. an initial setup screen). To edit an already-active configuration, prefer getConfig() followed by saveConfig(BBjAdminAIConfig), which persists the change without discarding unrelated settings.

      
       BBjAdminAIConfig config = new BBjAdminAIConfig();
       config.setActiveProvider(BBjAdminAIConfig.PROVIDER_ANTHROPIC);
      
       BBjAdminAIConfig.Anthropic anthropic = config.getAnthropic();
       anthropic.setApiKey("sk-ant-...");
       anthropic.setModels(List.of("claude-sonnet-5", "claude-haiku-4-5"));
       anthropic.setDefaultModel("claude-sonnet-5");
       anthropic.setTemperature(0.2);
       anthropic.setTimeoutSeconds(60);
      
       ai.configure(config);
       
      Parameters:
      p_config - Configuration to apply. Must not be null.
      Throws:
      BBjAdminException - if the underlying AI engine cannot be initialised with the given configuration.
    • loadDefaultConfig

      void loadDefaultConfig() throws BBjAdminException
      Loads configuration from the default location (~/.basis-ai/config.json) and activates it. If the file does not exist, a default unconfigured instance is prepared.

      Client applications typically call this immediately after BBjAdminBase.getAI(), and treat a thrown exception as "no provider configured yet" rather than a fatal error:

      
       BBjAdminAI ai = api.getAI();
       try {
           ai.loadDefaultConfig();
       } catch (BBjAdminException e) {
           // AI may not be configured yet; the user will see an error
           // if they try to build a query or send a prompt before configuring.
       }
       
      Throws:
      BBjAdminException - if the configuration file cannot be read or the AI engine cannot be initialised.
    • loadConfig

      void loadConfig(String p_configPath) throws BBjAdminException
      Loads configuration from the specified JSON file path and activates it.
      
       ai.loadConfig("/etc/basis/ai/prod-config.json");
       
      Parameters:
      p_configPath - Filesystem path to a JSON configuration file.
      Throws:
      BBjAdminException - if the file cannot be read or the AI engine cannot be initialised.
    • getConfig

      Returns a snapshot of the currently active configuration, or null if no configuration has been set yet.

      A settings screen typically loads this snapshot once, lets the user edit any number of fields across one or more providers, then hands the same object back to saveConfig(BBjAdminAIConfig) in a single call:

      
       BBjAdminAIConfig config = ai.getConfig();
       if (config == null) {
           config = new BBjAdminAIConfig();
       }
      
       config.getOpenai().setModels(List.of("gpt-5", "gpt-5-mini"));
       config.getOpenai().setDefaultModel("gpt-5");
       config.setActiveProvider(BBjAdminAIConfig.PROVIDER_OPENAI);
      
       ai.saveConfig(config);
       
      Returns:
      A snapshot of the active configuration, or null if none has been set.
      Throws:
      BBjAdminException - if the configuration cannot be read.
    • saveConfig

      void saveConfig() throws BBjAdminException
      Encrypts sensitive fields and writes the current configuration to the default location (~/.basis-ai/config.json). Parent directories are created if they do not exist.
      
       ai.saveConfig();
       
      Throws:
      BBjAdminException - if the configuration cannot be written.
    • saveConfig

      void saveConfig(BBjAdminAIConfig p_config) throws BBjAdminException
      Updates the active configuration from p_config and writes it to the default location (~/.basis-ai/config.json).

      This is the preferred overload when the caller obtained a config via getConfig(), modified it, and wants to persist the changes without reinitialising the AI engine.

      
       BBjAdminAIConfig config = ai.getConfig();
      
       BBjAdminAIConfig.OpenAI openai = config.getOpenai();
       openai.setApiKey(newApiKey);
       openai.setModels(List.of("gpt-5", "gpt-5-mini"));
       openai.setDefaultModel("gpt-5");
       config.setActiveProvider(BBjAdminAIConfig.PROVIDER_OPENAI);
      
       ai.saveConfig(config);
       
      Parameters:
      p_config - The configuration to apply and save.
      Throws:
      BBjAdminException - if the configuration cannot be applied or written.
    • saveConfig

      void saveConfig(BBjAdminAIConfig p_config, String p_configPath) throws BBjAdminException
      Updates the active configuration from p_config and writes it to the specified path.
      
       ai.saveConfig(config, "/etc/basis/ai/prod-config.json");
       
      Parameters:
      p_config - The configuration to apply and save.
      p_configPath - Filesystem path to write the JSON configuration to.
      Throws:
      BBjAdminException - if the configuration cannot be applied or written.
    • saveConfig

      void saveConfig(String p_configPath) throws BBjAdminException
      Encrypts sensitive fields and writes the current configuration to the specified path.
      
       ai.saveConfig("/etc/basis/ai/prod-config.json");
       
      Parameters:
      p_configPath - Filesystem path to write the JSON configuration to.
      Throws:
      BBjAdminException - if the configuration cannot be written.
    • listIndexedTableNames

      List<String> listIndexedTableNames(String p_jdbcUrl) throws BBjAdminException
      Returns an alphabetically sorted, immutable list of table names that have been indexed in the schema vector store for the database identified by the given JDBC URL. The database name is extracted from the database query-string parameter of the URL.

      A common use of this method is driving a table-picker UI that shows every table in the database with the already-indexed ones pre-checked, as in the Enterprise Manager's AI Databases screen:

      
       String jdbcUrl = "jdbc:basis://localhost?database=ChileCompany&token=" + api.getTokenValue();
       List<String> indexed = ai.listIndexedTableNames(jdbcUrl);
      
       BBjAdminDatabase db = api.getDatabase("ChileCompany");
       for (String table : db.getTableNames(false, false)) {
           boolean isIndexed = indexed.contains(table);
           System.out.println(table + (isIndexed ? " [indexed]" : " [not indexed]"));
       }
       
      Parameters:
      p_jdbcUrl - JDBC URL containing a database=<name> parameter, e.g. jdbc:basis://localhost?database=ChileCompany&token=...
      Returns:
      sorted list of indexed table names; empty if none are indexed
      Throws:
      BBjAdminException - if the index cannot be read
    • listTables

      List<String> listTables(String p_jdbcUrl, String p_username, String p_password) throws BBjAdminException
      Opens a JDBC connection and returns the names of all user tables visible to the given credentials, without modifying the schema index.

      This is a lightweight alternative to indexSchema(java.lang.String, java.lang.String, java.lang.String, java.util.List<java.lang.String>) intended for populating a table-picker UI before the user decides which tables to index.

      
       List<String> tables = ai.listTables("jdbc:basis://localhost?database=ChileCompany", "admin", "admin123");
       // Present as a checkbox list; the checked subset becomes p_tables
       // in a subsequent call to indexSchema(...).
       
      Parameters:
      p_jdbcUrl - JDBC connection URL.
      p_username - Database username. May be null.
      p_password - Database password. May be null.
      Returns:
      Sorted, immutable list of table names.
      Throws:
      BBjAdminException - if the connection or metadata call fails.
    • indexSchema

      void indexSchema(String p_jdbcUrl, String p_username, String p_password, List<String> p_tables) throws BBjAdminException
      Opens a new JDBC connection, reads the database schema via DatabaseMetaData, embeds the schema as dense vectors, and stores them in a JSON-backed vector store. Any previously indexed data for the same tables is replaced.

      Schema indexing is a one-time operation. The schema index persists across JVM restarts; re-index only when the database schema changes.

      Indexing embeds every column/table/relationship description through the configured LLM provider, so it is comparatively slow. In a UI application, run it off the UI/event thread and report status back when it completes, e.g.:

      
       List<String> tablesToIndex = List.of("CUSTOMER", "INVOICE", "INVOICE_LINE");
       new Thread(() -> {
           try {
               String jdbcUrl = "jdbc:basis://" + host + "?database=" + dbName
                       + "&token=" + api.getTokenValue();
               // Pass null username/password -- the token in the URL authenticates the connection.
               ai.indexSchema(jdbcUrl, null, null, tablesToIndex);
               System.out.println("Index updated for " + dbName);
           } catch (BBjAdminException e) {
               System.err.println("Index update failed: " + e.getMessage());
           }
       }).start();
       
      Parameters:
      p_jdbcUrl - JDBC connection URL (e.g. jdbc:basis://localhost?database=ChileCompany).
      p_username - Database username. May be null.
      p_password - Database password. May be null.
      p_tables - Table names to index. Pass null or an empty list to index all tables in the database.
      Throws:
      BBjAdminException - if indexing fails or no AI instance has been configured yet.
    • indexSchema

      void indexSchema(Connection p_connection, List<String> p_tables) throws BBjAdminException
      Indexes the schema using an already-open Connection. The connection is not closed by this method; the caller retains ownership.

      Use this overload when the caller already manages its own JDBC connection pool or transaction and does not want a second connection opened just for indexing:

      
       try (Connection conn = DriverManager.getConnection("jdbc:basis://localhost?database=ChileCompany", "admin", "admin123")) {
           ai.indexSchema(conn, List.of("CUSTOMER", "INVOICE"));
       }
       
      Parameters:
      p_connection - An open JDBC connection.
      p_tables - Table names to index, or null / empty to index all tables.
      Throws:
      BBjAdminException - if indexing fails or no AI instance has been configured yet.
    • clearSchemaIndex

      void clearSchemaIndex() throws BBjAdminException
      Deletes all documents from the schema vector index. Useful before performing a full re-index.
      
       ai.clearSchemaIndex();
       ai.indexSchema("jdbc:basis://localhost?database=ChileCompany", "admin", "admin123", null); // full re-index
       
      Throws:
      BBjAdminException - if the index cannot be cleared.
    • getSchemaDocumentCount

      long getSchemaDocumentCount() throws BBjAdminException
      Returns the number of documents currently stored in the schema index.
      
       long count = ai.getSchemaDocumentCount();
       System.out.println(count + " schema document(s) indexed.");
       
      Returns:
      Document count.
      Throws:
      BBjAdminException - if the count cannot be retrieved.
    • listSchemaDocuments

      List<BBjAdminAISchemaDocument> listSchemaDocuments() throws BBjAdminException
      Returns every document in the schema index, sorted by type (TABLE first, then RELATIONSHIP) then by table name. Embedding vectors are not included in the returned objects.

      Useful for an inspection/debugging screen that shows exactly what context the RAG pipeline has available for a given table:

      
       for (BBjAdminAISchemaDocument doc : ai.listSchemaDocuments()) {
           if (BBjAdminAISchemaDocument.TYPE_TABLE.equals(doc.getType())) {
               System.out.println("Table: " + doc.getTableName());
           } else {
               System.out.println("Relationship involving: " + doc.getTableName());
           }
           System.out.println("  " + doc.getContent());
       }
       
      Returns:
      Immutable list of BBjAdminAISchemaDocument objects.
      Throws:
      BBjAdminException - if the index cannot be read.
    • buildQuery

      BBjAdminAIQueryResult buildQuery(String p_naturalLanguageQuery) throws BBjAdminException
      Converts a plain-English question into a SQL SELECT statement using a Retrieval-Augmented Generation (RAG) pipeline. The schema must have been indexed at least once before calling this method.

      Always check BBjAdminAIQueryResult.isSuccessful() before using BBjAdminAIQueryResult.getGeneratedSql().

      
       BBjAdminAIQueryResult result = ai.buildQuery("Show all invoices for customer ACME Corp");
       if (result.isSuccessful()) {
           System.out.println(result.getGeneratedSql());
       } else {
           System.err.println(result.getErrorMessage());
       }
       
      Parameters:
      p_naturalLanguageQuery - Plain-English description of the data to retrieve.
      Returns:
      A BBjAdminAIQueryResult containing the generated SQL or an error message.
      Throws:
      BBjAdminException - if the LLM call fails or no AI instance has been configured yet.
    • buildQuery

      BBjAdminAIQueryResult buildQuery(String p_naturalLanguageQuery, String p_databaseName) throws BBjAdminException
      Converts a plain-English question into a SQL SELECT statement, restricting schema retrieval to documents indexed under p_databaseName. Use this overload when the index contains schemas from multiple databases and queries should only consider one of them.

      In a query-builder UI, p_databaseName is typically whatever database the user currently has selected in a combo box:

      
       String selectedDatabase = databaseCombo.getItem(databaseCombo.getSelectionIndex());
       BBjAdminAIQueryResult result = ai.buildQuery(
               "List customers with overdue invoices", selectedDatabase);
       if (result.isSuccessful()) {
           sqlTextArea.setText(result.getGeneratedSql());
       } else {
           showError(result.getErrorMessage());
       }
       
      Parameters:
      p_naturalLanguageQuery - Plain-English description of the data to retrieve.
      p_databaseName - Logical database name whose indexed schema to use. Pass null to search all indexed databases.
      Returns:
      A BBjAdminAIQueryResult containing the generated SQL or an error message.
      Throws:
      BBjAdminException - if the LLM call fails or no AI instance has been configured yet.
    • buildQuery

      BBjAdminAIQueryResult buildQuery(String p_naturalLanguageQuery, String p_databaseName, String p_provider, String p_model) throws BBjAdminException
      Converts a plain-English question into a SQL SELECT statement using an explicitly requested provider/model instead of the configured default, restricting schema retrieval to documents indexed under p_databaseName.

      This overload backs a "Model:" picker that lets the user try a specific provider/model for one query without changing the configured default - pass null for both p_provider and p_model for a "(Default)" choice:

      
       BBjAdminAIQueryResult result = ai.buildQuery(
               "List customers with overdue invoices",
               "ChileCompany",
               BBjAdminAIConfig.PROVIDER_ANTHROPIC,
               "claude-sonnet-5");
       if (result.isSuccessful()) {
           sqlTextArea.setText(result.getGeneratedSql());
       } else {
           showError(result.getErrorMessage());
       }
       
      Parameters:
      p_naturalLanguageQuery - Plain-English description of the data to retrieve.
      p_databaseName - Logical database name whose indexed schema to use. Pass null to search all indexed databases.
      p_provider - Provider to use, matching one of the PROVIDER_* constants on BBjAdminAIConfig, or null to use the configured default provider.
      p_model - Model name to use, or null to use the resolved provider's configured default model.
      Returns:
      A BBjAdminAIQueryResult containing the generated SQL or an error message.
      Throws:
      BBjAdminException - if the LLM call fails or no AI instance has been configured yet.
    • sendPrompt

      BBjAdminAIPromptResponse sendPrompt(String p_prompt) throws BBjAdminException
      Sends a simple one-shot prompt to the configured LLM using the configured generalSystemPrompt. Ideal for explanations, summaries, or any non-SQL interaction.
      
       BBjAdminAIPromptResponse response = ai.sendPrompt("Summarize what a correlated subquery is.");
       System.out.println(response.getContent());
       System.out.println("Tokens used: " + response.getTotalTokens());
       
      Parameters:
      p_prompt - The user message to send.
      Returns:
      A BBjAdminAIPromptResponse containing the reply and token-usage information.
      Throws:
      BBjAdminException - if the LLM call fails or no AI instance has been configured yet.
    • sendPrompt

      Sends a fully configured prompt to the LLM, supporting an explicit system prompt, conversation history, temperature override, and max-token override.

      Use this overload to carry a multi-turn conversation, override the system prompt for one request, or pin a specific provider/model the same way buildQuery(String, String, String, String) does:

      
       BBjAdminAIPromptRequest request = new BBjAdminAIPromptRequest("Which of those tables stores addresses?");
       request.setSystemPrompt("You are a helpful BBj database assistant.");
       request.addHistory(BBjAdminAIChatMessage.user("What tables are in the database?"));
       request.addHistory(BBjAdminAIChatMessage.assistant("CUSTOMER, INVOICE, ADDRESS"));
       request.setProvider(BBjAdminAIConfig.PROVIDER_OPENAI);
       request.setModel("gpt-5-mini");
      
       BBjAdminAIPromptResponse response = ai.sendPrompt(request);
       System.out.println(response.getContent());
       
      Parameters:
      p_request - A BBjAdminAIPromptRequest describing the request.
      Returns:
      A BBjAdminAIPromptResponse containing the reply and token-usage information.
      Throws:
      BBjAdminException - if the LLM call fails or no AI instance has been configured yet.
    • close

      void close() throws BBjAdminException
      Closes the underlying AI engine and releases all resources (schema vector store, embedding model, etc.). After this call, all other methods will throw BBjAdminException until the instance is reconfigured via configure(BBjAdminAIConfig) or loadDefaultConfig().

      Call this when a screen or session that owns a BBjAdminAI instance is shutting down, mirroring how a UI's dispose() closes its other server-side resources (e.g. an open JDBC connection):

      
       public void dispose() {
           try {
               if (ai != null) {
                   ai.close();
               }
           } catch (BBjAdminException e) {
               // Ignore on shutdown
           }
       }
       
      Throws:
      BBjAdminException - if resources cannot be released cleanly.