text2sql-engine 0.1.1__tar.gz → 0.2.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. {text2sql_engine-0.1.1 → text2sql_engine-0.2.0}/PKG-INFO +4 -3
  2. {text2sql_engine-0.1.1 → text2sql_engine-0.2.0}/README.md +3 -2
  3. {text2sql_engine-0.1.1 → text2sql_engine-0.2.0}/pyproject.toml +1 -1
  4. {text2sql_engine-0.1.1 → text2sql_engine-0.2.0}/tests/test_pipeline.py +15 -0
  5. {text2sql_engine-0.1.1 → text2sql_engine-0.2.0}/tests/test_prompts.py +8 -1
  6. {text2sql_engine-0.1.1 → text2sql_engine-0.2.0}/text2sql/__init__.py +1 -1
  7. {text2sql_engine-0.1.1 → text2sql_engine-0.2.0}/text2sql/pipeline/generation.py +16 -0
  8. text2sql_engine-0.2.0/text2sql/prompts/generation.txt +42 -0
  9. text2sql_engine-0.2.0/text2sql/prompts/generation_raw.txt +41 -0
  10. {text2sql_engine-0.1.1 → text2sql_engine-0.2.0}/text2sql_engine.egg-info/PKG-INFO +4 -3
  11. text2sql_engine-0.1.1/text2sql/prompts/generation.txt +0 -28
  12. text2sql_engine-0.1.1/text2sql/prompts/generation_raw.txt +0 -27
  13. {text2sql_engine-0.1.1 → text2sql_engine-0.2.0}/LICENSE +0 -0
  14. {text2sql_engine-0.1.1 → text2sql_engine-0.2.0}/setup.cfg +0 -0
  15. {text2sql_engine-0.1.1 → text2sql_engine-0.2.0}/tests/test_config.py +0 -0
  16. {text2sql_engine-0.1.1 → text2sql_engine-0.2.0}/tests/test_schema.py +0 -0
  17. {text2sql_engine-0.1.1 → text2sql_engine-0.2.0}/text2sql/config.py +0 -0
  18. {text2sql_engine-0.1.1 → text2sql_engine-0.2.0}/text2sql/errors.py +0 -0
  19. {text2sql_engine-0.1.1 → text2sql_engine-0.2.0}/text2sql/logging_utils.py +0 -0
  20. {text2sql_engine-0.1.1 → text2sql_engine-0.2.0}/text2sql/pipeline/__init__.py +0 -0
  21. {text2sql_engine-0.1.1 → text2sql_engine-0.2.0}/text2sql/pipeline/json_utils.py +0 -0
  22. {text2sql_engine-0.1.1 → text2sql_engine-0.2.0}/text2sql/pipeline/linking.py +0 -0
  23. {text2sql_engine-0.1.1 → text2sql_engine-0.2.0}/text2sql/pipeline/pipeline.py +0 -0
  24. {text2sql_engine-0.1.1 → text2sql_engine-0.2.0}/text2sql/prompts/__init__.py +0 -0
  25. {text2sql_engine-0.1.1 → text2sql_engine-0.2.0}/text2sql/prompts/linking.txt +0 -0
  26. {text2sql_engine-0.1.1 → text2sql_engine-0.2.0}/text2sql/providers/__init__.py +0 -0
  27. {text2sql_engine-0.1.1 → text2sql_engine-0.2.0}/text2sql/providers/anthropic_provider.py +0 -0
  28. {text2sql_engine-0.1.1 → text2sql_engine-0.2.0}/text2sql/providers/base.py +0 -0
  29. {text2sql_engine-0.1.1 → text2sql_engine-0.2.0}/text2sql/providers/openai_provider.py +0 -0
  30. {text2sql_engine-0.1.1 → text2sql_engine-0.2.0}/text2sql/providers/registry.py +0 -0
  31. {text2sql_engine-0.1.1 → text2sql_engine-0.2.0}/text2sql/py.typed +0 -0
  32. {text2sql_engine-0.1.1 → text2sql_engine-0.2.0}/text2sql/schema/__init__.py +0 -0
  33. {text2sql_engine-0.1.1 → text2sql_engine-0.2.0}/text2sql/schema/file_provider.py +0 -0
  34. {text2sql_engine-0.1.1 → text2sql_engine-0.2.0}/text2sql/schema/models.py +0 -0
  35. {text2sql_engine-0.1.1 → text2sql_engine-0.2.0}/text2sql/schema/provider.py +0 -0
  36. {text2sql_engine-0.1.1 → text2sql_engine-0.2.0}/text2sql/schema/serialize.py +0 -0
  37. {text2sql_engine-0.1.1 → text2sql_engine-0.2.0}/text2sql/types.py +0 -0
  38. {text2sql_engine-0.1.1 → text2sql_engine-0.2.0}/text2sql_engine.egg-info/SOURCES.txt +0 -0
  39. {text2sql_engine-0.1.1 → text2sql_engine-0.2.0}/text2sql_engine.egg-info/dependency_links.txt +0 -0
  40. {text2sql_engine-0.1.1 → text2sql_engine-0.2.0}/text2sql_engine.egg-info/requires.txt +0 -0
  41. {text2sql_engine-0.1.1 → text2sql_engine-0.2.0}/text2sql_engine.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: text2sql-engine
3
- Version: 0.1.1
3
+ Version: 0.2.0
4
4
  Summary: Schema-agnostic, config-driven Text-to-SQL library (2-stage LLM pipeline). Produces a SQL string; never connects to or executes against a database.
5
5
  Author: Mehmet Kaan Durupunar
6
6
  License: MIT
@@ -70,8 +70,9 @@ user request
70
70
  reduces the schema to the relevant part and returns structured JSON. Output is
71
71
  forced via `response_format` (OpenAI) or tool-calling (Anthropic), with
72
72
  strict-JSON parsing and retry as a fallback.
73
- - **Stage 2 (generation)** takes the request and that reduced subset. It writes
74
- SQL in the target dialect. Two output modes (config `output_mode`):
73
+ - **Stage 2 (generation)** takes the request, that reduced subset, and Stage 1's
74
+ reasoning (rationale, chosen joins, column reasons). It follows that logic and
75
+ writes SQL in the target dialect. Two output modes (config `output_mode`):
75
76
  `structured` forces JSON with `sql` + `explanation`; `raw` asks for plain SQL
76
77
  text, for specialized text-to-SQL models.
77
78
  - The two stages can use **different providers and models**. For example a cheap
@@ -33,8 +33,9 @@ user request
33
33
  reduces the schema to the relevant part and returns structured JSON. Output is
34
34
  forced via `response_format` (OpenAI) or tool-calling (Anthropic), with
35
35
  strict-JSON parsing and retry as a fallback.
36
- - **Stage 2 (generation)** takes the request and that reduced subset. It writes
37
- SQL in the target dialect. Two output modes (config `output_mode`):
36
+ - **Stage 2 (generation)** takes the request, that reduced subset, and Stage 1's
37
+ reasoning (rationale, chosen joins, column reasons). It follows that logic and
38
+ writes SQL in the target dialect. Two output modes (config `output_mode`):
38
39
  `structured` forces JSON with `sql` + `explanation`; `raw` asks for plain SQL
39
40
  text, for specialized text-to-SQL models.
40
41
  - The two stages can use **different providers and models**. For example a cheap
@@ -6,7 +6,7 @@ build-backend = "setuptools.build_meta"
6
6
  # Distribution name on PyPI. The IMPORT name stays `text2sql`:
7
7
  # pip install text2sql-engine -> import text2sql
8
8
  name = "text2sql-engine"
9
- version = "0.1.1"
9
+ version = "0.2.0"
10
10
  description = "Schema-agnostic, config-driven Text-to-SQL library (2-stage LLM pipeline). Produces a SQL string; never connects to or executes against a database."
11
11
  readme = "README.md"
12
12
  requires-python = ">=3.11"
@@ -59,6 +59,21 @@ def test_stage2_receives_only_linked_tables(
59
59
  assert "Table: customers" not in stage2_user_prompt
60
60
 
61
61
 
62
+ def test_stage2_prompt_carries_linking_reasoning(
63
+ settings, schema_provider, prompts, linked_payload, generation_payload
64
+ ):
65
+ # Stage 1's rationale and joins must reach the Stage 2 prompt so the SQL
66
+ # model follows the linking logic instead of guessing.
67
+ linking = FakeProvider([linked_payload])
68
+ generation = FakeProvider([generation_payload])
69
+ engine = _engine(settings, schema_provider, prompts, linking, generation)
70
+ engine.run("top products")
71
+
72
+ stage2_user_prompt = generation.calls[0]["user"]
73
+ assert "rank by units sold" in stage2_user_prompt # from the rationale
74
+ assert "order_items.product_id = products.id" in stage2_user_prompt # a join
75
+
76
+
62
77
  def test_hallucinated_names_are_dropped(
63
78
  settings, schema_provider, prompts, generation_payload
64
79
  ):
@@ -29,7 +29,14 @@ def test_packaged_fallback_when_no_directory():
29
29
  def test_render_substitutes_placeholders():
30
30
  tmpl = PromptTemplates().load("generation.txt")
31
31
  system, user = tmpl.render(
32
- linked_schema="X", request="Y", dialect="postgres", entities="a", filters="b"
32
+ linked_schema="X",
33
+ request="Y",
34
+ dialect="postgres",
35
+ entities="a",
36
+ filters="b",
37
+ columns="c",
38
+ joins="d",
39
+ rationale="e",
33
40
  )
34
41
  assert "postgres" in system
35
42
  assert "Y" in user
@@ -37,7 +37,7 @@ from .types import (
37
37
  TokenUsage,
38
38
  )
39
39
 
40
- __version__ = "0.1.1"
40
+ __version__ = "0.2.0"
41
41
 
42
42
  __all__ = [
43
43
  "Text2SQL",
@@ -82,6 +82,19 @@ class GenerationStage:
82
82
  reduced_schema = full_schema.subset(linked.tables)
83
83
  reduced_text = schema_to_prompt(reduced_schema) if reduced_schema.tables else "(none)"
84
84
 
85
+ # Forward Stage 1's reasoning to Stage 2. The linking model already
86
+ # worked out the logic (ranges, joins, which column means what); the SQL
87
+ # model should follow it instead of guessing.
88
+ columns_text = "\n".join(
89
+ f"- {c.table}.{c.column}" + (f" — {c.reason}" if c.reason else "")
90
+ for c in linked.columns
91
+ ) or "(none)"
92
+ joins_text = "\n".join(
93
+ f"- {j.left_table}.{j.left_column} = {j.right_table}.{j.right_column}"
94
+ for j in linked.joins
95
+ ) or "(none)"
96
+ rationale_text = linked.rationale.strip() or "(none)"
97
+
85
98
  template = self._prompts.load(stage_cfg.prompt_template)
86
99
  system, user = template.render(
87
100
  linked_schema=reduced_text,
@@ -89,6 +102,9 @@ class GenerationStage:
89
102
  dialect=self._settings.general.sql_dialect,
90
103
  entities=", ".join(linked.entities) or "(none)",
91
104
  filters="; ".join(linked.filters) or "(none)",
105
+ columns=columns_text,
106
+ joins=joins_text,
107
+ rationale=rationale_text,
92
108
  )
93
109
 
94
110
  attempts = {"n": 0}
@@ -0,0 +1,42 @@
1
+ SYSTEM:
2
+ You are an expert SQL generator. You get a user request, a reduced schema (only
3
+ the relevant tables/columns/joins picked by an upstream linking step), and that
4
+ step's analysis. Produce one correct SQL query in the target dialect.
5
+
6
+ Rules:
7
+ - Use only the tables and columns in the reduced schema.
8
+ - Follow the linking analysis. It already worked out the logic; do not ignore it.
9
+ - Respect the intended comparison semantics from the analysis and filter hints.
10
+ If a value should be matched by a numeric range or an exact compare, implement
11
+ it that way. Do not fall back to substring/text (LIKE) matching unless that is
12
+ clearly what is meant.
13
+ - Use the suggested joins to connect tables. Prefer explicit JOINs.
14
+ - Write idiomatic SQL for the target dialect: {dialect}.
15
+ - Do not run the query. Only produce it.
16
+ - Return the answer strictly as the requested JSON object with two fields:
17
+ "sql" (the query) and "explanation" (one or two sentences).
18
+
19
+ USER:
20
+ Target SQL dialect: {dialect}
21
+
22
+ Reduced schema (relevant subset):
23
+ --------------------------------
24
+ {linked_schema}
25
+ --------------------------------
26
+
27
+ Linking analysis (reasoning from the previous step — follow this logic):
28
+ {rationale}
29
+
30
+ Relevant columns:
31
+ {columns}
32
+
33
+ Suggested joins:
34
+ {joins}
35
+
36
+ Detected entities: {entities}
37
+ Filter hints: {filters}
38
+
39
+ User request:
40
+ "{request}"
41
+
42
+ Write the SQL query that answers the request.
@@ -0,0 +1,41 @@
1
+ SYSTEM:
2
+ You are an expert SQL generator. You get a user request, a reduced schema (only
3
+ the relevant tables/columns/joins picked by an upstream linking step), and that
4
+ step's analysis. Produce one correct SQL query in the target dialect.
5
+
6
+ Rules:
7
+ - Use only the tables and columns in the reduced schema.
8
+ - Follow the linking analysis. It already worked out the logic; do not ignore it.
9
+ - Respect the intended comparison semantics from the analysis and filter hints.
10
+ If a value should be matched by a numeric range or an exact compare, implement
11
+ it that way. Do not fall back to substring/text (LIKE) matching unless that is
12
+ clearly what is meant.
13
+ - Use the suggested joins to connect tables. Prefer explicit JOINs.
14
+ - Write idiomatic SQL for the target dialect: {dialect}.
15
+ - Do not run the query. Only produce it.
16
+ - Output ONLY the SQL query. No explanation, no comment, no markdown fences.
17
+
18
+ USER:
19
+ Target SQL dialect: {dialect}
20
+
21
+ Reduced schema (relevant subset):
22
+ --------------------------------
23
+ {linked_schema}
24
+ --------------------------------
25
+
26
+ Linking analysis (reasoning from the previous step — follow this logic):
27
+ {rationale}
28
+
29
+ Relevant columns:
30
+ {columns}
31
+
32
+ Suggested joins:
33
+ {joins}
34
+
35
+ Detected entities: {entities}
36
+ Filter hints: {filters}
37
+
38
+ User request:
39
+ "{request}"
40
+
41
+ Write the SQL query that answers the request.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: text2sql-engine
3
- Version: 0.1.1
3
+ Version: 0.2.0
4
4
  Summary: Schema-agnostic, config-driven Text-to-SQL library (2-stage LLM pipeline). Produces a SQL string; never connects to or executes against a database.
5
5
  Author: Mehmet Kaan Durupunar
6
6
  License: MIT
@@ -70,8 +70,9 @@ user request
70
70
  reduces the schema to the relevant part and returns structured JSON. Output is
71
71
  forced via `response_format` (OpenAI) or tool-calling (Anthropic), with
72
72
  strict-JSON parsing and retry as a fallback.
73
- - **Stage 2 (generation)** takes the request and that reduced subset. It writes
74
- SQL in the target dialect. Two output modes (config `output_mode`):
73
+ - **Stage 2 (generation)** takes the request, that reduced subset, and Stage 1's
74
+ reasoning (rationale, chosen joins, column reasons). It follows that logic and
75
+ writes SQL in the target dialect. Two output modes (config `output_mode`):
75
76
  `structured` forces JSON with `sql` + `explanation`; `raw` asks for plain SQL
76
77
  text, for specialized text-to-SQL models.
77
78
  - The two stages can use **different providers and models**. For example a cheap
@@ -1,28 +0,0 @@
1
- SYSTEM:
2
- You are an expert SQL generator. You get a user request and a reduced schema
3
- (only the relevant tables/columns/joins picked by an upstream linking step).
4
- Produce one correct SQL query in the target dialect.
5
-
6
- Rules:
7
- - Use only the tables and columns in the reduced schema.
8
- - Write idiomatic SQL for the target dialect: {dialect}.
9
- - Prefer explicit JOINs using the given relations.
10
- - Do not run the query. Only produce it.
11
- - Return the answer strictly as the requested JSON object with two fields:
12
- "sql" (the query) and "explanation" (one or two sentences).
13
-
14
- USER:
15
- Target SQL dialect: {dialect}
16
-
17
- Reduced schema (relevant subset):
18
- --------------------------------
19
- {linked_schema}
20
- --------------------------------
21
-
22
- Detected entities: {entities}
23
- Filter hints: {filters}
24
-
25
- User request:
26
- "{request}"
27
-
28
- Write the SQL query that answers the request.
@@ -1,27 +0,0 @@
1
- SYSTEM:
2
- You are an expert SQL generator. You get a user request and a reduced schema
3
- (only the relevant tables/columns/joins picked by an upstream linking step).
4
- Produce one correct SQL query in the target dialect.
5
-
6
- Rules:
7
- - Use only the tables and columns in the reduced schema.
8
- - Write idiomatic SQL for the target dialect: {dialect}.
9
- - Prefer explicit JOINs using the given relations.
10
- - Do not run the query. Only produce it.
11
- - Output ONLY the SQL query. No explanation, no comment, no markdown fences.
12
-
13
- USER:
14
- Target SQL dialect: {dialect}
15
-
16
- Reduced schema (relevant subset):
17
- --------------------------------
18
- {linked_schema}
19
- --------------------------------
20
-
21
- Detected entities: {entities}
22
- Filter hints: {filters}
23
-
24
- User request:
25
- "{request}"
26
-
27
- Write the SQL query that answers the request.
File without changes