AI-powered query generation for Lotus.
Configuration
Configure AI in your application config:
config :lotus,
ai: [
enabled: true,
model: "anthropic:claude-opus-4",
api_key: {:system, "ANTHROPIC_API_KEY"}
]The model key accepts any model string supported by ReqLLM, e.g.:
"openai:gpt-4o"(default)"anthropic:claude-opus-4""google:gemini-2.0-flash""groq:llama-3.3-70b-versatile"- Any other provider supported by ReqLLM
Usage
{:ok, result} = Lotus.AI.generate_query(
prompt: "Show me all users who signed up last month",
data_source: "postgres"
)
# Returns:
# %{
# statement: "SELECT * FROM users WHERE created_at >= ...",
# variables: [],
# model: "openai:gpt-4o",
# usage: %{total_tokens: 150}
# }Error Handling
The generate_query/1 function returns structured error tuples that clients can
pattern match on for custom handling and internationalization (i18n):
{:ok, result}- Successfully generated statement{:error, :not_configured}- AI features not enabled in config{:error, :api_key_not_configured}- API key missing or invalid{:error, {:unable_to_generate, reason}}- LLM refused (not a data question){:error, term}- Other errors (API failures, network issues, etc.)
Summary
Functions
Check if AI features are enabled and configured.
Get an AI-powered plain-language explanation of a query.
Generate a query statement from a natural language prompt.
Generate a query statement from a natural language prompt, with conversation context.
Get the configured AI model string.
Get AI-powered optimization suggestions for a statement.
Check whether a specific AI feature is supported for the given source.
Return the adapter-declared (or generic fallback) reason a feature is
unsupported for this source, or nil when supported.
Types
Functions
@spec enabled?() :: boolean()
Check if AI features are enabled and configured.
Returns true if AI is properly configured, false otherwise.
Examples
Lotus.AI.enabled?()
# => true
Get an AI-powered plain-language explanation of a query.
Supports explaining a full query or a selected fragment. When a fragment is provided, the full query is sent as context so the AI can explain even isolated terms accurately.
Options
:statement(required) - The full query statement:fragment(optional) - A selected portion of the query to explain:data_source(required) - Name of the data source to resolve schema context
Returns
{:ok, result}- Map with:explanation,:model, and:usage{:error, term}- Structured error tuple
Examples
# Explain a full query
{:ok, result} = Lotus.AI.explain_query(
statement: "SELECT d.name, COUNT(o.id) FROM departments d LEFT JOIN orders o ...",
data_source: "postgres"
)
result.explanation
# => "This query shows departments ranked by total order count..."
# Explain a selected fragment
{:ok, result} = Lotus.AI.explain_query(
statement: "SELECT d.name FROM departments d LEFT JOIN employees e ON e.department_id = d.id",
fragment: "LEFT JOIN employees e ON e.department_id = d.id",
data_source: "postgres"
)
Generate a query statement from a natural language prompt.
Uses the globally configured AI provider from application config.
Options
:prompt(required) - Natural language description of desired query:data_source(required) - Name of the data source to query against:read_only(optional) - Whentrue(default), the AI only generates read-only queries. Set tofalseto allow the AI to generate write queries.
Returns
{:ok, result}- Successfully generated statement with metadata{:error, term}- Structured error tuple (see module docs for error types)
Examples
{:ok, result} = Lotus.AI.generate_query(
prompt: "Count active users by signup month",
data_source: "postgres"
)
result.statement
# => "SELECT DATE_TRUNC('month', created_at) as month, COUNT(*) FROM users WHERE status = 'active' GROUP BY month"
result.model
# => "openai:gpt-4o"
result.usage
# => %{prompt_tokens: 150, completion_tokens: 50, total_tokens: 200}
# Error handling with pattern matching
{:error, :not_configured} = Lotus.AI.generate_query(
prompt: "some query",
data_source: "postgres"
)
Generate a query statement from a natural language prompt, with conversation context.
Enables multi-turn conversations by accepting conversation history. The AI can refine queries, fix errors, and provide iterative improvements.
Options
:prompt(required) - Natural language description of desired query:data_source(required) - Name of the data source to query against:conversation(optional) - Conversation struct with message history:read_only(optional) - Whentrue(default), the AI only generates read-only queries. Set tofalseto allow the AI to generate write queries.
Returns
{:ok, result}- Successfully generated statement with metadata{:error, term}- Structured error tuple (see module docs for error types)
Examples
# Simple single-turn (same as generate_query/1)
{:ok, result} = Lotus.AI.generate_query_with_context(
prompt: "Show active users",
data_source: "postgres"
)
# Multi-turn with conversation history
conversation = Conversation.new()
conversation = Conversation.add_user_message(conversation, "Show active users")
{:ok, result} = Lotus.AI.generate_query_with_context(
prompt: "Show active users",
data_source: "postgres",
conversation: conversation
)
# If query fails, add error to conversation
conversation = Conversation.add_query_result(conversation, {:error, "column 'status' not found"})
# AI can now fix the error with full context
{:ok, fixed_result} = Lotus.AI.generate_query_with_context(
prompt: "Fix the error",
data_source: "postgres",
conversation: conversation
)
@spec model() :: {:ok, String.t()} | {:error, :not_configured}
Get the configured AI model string.
Returns the full model string (e.g. "openai:gpt-4o") if configured.
Examples
Lotus.AI.model()
# => {:ok, "openai:gpt-4o"}
Get AI-powered optimization suggestions for a statement.
Runs the adapter's prepare_for_analysis/2 + query_plan/3 to get an
execution plan (when the engine exposes one), then asks the AI to
review the statement and the plan for potential improvements. Adapters
that can't produce a plan still get structural suggestions.
Options
:statement(required) — a%Lotus.Query.Statement{}to review.:data_source(required) — name of the data source.:search_path(optional) — Postgres search path.
Returns
{:ok, result}— map with:suggestions,:model,:usage.{:error, :ai_not_supported_for_source}— the adapter opted out of AI via itsai_context/1callback.{:error, term}— other failure.
Check whether a specific AI feature is supported for the given source.
Reads ai_context.capabilities[feature] via
Lotus.Source.Adapter.ai_context/1. Returns false if the adapter
opts out of AI entirely (returns {:error, _} from ai_context/1).
feature is one of :generation, :optimization, :explanation.
UIs should call this before rendering each AI button to hide or disable options the data source doesn't support.
Return the adapter-declared (or generic fallback) reason a feature is
unsupported for this source, or nil when supported.
Reasons from untrusted adapters are replaced with a generic fallback at the dispatch layer — UIs can surface this string verbatim.