# `Lotus.AI.Prompts.QueryGeneration`
[🔗](https://github.com/elixir-lotus/lotus/blob/v1.0.0/lib/lotus/ai/prompts/query_generation.ex#L1)

System prompts for query generation.

Prompts are assembled in a fixed order so that **core Lotus rules** (the
`{{var}}` / `[[...]]` template DSL, the response contract, the workflow
instructions) always precede **adapter-contributed content** (the
`ai_context.syntax_notes` and `:example_query`). Untrusted adapters have
their free-form fields stripped upstream in
`Lotus.Source.Adapter.ai_context/1`; placing core content first means a
compromised adapter cannot override the Lotus DSL rules through later
text.

# `extract_response`

```elixir
@spec extract_response(String.t()) ::
  {:ok, %{statement: String.t(), variables: [map()]}}
  | {:error, {:unable_to_generate, String.t()}}
```

Extract both the statement and its variables from LLM response content.

Combines `extract_statement/1` and `extract_variables/1` into a single call.
This is the primary extraction entry point for response parsing.

## Parameters

- `content` - Raw response content from the LLM

## Returns

- `{:ok, %{statement: String.t(), variables: [map()]}}` - Successfully extracted
- `{:error, {:unable_to_generate, reason}}` - LLM refused to generate

## Examples

    iex> extract_response("```sql\nSELECT * FROM users WHERE status = {{status}}\n```\n```variables\n[{\"name\": \"status\"}]\n```")
    {:ok, %{statement: "SELECT * FROM users WHERE status = {{status}}", variables: [...]}}

# `extract_statement`

```elixir
@spec extract_statement(String.t()) ::
  {:ok, String.t()} | {:error, {:unable_to_generate, String.t()}}
```

Extract the generated statement from LLM response content.

Accepts any fence label, since the prompt asks for the adapter's language
family, and detects when the LLM has refused to generate.

## Parameters

- `content` - Raw response content from the LLM

## Returns

- `{:ok, statement}` - Successfully extracted statement
- `{:error, {:unable_to_generate, reason}}` - LLM refused to generate

## Examples

    iex> extract_statement("```sql\nSELECT * FROM users\n```")
    {:ok, "SELECT * FROM users"}

    iex> extract_statement("UNABLE_TO_GENERATE: This is a weather question")
    {:error, {:unable_to_generate, "This is a weather question"}}

# `extract_variables`

```elixir
@spec extract_variables(String.t()) :: [map()]
```

Extract variable configurations from LLM response content.

Parses a ```` ```variables ```` JSON block from the response. Returns an empty list
if no block is found or if the JSON is malformed.

## Parameters

- `content` - Raw response content from the LLM

## Returns

List of variable configuration maps (empty list if none found).

## Examples

    iex> extract_variables("```variables\n[{\"name\": \"status\", \"type\": \"text\"}]\n```")
    [%{"name" => "status", "type" => "text", "widget" => "input", "list" => false}]

    iex> extract_variables("Just a statement without variables")
    []

# `system_prompt`

```elixir
@spec system_prompt(map(), [String.t()], keyword()) :: String.t()
```

Generate system prompt for query generation.

## Composition order

Each section is emitted in this order to establish the trust boundary:

  1. System role (core)
  2. Read-only / write-only instructions (core)
  3. Available tables (core, schema context)
  4. Tools + workflow (core)
  5. Lotus template DSL rules (core, immutable — `lotus_template_notes/0` + `Variables.system_docs/1`)
  6. Adapter `syntax_notes` (filtered if untrusted, truncated at 1 KB)
  7. Adapter `example_query` (bounded at 2 KB)
  8. Output contract examples (core)

## Parameters

  * `ai_context` — the sanitized map returned by
    `Lotus.Source.Adapter.ai_context/1` (`:language`, `:example_query`,
    `:syntax_notes`, `:error_patterns`).
  * `table_names` — list of available table names.
  * `opts` — keyword list:
    * `:read_only` — when `true` (default), instruct the LLM to stick to
      read-only statements.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
