omkar1804 commited on
Commit
249242b
·
verified ·
1 Parent(s): 06f002d

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +72 -57
app.py CHANGED
@@ -1,30 +1,45 @@
 
1
  from __future__ import annotations
2
 
3
  import re
4
 
 
5
  import spaces
6
  import gradio as gr
7
  import torch
8
- from transformers import AutoModelForCausalLM, AutoTokenizer
9
 
10
 
11
  MODEL_ID = "prem-research/prem-1B-SQL"
12
  MODEL_REVISION = "44dd7fcf9227af4efed936bf29323c61bf66aad1"
13
 
14
  MAX_INPUT_TOKENS = 3072
15
- DEFAULT_MAX_NEW_TOKENS = 256
16
- MAX_NEW_TOKENS = 512
 
17
 
18
- tokenizer = AutoTokenizer.from_pretrained(
19
  MODEL_ID,
20
  revision=MODEL_REVISION,
21
- use_fast=True,
22
  )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
23
 
24
- if tokenizer.pad_token_id is None:
25
- tokenizer.pad_token = tokenizer.eos_token
26
-
27
- # ZeroGPU requires the GPU model to be placed on CUDA at module load time.
28
  model = (
29
  AutoModelForCausalLM.from_pretrained(
30
  MODEL_ID,
@@ -36,28 +51,29 @@ model = (
36
  .eval()
37
  )
38
 
39
- SQL_START = re.compile(r"\b(?:WITH|SELECT)\b", re.IGNORECASE)
40
- CODE_BLOCK = re.compile(
41
- r"```(?:sql)?\s*(.*?)```",
42
- re.IGNORECASE | re.DOTALL,
43
- )
44
 
45
- EXAMPLE_PROMPT = """Write exactly one read-only SQLite query.
46
- Use only the tables and columns in the schema.
47
- Return SQL only, without Markdown or explanation.
48
 
49
- Database schema:
 
 
50
 
51
- CREATE TABLE customers (
52
- id INTEGER PRIMARY KEY,
53
- name TEXT NOT NULL,
54
- city TEXT
55
- );
56
 
57
- Question: How many customers are there?
 
58
 
59
- SQL:
60
- """
 
 
 
61
 
62
 
63
  def extract_sql(generated: str) -> str:
@@ -67,65 +83,67 @@ def extract_sql(generated: str) -> str:
67
  if fenced:
68
  text = fenced.group(1).strip()
69
 
 
 
 
 
70
  start = SQL_START.search(text)
71
- if start:
72
- text = text[start.start():]
73
 
74
- return text.removesuffix("```").strip()
 
 
 
 
 
 
 
75
 
76
 
77
  @spaces.GPU(duration=45)
78
  def generate_sql(prompt: str, max_new_tokens: int) -> str:
79
- prompt = (prompt or "").strip()
80
-
81
- if not prompt:
82
  raise gr.Error("Prompt cannot be empty.")
83
 
84
  token_limit = max(
85
  32,
86
  min(MAX_NEW_TOKENS, int(max_new_tokens or DEFAULT_MAX_NEW_TOKENS)),
87
  )
88
-
89
  inputs = tokenizer(
90
- prompt,
91
  return_tensors="pt",
92
  add_special_tokens=True,
93
  truncation=False,
94
  )
95
-
96
  input_length = int(inputs["input_ids"].shape[-1])
97
  if input_length > MAX_INPUT_TOKENS:
98
  raise gr.Error(
99
- f"Prompt has {input_length:,} tokens; "
100
- f"the maximum is {MAX_INPUT_TOKENS:,}."
101
  )
102
 
103
- inputs = {
104
- name: tensor.to("cuda")
105
- for name, tensor in inputs.items()
106
- }
107
-
108
  with torch.inference_mode():
109
  output = model.generate(
110
  **inputs,
111
  max_new_tokens=token_limit,
112
  do_sample=False,
 
113
  eos_token_id=tokenizer.eos_token_id,
114
  pad_token_id=tokenizer.pad_token_id,
 
 
115
  use_cache=True,
116
  )
117
 
118
- new_tokens = output[0, input_length:]
119
  generated = tokenizer.decode(
120
- new_tokens,
121
  skip_special_tokens=True,
 
122
  )
123
-
124
- sql = extract_sql(generated)
125
- if not sql:
126
- raise gr.Error("The model returned an empty response.")
127
-
128
- return sql
129
 
130
 
131
  with gr.Blocks(title="Prem 1B SQL API") as demo:
@@ -133,17 +151,15 @@ with gr.Blocks(title="Prem 1B SQL API") as demo:
133
  """
134
  # Prem 1B SQL
135
 
136
- Generate SQL from a schema-aware natural-language prompt.
137
- The calling application must validate all generated SQL.
138
  """
139
  )
140
-
141
  prompt_input = gr.Textbox(
142
  value=EXAMPLE_PROMPT,
143
  label="Schema-aware prompt",
144
- lines=18,
145
  )
146
-
147
  max_tokens_input = gr.Slider(
148
  minimum=32,
149
  maximum=MAX_NEW_TOKENS,
@@ -151,7 +167,6 @@ with gr.Blocks(title="Prem 1B SQL API") as demo:
151
  step=1,
152
  label="Maximum output tokens",
153
  )
154
-
155
  generate_button = gr.Button("Generate SQL", variant="primary")
156
  sql_output = gr.Code(label="Generated SQL", language="sql")
157
 
@@ -166,4 +181,4 @@ with gr.Blocks(title="Prem 1B SQL API") as demo:
166
  demo.queue(max_size=20, default_concurrency_limit=1)
167
 
168
  if __name__ == "__main__":
169
- demo.launch()
 
1
+ # ruff: noqa: I001
2
  from __future__ import annotations
3
 
4
  import re
5
 
6
+ # ZeroGPU patches torch when spaces is imported, so this import must remain first.
7
  import spaces
8
  import gradio as gr
9
  import torch
10
+ from transformers import AutoModelForCausalLM, PreTrainedTokenizerFast
11
 
12
 
13
  MODEL_ID = "prem-research/prem-1B-SQL"
14
  MODEL_REVISION = "44dd7fcf9227af4efed936bf29323c61bf66aad1"
15
 
16
  MAX_INPUT_TOKENS = 3072
17
+ DEFAULT_MAX_NEW_TOKENS = 128
18
+ MAX_NEW_TOKENS = 256
19
+ MAX_OUTPUT_CHARS = 20_000
20
 
21
+ tokenizer = PreTrainedTokenizerFast.from_pretrained(
22
  MODEL_ID,
23
  revision=MODEL_REVISION,
 
24
  )
25
+ if not tokenizer.is_fast:
26
+ raise RuntimeError("Prem-1B-SQL requires the fast byte-level tokenizer")
27
+ tokenizer.padding_side = "right"
28
+ tokenizer.pad_token = tokenizer.eos_token
29
+
30
+ # Transformers 5 can otherwise select the slow Llama tokenizer declared in
31
+ # this checkpoint and expose its internal space/newline pieces as literal text.
32
+ DECODE_PROBE = "SELECT count(*) FROM customers;\n"
33
+ decode_probe_ids = tokenizer.encode(DECODE_PROBE, add_special_tokens=False)
34
+ decoded_probe = tokenizer.decode(
35
+ decode_probe_ids,
36
+ skip_special_tokens=True,
37
+ clean_up_tokenization_spaces=False,
38
+ )
39
+ if decoded_probe != DECODE_PROBE:
40
+ raise RuntimeError("Prem-1B-SQL tokenizer failed its byte-level decode check")
41
 
42
+ # ZeroGPU requires the GPU-backed model to be placed on CUDA at module load.
 
 
 
43
  model = (
44
  AutoModelForCausalLM.from_pretrained(
45
  MODEL_ID,
 
51
  .eval()
52
  )
53
 
54
+ SQL_START = re.compile(r"^\s*(?:WITH|SELECT)\b", re.IGNORECASE | re.MULTILINE)
55
+ CODE_BLOCK = re.compile(r"```(?:sql)?\s*(.*?)```", re.IGNORECASE | re.DOTALL)
56
+ INVALID_DECODE_MARKERS = ("Ċ", "Ġ")
 
 
57
 
58
+ EXAMPLE_PROMPT = """# Follow these instructions:
59
+ You will be given table schemas for a database. Write one correct, read-only SQL query
60
+ that answers the question.
61
 
62
+ 1. Return SQL only, on one line, without Markdown or commentary.
63
+ 2. Use only tables and columns present in the schema; never invent identifiers.
64
+ 3. Never use INSERT, UPDATE, DELETE, DDL, or PRAGMA.
65
 
66
+ # SQL dialect: sqlite
67
+ # Maximum rows: 100
 
 
 
68
 
69
+ # Database and Table Schema:
70
+ TABLE customers (id INTEGER PK NOT NULL, name TEXT NOT NULL, city TEXT)
71
 
72
+ # Here are some Examples on how to generate SQL statements and use column names:
73
+
74
+ # Question: How many customers are there?
75
+
76
+ # SQL:"""
77
 
78
 
79
  def extract_sql(generated: str) -> str:
 
83
  if fenced:
84
  text = fenced.group(1).strip()
85
 
86
+ # Some checkpoints repeat the training delimiter before the answer.
87
+ if "# SQL:" in text:
88
+ text = text.rsplit("# SQL:", maxsplit=1)[-1].strip()
89
+
90
  start = SQL_START.search(text)
91
+ if not start:
92
+ raise gr.Error("The model did not return a SQL SELECT statement.")
93
 
94
+ sql = text[start.start() :].removesuffix("```").strip()
95
+ if not sql:
96
+ raise gr.Error("The model returned an empty response.")
97
+ if len(sql) > MAX_OUTPUT_CHARS:
98
+ raise gr.Error("The model response was unexpectedly large.")
99
+ if "\x00" in sql or any(marker in sql for marker in INVALID_DECODE_MARKERS):
100
+ raise gr.Error("The model returned malformed tokenizer output. Please retry.")
101
+ return sql
102
 
103
 
104
  @spaces.GPU(duration=45)
105
  def generate_sql(prompt: str, max_new_tokens: int) -> str:
106
+ normalized_prompt = (prompt or "").strip()
107
+ if not normalized_prompt:
 
108
  raise gr.Error("Prompt cannot be empty.")
109
 
110
  token_limit = max(
111
  32,
112
  min(MAX_NEW_TOKENS, int(max_new_tokens or DEFAULT_MAX_NEW_TOKENS)),
113
  )
 
114
  inputs = tokenizer(
115
+ normalized_prompt,
116
  return_tensors="pt",
117
  add_special_tokens=True,
118
  truncation=False,
119
  )
 
120
  input_length = int(inputs["input_ids"].shape[-1])
121
  if input_length > MAX_INPUT_TOKENS:
122
  raise gr.Error(
123
+ f"Prompt has {input_length:,} tokens; the maximum is {MAX_INPUT_TOKENS:,}."
 
124
  )
125
 
126
+ inputs = {name: tensor.to("cuda") for name, tensor in inputs.items()}
 
 
 
 
127
  with torch.inference_mode():
128
  output = model.generate(
129
  **inputs,
130
  max_new_tokens=token_limit,
131
  do_sample=False,
132
+ num_beams=1,
133
  eos_token_id=tokenizer.eos_token_id,
134
  pad_token_id=tokenizer.pad_token_id,
135
+ stop_strings=[";\n", "\n#"],
136
+ tokenizer=tokenizer,
137
  use_cache=True,
138
  )
139
 
140
+ generated_token_ids = output[0, input_length:].tolist()
141
  generated = tokenizer.decode(
142
+ generated_token_ids,
143
  skip_special_tokens=True,
144
+ clean_up_tokenization_spaces=False,
145
  )
146
+ return extract_sql(generated)
 
 
 
 
 
147
 
148
 
149
  with gr.Blocks(title="Prem 1B SQL API") as demo:
 
151
  """
152
  # Prem 1B SQL
153
 
154
+ Generate SQL from a schema-aware prompt. The calling application must
155
+ validate every generated statement before execution.
156
  """
157
  )
 
158
  prompt_input = gr.Textbox(
159
  value=EXAMPLE_PROMPT,
160
  label="Schema-aware prompt",
161
+ lines=20,
162
  )
 
163
  max_tokens_input = gr.Slider(
164
  minimum=32,
165
  maximum=MAX_NEW_TOKENS,
 
167
  step=1,
168
  label="Maximum output tokens",
169
  )
 
170
  generate_button = gr.Button("Generate SQL", variant="primary")
171
  sql_output = gr.Code(label="Generated SQL", language="sql")
172
 
 
181
  demo.queue(max_size=20, default_concurrency_limit=1)
182
 
183
  if __name__ == "__main__":
184
+ demo.launch()