text2sql-engine 0.1.0__py3-none-any.whl
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.
- text2sql/__init__.py +67 -0
- text2sql/config.py +251 -0
- text2sql/errors.py +43 -0
- text2sql/logging_utils.py +54 -0
- text2sql/pipeline/__init__.py +9 -0
- text2sql/pipeline/generation.py +110 -0
- text2sql/pipeline/json_utils.py +108 -0
- text2sql/pipeline/linking.py +206 -0
- text2sql/pipeline/pipeline.py +156 -0
- text2sql/prompts/__init__.py +96 -0
- text2sql/prompts/generation.txt +28 -0
- text2sql/prompts/linking.txt +28 -0
- text2sql/providers/__init__.py +19 -0
- text2sql/providers/anthropic_provider.py +123 -0
- text2sql/providers/base.py +131 -0
- text2sql/providers/openai_provider.py +125 -0
- text2sql/providers/registry.py +38 -0
- text2sql/py.typed +0 -0
- text2sql/schema/__init__.py +18 -0
- text2sql/schema/file_provider.py +140 -0
- text2sql/schema/models.py +84 -0
- text2sql/schema/provider.py +30 -0
- text2sql/schema/serialize.py +55 -0
- text2sql/types.py +138 -0
- text2sql_engine-0.1.0.dist-info/METADATA +304 -0
- text2sql_engine-0.1.0.dist-info/RECORD +29 -0
- text2sql_engine-0.1.0.dist-info/WHEEL +5 -0
- text2sql_engine-0.1.0.dist-info/licenses/LICENSE +21 -0
- text2sql_engine-0.1.0.dist-info/top_level.txt +1 -0
text2sql/types.py
ADDED
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
"""Dataclasses returned by the pipeline.
|
|
2
|
+
|
|
3
|
+
Plain dataclasses, no external deps. Easy to inspect and serialize.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from __future__ import annotations
|
|
7
|
+
|
|
8
|
+
import time
|
|
9
|
+
from dataclasses import dataclass, field
|
|
10
|
+
from typing import Any
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
@dataclass
|
|
14
|
+
class TokenUsage:
|
|
15
|
+
"""Token counts for one LLM call. Zero when the provider reports nothing."""
|
|
16
|
+
|
|
17
|
+
prompt_tokens: int = 0
|
|
18
|
+
completion_tokens: int = 0
|
|
19
|
+
total_tokens: int = 0
|
|
20
|
+
|
|
21
|
+
def __add__(self, other: "TokenUsage") -> "TokenUsage":
|
|
22
|
+
return TokenUsage(
|
|
23
|
+
prompt_tokens=self.prompt_tokens + other.prompt_tokens,
|
|
24
|
+
completion_tokens=self.completion_tokens + other.completion_tokens,
|
|
25
|
+
total_tokens=self.total_tokens + other.total_tokens,
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
@dataclass
|
|
30
|
+
class LLMResponse:
|
|
31
|
+
"""One provider call, normalized.
|
|
32
|
+
|
|
33
|
+
Same shape for every provider, so the pipeline never touches an SDK type.
|
|
34
|
+
``raw`` keeps the native response for advanced callers.
|
|
35
|
+
"""
|
|
36
|
+
|
|
37
|
+
text: str
|
|
38
|
+
usage: TokenUsage = field(default_factory=TokenUsage)
|
|
39
|
+
model: str = ""
|
|
40
|
+
raw: Any = None
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
@dataclass
|
|
44
|
+
class LinkedColumn:
|
|
45
|
+
"""A column picked by Stage 1."""
|
|
46
|
+
|
|
47
|
+
table: str
|
|
48
|
+
column: str
|
|
49
|
+
reason: str = ""
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
@dataclass
|
|
53
|
+
class LinkedJoin:
|
|
54
|
+
"""A candidate join between two tables, found during linking."""
|
|
55
|
+
|
|
56
|
+
left_table: str
|
|
57
|
+
left_column: str
|
|
58
|
+
right_table: str
|
|
59
|
+
right_column: str
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
@dataclass
|
|
63
|
+
class LinkedSchema:
|
|
64
|
+
"""Stage 1 output: the reduced schema.
|
|
65
|
+
|
|
66
|
+
This is what Stage 2 receives. It is also returned on the final result so
|
|
67
|
+
callers can see what the pipeline picked as relevant.
|
|
68
|
+
"""
|
|
69
|
+
|
|
70
|
+
tables: list[str] = field(default_factory=list)
|
|
71
|
+
columns: list[LinkedColumn] = field(default_factory=list)
|
|
72
|
+
joins: list[LinkedJoin] = field(default_factory=list)
|
|
73
|
+
entities: list[str] = field(default_factory=list)
|
|
74
|
+
filters: list[str] = field(default_factory=list)
|
|
75
|
+
rationale: str = ""
|
|
76
|
+
|
|
77
|
+
def is_empty(self) -> bool:
|
|
78
|
+
"""True when linking found no usable table or column."""
|
|
79
|
+
return not self.tables and not self.columns
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
@dataclass
|
|
83
|
+
class StageMetadata:
|
|
84
|
+
"""Metadata for one stage: which model, how many tokens, how long."""
|
|
85
|
+
|
|
86
|
+
provider: str
|
|
87
|
+
model: str
|
|
88
|
+
usage: TokenUsage = field(default_factory=TokenUsage)
|
|
89
|
+
latency_seconds: float = 0.0
|
|
90
|
+
attempts: int = 1
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
@dataclass
|
|
94
|
+
class RunMetadata:
|
|
95
|
+
"""Metadata for a full run (both stages)."""
|
|
96
|
+
|
|
97
|
+
linking: StageMetadata
|
|
98
|
+
generation: StageMetadata
|
|
99
|
+
total_latency_seconds: float = 0.0
|
|
100
|
+
|
|
101
|
+
@property
|
|
102
|
+
def total_usage(self) -> TokenUsage:
|
|
103
|
+
"""Token usage of both stages combined."""
|
|
104
|
+
return self.linking.usage + self.generation.usage
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
@dataclass
|
|
108
|
+
class Text2SQLResult:
|
|
109
|
+
"""Final result of :meth:`text2sql.Text2SQL.run`.
|
|
110
|
+
|
|
111
|
+
Attributes:
|
|
112
|
+
sql: The generated SQL string. This is the deliverable.
|
|
113
|
+
explanation: Optional short explanation from Stage 2.
|
|
114
|
+
linked_schema: Stage 1 output (the reduced schema).
|
|
115
|
+
metadata: Models, tokens, and latency for the run.
|
|
116
|
+
request: The original request.
|
|
117
|
+
"""
|
|
118
|
+
|
|
119
|
+
sql: str
|
|
120
|
+
linked_schema: LinkedSchema
|
|
121
|
+
metadata: RunMetadata
|
|
122
|
+
request: str
|
|
123
|
+
explanation: str = ""
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
class _Timer:
|
|
127
|
+
"""Small monotonic timer for stage latency. Internal only."""
|
|
128
|
+
|
|
129
|
+
def __init__(self) -> None:
|
|
130
|
+
self._start = 0.0
|
|
131
|
+
self.elapsed = 0.0
|
|
132
|
+
|
|
133
|
+
def __enter__(self) -> "_Timer":
|
|
134
|
+
self._start = time.perf_counter()
|
|
135
|
+
return self
|
|
136
|
+
|
|
137
|
+
def __exit__(self, *exc: object) -> None:
|
|
138
|
+
self.elapsed = time.perf_counter() - self._start
|
|
@@ -0,0 +1,304 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: text2sql-engine
|
|
3
|
+
Version: 0.1.0
|
|
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
|
+
Author: stajyer14
|
|
6
|
+
License: MIT
|
|
7
|
+
Keywords: text-to-sql,nl2sql,llm,sql-generation,schema-linking
|
|
8
|
+
Classifier: Development Status :: 4 - Beta
|
|
9
|
+
Classifier: Intended Audience :: Developers
|
|
10
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
11
|
+
Classifier: Operating System :: OS Independent
|
|
12
|
+
Classifier: Programming Language :: Python :: 3
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
15
|
+
Classifier: Topic :: Database
|
|
16
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
17
|
+
Classifier: Topic :: Software Development :: Libraries
|
|
18
|
+
Classifier: Typing :: Typed
|
|
19
|
+
Requires-Python: >=3.11
|
|
20
|
+
Description-Content-Type: text/markdown
|
|
21
|
+
License-File: LICENSE
|
|
22
|
+
Requires-Dist: python-dotenv>=1.0
|
|
23
|
+
Requires-Dist: PyYAML>=6.0
|
|
24
|
+
Provides-Extra: openai
|
|
25
|
+
Requires-Dist: openai>=1.0; extra == "openai"
|
|
26
|
+
Provides-Extra: anthropic
|
|
27
|
+
Requires-Dist: anthropic>=0.34; extra == "anthropic"
|
|
28
|
+
Provides-Extra: all
|
|
29
|
+
Requires-Dist: openai>=1.0; extra == "all"
|
|
30
|
+
Requires-Dist: anthropic>=0.34; extra == "all"
|
|
31
|
+
Provides-Extra: dev
|
|
32
|
+
Requires-Dist: pytest>=7.4; extra == "dev"
|
|
33
|
+
Dynamic: license-file
|
|
34
|
+
|
|
35
|
+
# text2sql
|
|
36
|
+
|
|
37
|
+
A **schema-agnostic, config-driven Text-to-SQL library**. Give it a
|
|
38
|
+
natural-language request and metadata for any relational schema, and it returns
|
|
39
|
+
a SQL string.
|
|
40
|
+
|
|
41
|
+
It is a **library**, not an app. No UI, no web server, no CLI. It **never
|
|
42
|
+
connects to or runs against a database**. Generating the SQL is the last step.
|
|
43
|
+
Running it is the caller's job.
|
|
44
|
+
|
|
45
|
+
## How it works — a 2-stage LLM pipeline
|
|
46
|
+
|
|
47
|
+
```
|
|
48
|
+
user request
|
|
49
|
+
│
|
|
50
|
+
▼
|
|
51
|
+
┌─────────────────────────────┐ full schema metadata
|
|
52
|
+
│ Stage 1: Schema Linking LLM │◄──────────────────────
|
|
53
|
+
│ reduce schema → subset │
|
|
54
|
+
└─────────────────────────────┘
|
|
55
|
+
│ linked schema (tables, columns, joins, entities, filters, rationale)
|
|
56
|
+
▼
|
|
57
|
+
┌─────────────────────────────┐
|
|
58
|
+
│ Stage 2: SQL Generation LLM │
|
|
59
|
+
│ produce SQL in dialect │
|
|
60
|
+
└─────────────────────────────┘
|
|
61
|
+
│
|
|
62
|
+
▼
|
|
63
|
+
Text2SQLResult(sql, linked_schema, metadata, explanation)
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
- **Stage 1 (linking)** takes the request and the *full* schema metadata. It
|
|
67
|
+
reduces the schema to the relevant part and returns structured JSON. Output is
|
|
68
|
+
forced via `response_format` (OpenAI) or tool-calling (Anthropic), with
|
|
69
|
+
strict-JSON parsing and retry as a fallback.
|
|
70
|
+
- **Stage 2 (generation)** takes the request and that reduced subset. It writes
|
|
71
|
+
SQL in the target dialect, plus a short explanation.
|
|
72
|
+
- The two stages can use **different providers and models**. For example a cheap
|
|
73
|
+
general model for linking, a stronger SQL model for generation.
|
|
74
|
+
|
|
75
|
+
## Installation
|
|
76
|
+
|
|
77
|
+
```bash
|
|
78
|
+
pip install text2sql-engine # from PyPI (import name: text2sql)
|
|
79
|
+
pip install "text2sql-engine[all]" # + openai and anthropic SDKs
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
The PyPI name is `text2sql-engine`. The import name is `text2sql`
|
|
83
|
+
(`import text2sql`). Different names on purpose, since `text2sql` was taken.
|
|
84
|
+
|
|
85
|
+
From a checkout of this repo:
|
|
86
|
+
|
|
87
|
+
```bash
|
|
88
|
+
pip install -e . # core library
|
|
89
|
+
pip install -e ".[all]" # + openai and anthropic SDKs
|
|
90
|
+
pip install -e ".[dev]" # + pytest
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
The provider SDKs (`openai`, `anthropic`) are imported lazily. So the library
|
|
94
|
+
imports, and the tests run, without them installed.
|
|
95
|
+
|
|
96
|
+
## Quick start
|
|
97
|
+
|
|
98
|
+
```python
|
|
99
|
+
from text2sql import Text2SQL
|
|
100
|
+
|
|
101
|
+
engine = Text2SQL.from_config() # reads .env + config.toml
|
|
102
|
+
result = engine.run("get me the top 5 best-selling products last month")
|
|
103
|
+
|
|
104
|
+
print(result.sql) # the generated SQL string
|
|
105
|
+
print(result.linked_schema) # Stage 1 output (reduced schema)
|
|
106
|
+
print(result.metadata) # models, tokens, latency, attempts
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
1. Copy `.env.example` to `.env`. Fill in provider selection and API keys.
|
|
110
|
+
2. Adjust `config.toml` for behavior (dialect, sampling, retries).
|
|
111
|
+
3. Point `SCHEMA_PATH` at your metadata file. See `examples/schema.yaml`.
|
|
112
|
+
|
|
113
|
+
## Configuration — two separate layers
|
|
114
|
+
|
|
115
|
+
Config is split by concern. **Secrets, paths, and provider/model *selection* go
|
|
116
|
+
in `.env`. Everything tunable about *behavior* goes in `config.toml`.** One typed
|
|
117
|
+
layer (`text2sql.config.Settings`) loads both. It fails fast with a clear error
|
|
118
|
+
if a required `.env` variable is missing.
|
|
119
|
+
|
|
120
|
+
### `.env` — secrets, paths, provider & model selection
|
|
121
|
+
|
|
122
|
+
| Variable | Required | Description |
|
|
123
|
+
|----------|----------|-------------|
|
|
124
|
+
| `SCHEMA_PATH` | yes | Path to the schema file (`.json`, `.yaml`, `.yml`). |
|
|
125
|
+
| `PROMPT_DIR` | no | Directory with prompt templates (`linking.txt`, `generation.txt`). If unset, the templates **packaged inside the library** are used. So it works after pip install, from any working directory. |
|
|
126
|
+
| `CONFIG_PATH` | no | Override the `config.toml` location. Defaults to `./config.toml`. |
|
|
127
|
+
| `LINKING_PROVIDER` | yes | Provider for Stage 1. One of the registered names: `openai`, `anthropic`. |
|
|
128
|
+
| `LINKING_MODEL` | yes | Model id for Stage 1 (schema linking). |
|
|
129
|
+
| `LINKING_BASE_URL` | no | Endpoint override for Stage 1 (for OpenAI-compatible gateways). |
|
|
130
|
+
| `SQL_PROVIDER` | yes | Provider for Stage 2. |
|
|
131
|
+
| `SQL_MODEL` | yes | Model id for Stage 2 (SQL generation). |
|
|
132
|
+
| `SQL_BASE_URL` | no | Endpoint override for Stage 2. |
|
|
133
|
+
| `OPENAI_API_KEY` | if used | API key. Needed only if a stage uses provider `openai`. |
|
|
134
|
+
| `ANTHROPIC_API_KEY` | if used | API key. Needed only if a stage uses provider `anthropic`. |
|
|
135
|
+
|
|
136
|
+
Provider, model, and `base_url` are picked **per stage**, independently.
|
|
137
|
+
|
|
138
|
+
### `config.toml` — behavior
|
|
139
|
+
|
|
140
|
+
No magic numbers in code. Every tunable is here.
|
|
141
|
+
|
|
142
|
+
#### `[general]`
|
|
143
|
+
|
|
144
|
+
| Key | Type | Default | Description |
|
|
145
|
+
|-----|------|---------|-------------|
|
|
146
|
+
| `sql_dialect` | string | `postgres` | Target dialect (e.g. `postgres`, `mysql`, `sqlite`). Passed to the prompts. |
|
|
147
|
+
| `log_level` | string | `INFO` | `DEBUG` / `INFO` / `WARNING` / `ERROR`. |
|
|
148
|
+
| `strict_json` | bool | `true` | `true`: parse LLM JSON strictly. `false`: tolerate fences / extra prose. |
|
|
149
|
+
|
|
150
|
+
#### `[linking]` — Stage 1
|
|
151
|
+
|
|
152
|
+
| Key | Type | Default | Description |
|
|
153
|
+
|-----|------|---------|-------------|
|
|
154
|
+
| `temperature` | float | `0.0` | Sampling temperature. |
|
|
155
|
+
| `top_p` | float | `1.0` | Nucleus-sampling cutoff. |
|
|
156
|
+
| `max_tokens` | int | `1024` | Max tokens for the linking response. |
|
|
157
|
+
| `prompt_template` | string | `linking.txt` | Template file, looked up in `PROMPT_DIR`. |
|
|
158
|
+
| `max_tables` | int | `8` | Max tables linking may return. |
|
|
159
|
+
| `timeout_seconds` | float | `60` | Per-request timeout. |
|
|
160
|
+
|
|
161
|
+
#### `[generation]` — Stage 2
|
|
162
|
+
|
|
163
|
+
| Key | Type | Default | Description |
|
|
164
|
+
|-----|------|---------|-------------|
|
|
165
|
+
| `temperature` | float | `0.0` | Sampling temperature. |
|
|
166
|
+
| `top_p` | float | `1.0` | Nucleus-sampling cutoff. |
|
|
167
|
+
| `max_tokens` | int | `1024` | Max tokens for the SQL response. |
|
|
168
|
+
| `prompt_template` | string | `generation.txt` | Template file, looked up in `PROMPT_DIR`. |
|
|
169
|
+
| `timeout_seconds` | float | `60` | Per-request timeout. |
|
|
170
|
+
|
|
171
|
+
#### `[retry]` — shared by both stages
|
|
172
|
+
|
|
173
|
+
| Key | Type | Default | Description |
|
|
174
|
+
|-----|------|---------|-------------|
|
|
175
|
+
| `max_attempts` | int | `3` | Attempts per LLM call before failing. Retries on JSON-parse and transient provider errors. |
|
|
176
|
+
| `backoff_seconds` | float | `1.0` | First delay between retries. |
|
|
177
|
+
| `backoff_factor` | float | `2.0` | Delay is multiplied by this each retry. |
|
|
178
|
+
|
|
179
|
+
## Schema metadata format
|
|
180
|
+
|
|
181
|
+
The schema is **never hardcoded**. It is loaded from an external file through a
|
|
182
|
+
`SchemaProvider`. `examples/schema.yaml` (and the same `examples/schema.json`)
|
|
183
|
+
show the full format: a small e-commerce database with `customers`,
|
|
184
|
+
`categories`, `products`, `orders`, and `order_items`. Each table has a
|
|
185
|
+
description. Each column has a name, type, description, primary-key flag, and
|
|
186
|
+
sample values. Foreign keys are listed. Both JSON and YAML work, picked by
|
|
187
|
+
extension.
|
|
188
|
+
|
|
189
|
+
The example runs out of the box. The tests exercise the pipeline against it with
|
|
190
|
+
mocked LLM calls.
|
|
191
|
+
|
|
192
|
+
## Extending
|
|
193
|
+
|
|
194
|
+
Everything is swappable via **config + dependency injection**:
|
|
195
|
+
|
|
196
|
+
```python
|
|
197
|
+
from text2sql import Text2SQL, SchemaProvider, PromptTemplates
|
|
198
|
+
from text2sql.schema import Schema
|
|
199
|
+
|
|
200
|
+
# 1. Custom schema source, e.g. live DB introspection instead of a file.
|
|
201
|
+
class MyIntrospectionProvider(SchemaProvider):
|
|
202
|
+
def load(self) -> Schema:
|
|
203
|
+
... # build and return a Schema
|
|
204
|
+
|
|
205
|
+
# 2. Inject any part. Anything left None is built from config.
|
|
206
|
+
engine = Text2SQL.from_config(
|
|
207
|
+
schema_provider=MyIntrospectionProvider(),
|
|
208
|
+
prompts=PromptTemplates("/path/to/my/templates"),
|
|
209
|
+
# linking_provider=..., generation_provider=...,
|
|
210
|
+
)
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
### Adding a new LLM provider
|
|
214
|
+
|
|
215
|
+
Subclass `LLMProvider`, implement `complete` (and optionally `complete_json` for
|
|
216
|
+
native structured output), and register it. One class, one decorator:
|
|
217
|
+
|
|
218
|
+
```python
|
|
219
|
+
from text2sql import LLMProvider, register_provider
|
|
220
|
+
from text2sql.types import LLMResponse
|
|
221
|
+
|
|
222
|
+
@register_provider("myprovider")
|
|
223
|
+
class MyProvider(LLMProvider):
|
|
224
|
+
def complete(self, *, system, user, temperature, top_p, max_tokens) -> LLMResponse:
|
|
225
|
+
...
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
Then set `LINKING_PROVIDER=myprovider` (or `SQL_PROVIDER`) in `.env`.
|
|
229
|
+
|
|
230
|
+
### Prompts
|
|
231
|
+
|
|
232
|
+
Prompt templates are plain, editable text files in `PROMPT_DIR`. Each has a
|
|
233
|
+
`SYSTEM:` and a `USER:` section with `{placeholder}` substitution. Edit them
|
|
234
|
+
without touching code.
|
|
235
|
+
|
|
236
|
+
## Result object
|
|
237
|
+
|
|
238
|
+
`engine.run(...)` returns a `Text2SQLResult`:
|
|
239
|
+
|
|
240
|
+
| Field | Description |
|
|
241
|
+
|-------|-------------|
|
|
242
|
+
| `sql` | The generated SQL string. The deliverable. |
|
|
243
|
+
| `explanation` | Short explanation from Stage 2. |
|
|
244
|
+
| `linked_schema` | Stage 1 output: `tables`, `columns`, `joins`, `entities`, `filters`, `rationale`. |
|
|
245
|
+
| `metadata` | `RunMetadata`: per-stage provider/model, token usage, latency, attempts. Plus `total_usage` and `total_latency_seconds`. |
|
|
246
|
+
| `request` | The original request. |
|
|
247
|
+
|
|
248
|
+
## Error handling
|
|
249
|
+
|
|
250
|
+
Every error subclasses `Text2SQLError`:
|
|
251
|
+
|
|
252
|
+
- `ConfigError` — missing/invalid `.env` var or `config.toml`.
|
|
253
|
+
- `SchemaError` — schema file missing or invalid.
|
|
254
|
+
- `ProviderError` — provider build or API call failed.
|
|
255
|
+
- `StructuredOutputError` — output not parseable as JSON after all retries.
|
|
256
|
+
- `EmptyLinkingError` — Stage 1 matched no table/column.
|
|
257
|
+
|
|
258
|
+
Uses stdlib `logging` throughout, never `print`. Secrets are never logged.
|
|
259
|
+
|
|
260
|
+
## Try it end to end
|
|
261
|
+
|
|
262
|
+
With a real provider and key set in `.env`:
|
|
263
|
+
|
|
264
|
+
```bash
|
|
265
|
+
python examples/run_example.py "top 5 best-selling products last month"
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
Makes real LLM calls for both stages. Prints the linked schema, the SQL, and the
|
|
269
|
+
metadata. Never connects to a database.
|
|
270
|
+
|
|
271
|
+
## Development
|
|
272
|
+
|
|
273
|
+
```bash
|
|
274
|
+
pip install -e ".[dev]"
|
|
275
|
+
pytest
|
|
276
|
+
```
|
|
277
|
+
|
|
278
|
+
Tests mock all LLM calls (no network) and run on `examples/schema.yaml`.
|
|
279
|
+
|
|
280
|
+
## Project layout
|
|
281
|
+
|
|
282
|
+
```
|
|
283
|
+
text2sql/
|
|
284
|
+
config.py .env + config.toml loading, typed settings
|
|
285
|
+
types.py result dataclasses
|
|
286
|
+
errors.py exceptions
|
|
287
|
+
logging_utils.py logging setup
|
|
288
|
+
schema/ SchemaProvider, file loader, models, serializer
|
|
289
|
+
providers/ base + registry + openai + anthropic
|
|
290
|
+
prompts/ editable linking.txt / generation.txt templates
|
|
291
|
+
pipeline/ linking (Stage 1), generation (Stage 2), orchestrator
|
|
292
|
+
examples/
|
|
293
|
+
schema.yaml / schema.json sample metadata "spec"
|
|
294
|
+
run_example.py runnable end-to-end example (real LLM call)
|
|
295
|
+
config.toml behavioral parameters
|
|
296
|
+
.env.example secrets / paths / model selection template
|
|
297
|
+
tests/ pipeline, schema, config, and prompt tests
|
|
298
|
+
.github/workflows/ci.yml GitHub Actions: pytest on 3.11 and 3.12
|
|
299
|
+
LICENSE MIT
|
|
300
|
+
```
|
|
301
|
+
|
|
302
|
+
## License
|
|
303
|
+
|
|
304
|
+
MIT. See [LICENSE](LICENSE).
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
text2sql/__init__.py,sha256=EUP3cVW8vLecps3wqmPqqr6Apaht2tD9QsfdjP5-CQM,1442
|
|
2
|
+
text2sql/config.py,sha256=jCpdUogpzRmkL5kN6CPqYXhUNhRA7EdaCFlCY65QnLA,7709
|
|
3
|
+
text2sql/errors.py,sha256=Y5ORy7g_UsFvDJ2KorB2JFvTyOBqmrPv9MWc8Cl9MI8,1052
|
|
4
|
+
text2sql/logging_utils.py,sha256=hiEH1SDqP16kctUj4uPP0tbXdrCO9AlRcdS-mHYDg5s,1633
|
|
5
|
+
text2sql/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
6
|
+
text2sql/types.py,sha256=MxcdHP7Gnftk1yKdcBQKaVVyeqRnBu5kxbhSZ2xJJ80,3532
|
|
7
|
+
text2sql/pipeline/__init__.py,sha256=rjEOi4U9C-SKP15cyKPdvG_EZBOW0VW01LouS3PJVdY,245
|
|
8
|
+
text2sql/pipeline/generation.py,sha256=LpnsKmSgu4Gfg-nHCCk6akg7ToI-G9Vk0LaVRn9WK5Y,3635
|
|
9
|
+
text2sql/pipeline/json_utils.py,sha256=92viA3xyw_GMF_nKdjSPgAyQ8iBUZgnyNRAoyWC5ZlE,3145
|
|
10
|
+
text2sql/pipeline/linking.py,sha256=U28LOcWJn3cItwAsv2fG4fYv9KcJ80XmP1Fi8nNx4qc,6966
|
|
11
|
+
text2sql/pipeline/pipeline.py,sha256=TE0cHSEJyeY43P89HsQDbnPGSfgRQmtw7KZAq-Yjj1U,5675
|
|
12
|
+
text2sql/prompts/__init__.py,sha256=HfeflfAGsZl3xHnNrmklTP6K_8EKxX6vWJrayq6QuEQ,3315
|
|
13
|
+
text2sql/prompts/generation.txt,sha256=r4125WCr7gsJIzTa5wZTAmbTyn5WvP3QrfGf4CWNP_w,849
|
|
14
|
+
text2sql/prompts/linking.txt,sha256=nLPSkaq3PiWiJ7wXLeD0BvK_-rR3hhc_tLhZ-daPnGY,889
|
|
15
|
+
text2sql/providers/__init__.py,sha256=R5a-YKtyaUFpkVJGWrmhDGyRHeh84gA3F7nEH57dSGo,345
|
|
16
|
+
text2sql/providers/anthropic_provider.py,sha256=Zhh8zM6EfHqkjTgNmhYKdZLHQ69b0Pu2-5WbCW2rD6s,4342
|
|
17
|
+
text2sql/providers/base.py,sha256=lqy_-Z923EfuKvOY247lyifdE5HjN0o52tyi9M2Ufns,3655
|
|
18
|
+
text2sql/providers/openai_provider.py,sha256=EoIkCB-RnFFHzyBMAY2eglzu7WLrVF5i9q8MhcqEyG0,4370
|
|
19
|
+
text2sql/providers/registry.py,sha256=epe7OLm07_jhsspItpauupTy3UEyuUheZ20VYQcq1Ec,1147
|
|
20
|
+
text2sql/schema/__init__.py,sha256=AvXHTwCDiOolg-_btcLReFDWupztDswEsFkz_1EGOmo,395
|
|
21
|
+
text2sql/schema/file_provider.py,sha256=Nv9x2-Eh9mXISk5XvUQaLfagth2s3LbX5F7jWp-oZqw,4918
|
|
22
|
+
text2sql/schema/models.py,sha256=yX5zdKbTuy-g-nkwT0v-0HUTK81jBRoabV-OC-kD-JI,2263
|
|
23
|
+
text2sql/schema/provider.py,sha256=JPMRsjRgEB4RdQR_pFwbP2LwLIXZhQVWSQPoYzaXC7s,798
|
|
24
|
+
text2sql/schema/serialize.py,sha256=_2MeA1IwDJ51-69PXyqXBSFZSyzovbqJkRIMpekkfJA,1833
|
|
25
|
+
text2sql_engine-0.1.0.dist-info/licenses/LICENSE,sha256=dzrVwSmgUgCDYwDn21Job7Yb4huyHjC74evyzsHXas8,1078
|
|
26
|
+
text2sql_engine-0.1.0.dist-info/METADATA,sha256=Cni4NycxDnoS3HgP5eYTRiUXgOmnLhSYBI27LTBm08U,12302
|
|
27
|
+
text2sql_engine-0.1.0.dist-info/WHEEL,sha256=K260EYznzXsJYBQGqmI8VTxEdiZYNvDZwW9cBh9-_MA,91
|
|
28
|
+
text2sql_engine-0.1.0.dist-info/top_level.txt,sha256=ZONWO1r8wsWpyHhF5qPzAjnsNoCEhwZ2Z-M_V8nKALc,9
|
|
29
|
+
text2sql_engine-0.1.0.dist-info/RECORD,,
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 text2sql contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
text2sql
|