Skip to content

Commit 054acad

Browse files
Merge pull request #10 from LLMSQL/6-add-dependency-manager
pdm added; mypy and ruff added;
2 parents 6acd576 + c43f051 commit 054acad

12 files changed

Lines changed: 5110 additions & 86 deletions

File tree

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,4 @@ dataset/sqlite_tables.db
55
dist/
66

77
*.egg-info/
8+
.pdm-python

llmsql/__init__.py

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,7 @@
1-
import logging
2-
import os
3-
41
__version__ = "0.1.2"
52

63

7-
def __getattr__(name: str):
4+
def __getattr__(name: str): # type: ignore
85
if name == "LLMSQLVLLMInference":
96
from .inference.inference import LLMSQLVLLMInference
107

llmsql/__main__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
import sys
33

44

5-
def main():
5+
def main() -> None:
66
parser = argparse.ArgumentParser(prog="llmsql", description="LLMSQL CLI")
77
subparsers = parser.add_subparsers(dest="command")
88

llmsql/evaluation/evaluator.py

Lines changed: 12 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,8 @@
2222

2323
import json
2424
import os
25-
import sqlite3
2625
from pathlib import Path
27-
from typing import Dict, List, Optional
26+
import sqlite3
2827

2928
from huggingface_hub import hf_hub_download
3029
from rich.progress import track
@@ -45,7 +44,7 @@ def __init__(self, workdir_path: str = "llmsql_workdir"):
4544
"""
4645
Initialize evaluator.
4746
"""
48-
self.conn = None
47+
self.conn: sqlite3.Connection | None = None
4948
self.workdir_path = Path(workdir_path)
5049
self.workdir_path.mkdir(parents=True, exist_ok=True)
5150
self.repo_id = "llmsql-bench/llmsql-benchmark"
@@ -67,30 +66,27 @@ def _download_file(self, filename: str) -> str:
6766
log.info(f"File saved at: {file_path}")
6867
return file_path
6968

70-
def connect(self, db_path: str):
69+
def connect(self, db_path: str) -> None:
7170
"""Establish SQLite connection."""
7271
if not os.path.exists(db_path):
7372
raise FileNotFoundError(f"Database not found at {db_path}")
7473
self.conn = sqlite3.connect(db_path)
7574

76-
def close(self):
75+
def close(self) -> None:
7776
"""Close SQLite connection if open."""
7877
if self.conn:
7978
self.conn.close()
8079
self.conn = None
8180

82-
# ------------------------------------------------------------------
83-
# Evaluation
84-
# ------------------------------------------------------------------
8581
def evaluate(
8682
self,
8783
outputs_path: str,
88-
questions_path: Optional[str] = None,
89-
db_path: Optional[str] = None,
90-
save_report: Optional[str] = None,
84+
questions_path: str | None = None,
85+
db_path: str | None = None,
86+
save_report: str | None = None,
9187
show_mismatches: bool = True,
9288
max_mismatches: int = 5,
93-
) -> Dict:
89+
) -> dict:
9490
"""
9591
Evaluate predicted SQL queries against benchmark ground truth.
9692
@@ -145,12 +141,14 @@ def evaluate(
145141
"gold_none": 0,
146142
"sql_errors": 0,
147143
}
148-
mismatches: List[Dict] = []
144+
mismatches: list[dict] = []
149145

150146
for item in track(outputs, description="Evaluating"):
151147
metrics["total"] += 1
152148
is_match, mismatch_info, m_update = evaluate_sample(
153-
item, questions, self.conn
149+
item,
150+
questions,
151+
self.conn, # type: ignore
154152
)
155153

156154
metrics["matches"] += is_match

llmsql/finetune/finetune.py

Lines changed: 18 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -20,23 +20,24 @@
2020
"""
2121

2222
import argparse
23+
from collections.abc import Callable
2324
import os
24-
import shutil
2525
from pathlib import Path
26-
from typing import Dict, Optional
26+
import shutil
27+
from typing import Any
2728

28-
import torch
29-
import yaml
30-
from datasets import Dataset
29+
from datasets import Dataset # type: ignore
3130
from huggingface_hub import hf_hub_download
31+
import torch
3232
from transformers import AutoModelForCausalLM
3333
from trl import SFTConfig, SFTTrainer
34+
import yaml
3435

3536
from llmsql.loggers.logging_config import log
3637
from llmsql.utils.utils import choose_prompt_builder, load_jsonl
3738

3839

39-
def parse_args_and_config():
40+
def parse_args_and_config() -> argparse.Namespace:
4041
"""Parse CLI args and optionally merge with YAML config."""
4142
p = argparse.ArgumentParser(
4243
description="Fine-tune a causal LM on Text-to-SQL benchmark."
@@ -66,10 +67,10 @@ def parse_args_and_config():
6667
# Load YAML config
6768
config = {}
6869
if args.get("config_file"):
69-
with open(args["config_file"], "r") as f:
70+
with open(args["config_file"]) as f:
7071
config = yaml.safe_load(f)
7172

72-
def flatten(d, parent_key="", sep="_"):
73+
def flatten(d: Any, parent_key: str = "", sep: str = "_") -> dict[str, Any]:
7374
items = {}
7475
for k, v in d.items():
7576
new_key = f"{parent_key}{sep}{k}" if parent_key else k
@@ -88,7 +89,7 @@ def flatten(d, parent_key="", sep="_"):
8889
return argparse.Namespace(**args)
8990

9091

91-
def build_dataset(file_path: str, tables: Dict, prompt_builder) -> Dataset:
92+
def build_dataset(file_path: str, tables: dict, prompt_builder: Callable) -> Dataset:
9293
"""Convert JSONL file to HF dataset samples."""
9394
questions = load_jsonl(file_path)
9495
samples = []
@@ -115,15 +116,13 @@ def _download_file(filename: str, repo_id: str, workdir_path: str) -> str:
115116
shutil.copy(cached_path, local_path)
116117
return local_path
117118

118-
return cached_path
119-
120119

121120
def main(
122121
model_name_or_path: str,
123122
output_dir: str,
124-
train_file: Optional[str] = None,
125-
val_file: Optional[str] = None,
126-
tables_file: Optional[str] = None,
123+
train_file: str | None = None,
124+
val_file: str | None = None,
125+
tables_file: str | None = None,
127126
shots: int = 5,
128127
num_train_epochs: int = 3,
129128
per_device_train_batch_size: int = 4,
@@ -136,11 +135,11 @@ def main(
136135
max_length: int = 32768,
137136
no_eval: bool = False,
138137
eval_steps: int = 100,
139-
wandb_project: Optional[str] = None,
140-
wandb_run_name: Optional[str] = None,
141-
wandb_key: Optional[str] = None,
138+
wandb_project: str | None = None,
139+
wandb_run_name: str | None = None,
140+
wandb_key: str | None = None,
142141
wandb_offline: bool = False,
143-
):
142+
) -> None:
144143
os.makedirs(output_dir, exist_ok=True)
145144

146145
# Seed
@@ -268,7 +267,7 @@ def main(
268267
log.info(f"Model saved at {output_dir}/final_model")
269268

270269

271-
def run_cli():
270+
def run_cli() -> None:
272271
args = parse_args_and_config()
273272
main(
274273
train_file=args.train_file,

llmsql/inference/inference.py

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -33,14 +33,14 @@
3333
"""
3434

3535
import os
36-
import random
3736
from pathlib import Path
38-
from typing import Dict, List, Optional
37+
import random
38+
from typing import Any
3939

40-
import numpy as np
41-
import torch
4240
from dotenv import load_dotenv
4341
from huggingface_hub import hf_hub_download
42+
import numpy as np
43+
import torch
4444
from tqdm import tqdm
4545
from vllm import LLM, SamplingParams
4646

@@ -54,8 +54,8 @@
5454

5555
load_dotenv()
5656

57-
Question = Dict[str, object]
58-
Table = Dict[str, object]
57+
Question = dict[str, object]
58+
Table = dict[str, object]
5959

6060

6161
class LLMSQLVLLMInference:
@@ -70,7 +70,7 @@ def __init__(
7070
tensor_parallel_size: int = 1,
7171
seed: int = 42,
7272
workdir_path: str = "llmsql_workdir",
73-
**llm_kwargs,
73+
**llm_kwargs: Any,
7474
):
7575
"""
7676
Initialize vLLM model for SQL inference.
@@ -127,15 +127,15 @@ def _download_file(self, filename: str) -> str:
127127
def generate(
128128
self,
129129
output_file: str,
130-
questions_path: Optional[str] = None,
131-
tables_path: Optional[str] = None,
130+
questions_path: str | None = None,
131+
tables_path: str | None = None,
132132
shots: int = 5,
133133
batch_size: int = 8,
134134
max_new_tokens: int = 256,
135135
temperature: float = 1.0,
136136
do_sample: bool = True,
137-
**sampling_kwargs,
138-
) -> List[Dict[str, str]]:
137+
**sampling_kwargs: Any,
138+
) -> list[dict[str, str]]:
139139
"""
140140
Generate SQL queries for all benchmark questions.
141141
@@ -199,7 +199,7 @@ def generate(
199199
prompt_builder = choose_prompt_builder(shots)
200200
log.info(f"Using {shots}-shot prompt builder: {prompt_builder.__name__}")
201201

202-
all_results: List[Dict[str, str]] = []
202+
all_results: list[dict[str, str]] = []
203203
total = len(questions)
204204

205205
temperature = 0.0 if not do_sample else temperature
@@ -225,8 +225,8 @@ def generate(
225225
# Generate with vLLM
226226
outputs = self.llm.generate(prompts, sampling_params)
227227

228-
batch_results: List[Dict[str, str]] = []
229-
for q, out in zip(batch, outputs):
228+
batch_results: list[dict[str, str]] = []
229+
for q, out in zip(batch, outputs, strict=False):
230230
text = out.outputs[0].text
231231
batch_results.append(
232232
{

llmsql/prompts/prompts.py

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,9 @@
1-
def build_prompt_5shot(question, headers, types, sample_row) -> str:
1+
def build_prompt_5shot(
2+
question: str,
3+
headers: list[str],
4+
types: list[str],
5+
sample_row: list[str | float | int],
6+
) -> str:
27
return f"""You are an expert SQLite SQL query generator.
38
Your task: Given a question and a table schema, output ONLY a valid SQL SELECT query.
49
⚠️ STRICT RULES:
@@ -52,7 +57,12 @@ def build_prompt_5shot(question, headers, types, sample_row) -> str:
5257
SQL:"""
5358

5459

55-
def build_prompt_1shot(question, headers, types, sample_row) -> str:
60+
def build_prompt_1shot(
61+
question: str,
62+
headers: list[str],
63+
types: list[str],
64+
sample_row: list[str | float | int],
65+
) -> str:
5666
return f"""You are an expert SQLite SQL query generator.
5767
Your task: Given a question and a table schema, output ONLY a valid SQL SELECT query.
5868
⚠️ STRICT RULES:
@@ -78,7 +88,12 @@ def build_prompt_1shot(question, headers, types, sample_row) -> str:
7888
SQL:"""
7989

8090

81-
def build_prompt_0shot(question, headers, types, sample_row) -> str:
91+
def build_prompt_0shot(
92+
question: str,
93+
headers: list[str],
94+
types: list[str],
95+
sample_row: list[str | float | int],
96+
) -> str:
8297
return f"""You are an expert SQLite SQL query generator.
8398
Your task: Given a question and a table schema, output ONLY a valid SQL SELECT query.
8499
⚠️ STRICT RULES:

llmsql/utils/evaluation_utils.py

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
import sqlite3
2-
from typing import List, Optional, Tuple
2+
from typing import Any
33

44
from llmsql.loggers.logging_config import log
55
from llmsql.utils.regex_extractor import find_sql
66

77

8-
def execute_sql(conn: sqlite3.Connection, sql: str) -> Optional[List[Tuple]]:
8+
def execute_sql(conn: sqlite3.Connection, sql: str) -> list[tuple] | None:
99
"""
1010
Execute a SQL query on the given SQLite connection and return its results.
1111
@@ -59,7 +59,11 @@ def fix_table_name(sql: str, table_id: str) -> str:
5959
)
6060

6161

62-
def evaluate_sample(item, questions, conn):
62+
def evaluate_sample(
63+
item: dict[str, int | str],
64+
questions: dict[int, dict[str, str]],
65+
conn: sqlite3.Connection,
66+
) -> tuple[int, dict[str, Any] | None, dict[Any, Any]]:
6367
"""
6468
Evaluate a single model prediction against the gold (ground-truth) SQL query.
6569
@@ -93,6 +97,9 @@ def evaluate_sample(item, questions, conn):
9397
"""
9498
# Extract question metadata
9599
qid = item["question_id"]
100+
assert isinstance(qid, int), (
101+
"question_id in the outputs file needs to be of type int."
102+
)
96103
q_info = questions[qid]
97104
table_id, gold_sql, question_text = (
98105
q_info["table_id"],
@@ -115,6 +122,9 @@ def evaluate_sample(item, questions, conn):
115122
last_pred_res = None # store last prediction results for mismatch logging
116123

117124
# Loop over all SQL queries extracted from the model output
125+
assert isinstance(item["completion"], str), (
126+
f"Completion filed in outputs file must be of type string: {item['completion']}. Type: {type(item['completion'])}"
127+
)
118128
for pred_sql in find_sql(item["completion"]):
119129
# Replace placeholder table names with the actual one
120130
pred_sql_fixed = fix_table_name(pred_sql, table_id)

llmsql/utils/regex_extractor.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
11
import re
2-
from typing import List
32

43

5-
def find_sql(model_output: str, limit: int = 10) -> List[str]:
4+
def find_sql(model_output: str, limit: int = 10) -> list[str]:
65
"""Function to extract SQL queries from the model's response
76
87
Args:

0 commit comments

Comments
 (0)