| """Question enrichment (phase A4, E-SQL arXiv:2409.16751). |
| |
| E-SQL reports ~+5 EA on challenging BIRD from a single extra LLM call that |
| rewrites the question into an explicit specification β conditions, steps and |
| schema names spelled out. The enriched text goes into the generate prompt |
| *in addition to* the original question (never instead: the original stays |
| authoritative, so an enrichment mistake cannot override it). |
| |
| Default OFF β wired only when ``PipelineConfig.enrich_question`` is True and |
| ``enrichment_provider`` is set. The call rides the ordinary provider stack, |
| so it is cached alongside generation calls and replays on reruns. |
| """ |
|
|
| from __future__ import annotations |
|
|
| import re |
|
|
| from nl_sql.agent.prompts import load_prompt |
| from nl_sql.llm.providers.base import GenerateRequest, LLMProvider |
|
|
| _ENRICH_MAX_TOKENS = 1024 |
| _MAX_ENRICHED_CHARS = 2000 |
| _FENCE_RE = re.compile(r"```\w*\s*|\s*```") |
|
|
|
|
| def clean_enriched_text(text: str) -> str: |
| """Normalise the model reply into a short plain-text restatement. |
| |
| Strips code fences and whitespace; truncates runaway replies (the block |
| is auxiliary β a wall of text would drown the actual question). Returns |
| ``""`` for empty/whitespace replies so the caller can skip the block. |
| """ |
| cleaned = _FENCE_RE.sub("", text or "").strip() |
| if len(cleaned) > _MAX_ENRICHED_CHARS: |
| cleaned = cleaned[:_MAX_ENRICHED_CHARS].rsplit("\n", 1)[0].strip() |
| return cleaned |
|
|
|
|
| def enrich_question( |
| provider: LLMProvider, |
| *, |
| question: str, |
| schema_text: str, |
| ) -> str: |
| """One LLM call β explicit restatement of the question ("" on empty).""" |
| prompt = load_prompt( |
| "enrich_question", |
| schema_block=schema_text, |
| question=question, |
| ) |
| response = provider.generate( |
| GenerateRequest(prompt=prompt, max_tokens=_ENRICH_MAX_TOKENS, temperature=0.0) |
| ) |
| return clean_enriched_text(response.text) |
|
|