Skip to content

Fix LLMSQLQueryOperator not stripping single-line markdown code fences - #70137

Merged
kaxil merged 2 commits into
apache:mainfrom
ColtenOuO:fix-llm-sql-single-line-fence
Jul 21, 2026
Merged

Fix LLMSQLQueryOperator not stripping single-line markdown code fences#70137
kaxil merged 2 commits into
apache:mainfrom
ColtenOuO:fix-llm-sql-single-line-fence

Conversation

@ColtenOuO

Copy link
Copy Markdown
Contributor

Summary

LLMSQLQueryOperator._strip_llm_output strips markdown code fences from LLM output before the generated SQL is validated/executed. The existing logic only handles fences where the LLM's response spans multiple lines
(e.g. ```sql\nSELECT 1\n```) — it splits on "\n" and requires at least 2 lines before attempting to drop the opening/closing fence.

When the whole fenced response is on a single line (e.g. ```SELECT 1```, no internal newline), text.split("\n") yields a
single element, the len(lines) >= 2 guard is never satisfied, and the fence-stripping is skipped entirely — the backticks stay attached and the value is passed straight into _validate_sql (and potentially execution) as-is.

The operator's system prompt already asks the LLM not to use markdown at all ("Return ONLY the SQL query, no explanation or markdown."), but the existing multi-line fence-handling logic (and its test coverage) already assumes LLMs commonly ignore that instruction and wrap output in a fence anyway.

This change extends the same defensive handling to the single-line variant of that same behavior, which the multi-line path doesn't cover.

Changes

  • _strip_llm_output gains an elif branch for the case where the fenced
    text has no internal newline: strip the leading/trailing ``` directly
    instead of relying on line-splitting. The existing multi-line branch is
    untouched, so behavior for every previously-tested case is unchanged.

Test plan

  • Added two regression cases to TestStripLLMOutput: a single-line fence
    with no language tag, and one wrapping a longer query.
  • Full test_llm_sql.py suite passes (44 passed), including all
    pre-existing multi-line fence cases unchanged.
  • ruff format / ruff check, prek --stage pre-commit, and
    breeze run mypy all clean on the changed file.

_strip_llm_output only removed ```sql fences when the LLM's response
spanned multiple lines. LLMs that wrap short queries in a fence on a
single line (e.g. "```SELECT 1```") passed the fenced text straight
into SQL validation/execution with the backticks still attached. Add
a fallback branch for the single-line case, alongside the existing
multi-line handling.
@ColtenOuO
ColtenOuO force-pushed the fix-llm-sql-single-line-fence branch from d7f205b to e6a9e6c Compare July 20, 2026 15:50
Comment thread providers/common/ai/src/airflow/providers/common/ai/operators/llm_sql.py Outdated
Address review feedback: a single-line fence like "```sql SELECT 1```"
left "sql " stuck to the front of the query, since the earlier fix only
stripped the outer backticks. Drop the leading word too, but only when
it's "sql" or the resolved dialect, so a real leading SQL keyword is
never mistaken for a tag.
@kaxil
kaxil merged commit 1073e49 into apache:main Jul 21, 2026
83 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants