Class AiToolsLlmDataQueryService

java.lang.Object
io.jmix.reports.llm.impl.AiToolsLlmDataQueryService
All Implemented Interfaces:
LlmDataQueryService

public class AiToolsLlmDataQueryService extends Object implements LlmDataQueryService
Implements the Reports-side seam on top of the AI Tools data-load subsystem: it turns a prompt into a query, gives a faulty generated query one chance to be corrected, and checks a query — all of which happen while a report is authored in the designer.

The only class in Reports that depends on the AI Tools add-on, and therefore the only one that must not be loaded when the add-on is absent — its bean is declared by a conditional auto-configuration.

  • Field Details

  • Constructor Details

    • AiToolsLlmDataQueryService

      public AiToolsLlmDataQueryService()
  • Method Details

    • isGenerationAvailable

      public boolean isGenerationAvailable()
      The add-on's data-load beans are declared on properties alone, so they are there even when no model is configured for the application. Generation then fails on the first call, which the designer has no reason to find out by trying.
      Specified by:
      isGenerationAvailable in interface LlmDataQueryService
      Returns:
      true if generation can be performed
    • generate

      public LlmDataQuery generate(LlmQueryGenerationRequest request)
      Description copied from interface: LlmDataQueryService
      Generates a query for the given prompt. An implementation may correct a query it finds faulty before answering with it — a generated query is the model's, and no one is waiting to be told about a mistake only the model made.
      Specified by:
      generate in interface LlmDataQueryService
      Parameters:
      request - prompt together with the parameters the query may reference
      Returns:
      the generated query
    • toGenerationRequest

      protected EntityDataLoadGenerationRequest toGenerationRequest(LlmQueryGenerationRequest request)
      Turns the seam request into the add-on's structured generation request: the parameters and their binding flags become data, and the only prose Reports still owns — the cross-tab narrowing — goes into the prompt the add-on treats as free text.
      Parameters:
      request - the seam request
      Returns:
      the add-on generation request carrying the mapped parameters, the required result columns and the prompt with any cross-tab intent appended
    • composePrompt

      protected String composePrompt(LlmQueryGenerationRequest request)
      Appends the cross-tab intent to the prompt when the band is one: the required result columns are its axes, and each row must carry its axis values under those exact names. When an axis parameter is multi-valued, the query is narrowed to the matrix by matching it with IN. This is Reports' own framing, so it stays here rather than in the neutral add-on request.
      Parameters:
      request - the seam request
      Returns:
      the prompt, with the cross-tab intent appended when there are required result columns
    • repairIfNeeded

      protected LlmDataQuery repairIfNeeded(String userText, LlmDataQuery query)
      Gives a query the add-on found faulty one chance to be corrected, which is what the add-on's own generation path does. Repair belongs to generation alone: a query the author then edits by hand is theirs, and a report run has no model to ask.

      Best effort — a query that is still faulty afterwards, and a repair that could not be carried out at all, are both answered with a query rather than with a failure: the designer says what is wrong with it, and an author can correct by hand what a model could not.

      Parameters:
      userText - the request the query was generated from, which repair is told to satisfy
      query - the generated query
      Returns:
      the repaired query, or the generated one when repair did not happen or did not help
    • toExecutionRequest

      protected JpqlExecutionRequest toExecutionRequest(String userText, LlmDataQuery query)
    • resultPropertiesOf

      protected List<String> resultPropertiesOf(String jpql)
      Reads the columns of a repaired query off its select clause. Repair answers with a query text alone — the add-on carries the columns beside it, unchanged — while a repair can well rename them, which is what it does with an alias that turned out to be a reserved word.

      Columns are handed to the query by position, so a list naming fewer values than the query selects would put values under the wrong names, silently. A repair is asked to alias every selected value; when it did not, its columns are unreadable and the repair is not used at all — pairing its text with the columns of the generated query would be the same mismatch by another route.

      Parameters:
      jpql - the repaired query
      Returns:
      the columns the repaired query returns in select-clause order, or an empty list when they cannot be read from it
    • validate

      public List<String> validate(LlmDataQuery query)
      Description copied from interface: LlmDataQueryService
      Reports what makes a query unrunnable, without running it, without asking a model anything and without correcting anything: a query checked here belongs to whoever wrote it. The designer asks after a query is generated or edited, so that an author learns of a broken query while looking at it rather than on the next report run.
      Specified by:
      validate in interface LlmDataQueryService
      Parameters:
      query - query to check
      Returns:
      one message per problem, in no particular order, or an empty list if the query is runnable
    • retainNonNull

      protected <T> List<T> retainNonNull(@Nullable List<T> values)
    • toQueryParameters

      protected List<LlmQueryParameter> toQueryParameters(String jpql, List<GeneratedJpqlParameter> generatedParameters)
      Declares the parameters the generated text references, and nothing else. The model's own list only supplies the Java types: a list that says anything else than the text does is what the add-on rejects a query for, and a query stored with such a list fails on every run instead of once here.

      The values are dropped along the way: the values a model invents belong to the request it was generated for, while the query is stored and reused.

    • toGeneratedResult

      protected GeneratedJpqlResult toGeneratedResult(LlmDataQuery query)
      Describes a query the way the add-on's validation reads one. The arguments are not part of it: a check is about the query alone, while the values it is run with are decided per run.