Skip to content

Commit 085cc55

Browse files
author
John Doe
committed
Prompt generator, updated demo
API Token now can be set in localStorage
1 parent baac2b0 commit 085cc55

File tree

5 files changed

+176
-27
lines changed

5 files changed

+176
-27
lines changed

README.md

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,20 +3,21 @@
33
This is simple python application to generate SQL Schema + prompt to ask GPT-3 to generate SQL queries.
44
It also has a simple UI to show results in a table.
55

6+
**It can generate a prompt for you, if you don't know what to ask GPT-3.**
7+
68
![](https://github.com/Hormold/gpt-sql-box/blob/master/docs/demo.gif?raw=true)
79

810

911
## How it works:
10-
1. Getting SQL schemas from PostgreSQL
11-
2. Compile prompt from SQL Schema
12+
1. Getting SQL schemas from PostgreSQL and compile prompt from SQL Schema
1213
3. Wait for user input
1314
4. Generate SQL query from prompt + user input
1415
5. Show SQL query and ask user to confirm or edit if it is correct before executing it
1516
6. Execute SQL query and show results in a table
1617

1718
## Environment
1819
- DATABASE_URL: PostgreSQL database URL
19-
- OPENAI_TOKEN: OpenAI API token
20+
- OPENAI_TOKEN: OpenAI API token (Not nessessary, you can set it in the UI)
2021
- APP_PORT: Port to run the application (default: 5000)
2122

2223
## How to run
@@ -26,4 +27,4 @@ It also has a simple UI to show results in a table.
2627
4. Run the application using `python app.py` and open `http://localhost:5000` in your browser
2728

2829
## Contact
29-
If you have any questions, please contact me at [t.me/define](https://t.me/define)
30+
If you have any questions, please contact me at [@define](https://t.me/define) in Telegram.

app.py

Lines changed: 75 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,12 @@
1818
if not DATABASE_URL:
1919
print('Please set DATABASE_URL in .env file.')
2020
sys.exit(1)
21-
openai.api_key = os.getenv('OPENAI_TOKEN')
22-
if not os.getenv('OPENAI_TOKEN'):
23-
print('Please set OPENAI_TOKEN in .env file.')
24-
sys.exit(1)
21+
22+
if os.getenv('OPENAI_TOKEN'):
23+
openai.api_key = os.getenv('OPENAI_TOKEN')
24+
25+
if not openai.api_key:
26+
print('Please set OPENAI_TOKEN in .env file or set token in UI') # Not a critical error
2527

2628
# Generate SQL Schema from PostgreSQL
2729
schema = Schema()
@@ -31,10 +33,10 @@
3133
@app.get('/')
3234
def index():
3335
"""Show SQL Schema + prompt to ask GPT-3 to generate SQL queries"""
34-
# Get JSON data (not escaped)
35-
normalized_json_data = json.dumps(json_data);
36+
normalized_json_data = json.dumps(json_data)
3637
return render_template(
3738
'index.html',
39+
has_openai_key=bool(openai.api_key),
3840
sql_schema=sql_schema,
3941
json_data=normalized_json_data
4042
)
@@ -44,14 +46,25 @@ def generate():
4446
"""Generate SQL query from prompt + user input"""
4547
try:
4648
content = request.json
47-
print('Content:', content)
4849
user_input = content['query']
4950
query_temperture = content['temp']
5051
selected = content['selected']
5152
print('Selected tables:', selected)
5253
print('User input:', user_input)
5354
print('Query temperture:', query_temperture)
5455

56+
if not content['api_key'] and not openai.api_key:
57+
return {
58+
'success': False,
59+
'error': 'Please set OPENAI_TOKEN in .env file or set token in UI'
60+
}
61+
62+
if content['api_key'] and not openai.api_key:
63+
openai.api_key = content['api_key'] # Inject API key from UI
64+
print('API key was set from UI')
65+
else:
66+
print('API key was set from .env file')
67+
5568
# Update prompt
5669
regen_schema = schema.regen(selected)
5770
new_prompt = f'Given an input question, respond with syntactically correct PostgreSQL. Be creative but the SQL must be correct, not nessesary to use all tables. {regen_schema}'
@@ -90,7 +103,7 @@ def generate():
90103
print(err)
91104
return {
92105
'success': False,
93-
'sql_query': err
106+
'error': str(err)
94107
}
95108

96109
@app.post('/run')
@@ -123,13 +136,65 @@ def execute():
123136
print(err)
124137
return {
125138
'success': False,
126-
'error': err
139+
'error': str(err)
140+
}
141+
except Exception as err:
142+
print(err)
143+
return {
144+
'success': False,
145+
'error': str(err)
146+
}
147+
148+
@app.post('/generate_prompt')
149+
def generate_prompt():
150+
"""Generate prompt from selected tables"""
151+
try:
152+
content = request.json
153+
selected = content['selected']
154+
query_temperture = content['temp']
155+
156+
if not content['api_key'] and not openai.api_key:
157+
return {
158+
'success': False,
159+
'error': 'Please set OPENAI_TOKEN in .env file or set token in UI'
160+
}
161+
162+
if content['api_key'] and not openai.api_key:
163+
openai.api_key = content['api_key']
164+
165+
# Update prompt
166+
regen_schema = schema.regen(selected)
167+
final_prompt = f'Your task is create creative prompt, using this scheme of database: {regen_schema}\n\nDo not generate SQL query, generate text based prompt:\n\n'
168+
169+
gpt_response = openai.Completion.create(
170+
engine="text-davinci-003",
171+
prompt=final_prompt,
172+
temperature=float(query_temperture),
173+
max_tokens=500,
174+
top_p=1,
175+
frequency_penalty=0,
176+
presence_penalty=0,
177+
stop=["\n\n"]
178+
)
179+
180+
print(gpt_response)
181+
182+
used_tokens = gpt_response['usage']['total_tokens']
183+
184+
# Get SQL query
185+
query = gpt_response['choices'][0]['text'].lstrip().rstrip()
186+
print('Generated prompt:', query)
187+
188+
return {
189+
'success': True,
190+
'query': query,
191+
'used_tokens': used_tokens,
127192
}
128193
except Exception as err:
129194
print(err)
130195
return {
131196
'success': False,
132-
'error': err
197+
'error': str(err)
133198
}
134199

135200

docs/demo.gif

4.96 KB
Loading

schema.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111

1212
class Schema:
1313
"""Generate SQL Schema from PostgreSQL"""
14+
1415
def __init__(self, schema = 'public'):
1516
"""Connect to PostgreSQL database"""
1617
self.schema = schema
@@ -25,6 +26,7 @@ def __init__(self, schema = 'public'):
2526
self.comments = []
2627
self.tables = []
2728
self.columns = []
29+
2830
def get_tables(self):
2931
"""Get list of tables"""
3032
self.cur.execute("SELECT table_name FROM information_schema.tables WHERE table_schema = %s", (self.schema,))

tpl/index.html

Lines changed: 94 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,31 @@
4242
<div id="app">
4343
<div class="container">
4444
<div class="row">
45+
46+
<div class="col-md-8 col-md-offset-2" v-if="view === 'setkey'">
47+
<div class="panel panel-default">
48+
<div class="panel-heading">
49+
<h3 class="panel-title">Set OpenAI Token</h3>
50+
</div>
51+
<div class="panel-body">
52+
<p>Looks like API token is not set in environment variable. Please enter your OpenAI API token below and it will be saved to browser localStorage</p>
53+
<fieldset>
54+
<div class="form-group">
55+
<input class="form-control" v-model="key" placeholder="Enter OpenAI API token"
56+
name="key" type="text">
57+
</div>
58+
<div>
59+
<input class="btn btn-lg btn-success btn-block" type="submit" value="Save to localStorage and continue"
60+
@click="setKey">
61+
</div>
62+
</fieldset>
63+
</div>
64+
<div class="panel-footer">
65+
<span class="text-muted">You can get your OpenAI API token <a href="https://beta.openai.com/account/api-keys" target="_blank">here</a>.</span>
66+
</div>
67+
</div>
68+
</div>
69+
4570
<div class="col-md-8 col-md-offset-2" v-if="view === 'index'">
4671
<div class="panel panel-default">
4772
<div class="panel-heading">
@@ -61,8 +86,15 @@ <h3 class="panel-title">GPT SQL Query: Configuation</h3>
6186
v-model="temp" max="1" step="0.1" value="0.5">
6287
</div>
6388
<div v-if="!isLoading">
64-
<input class="btn btn-lg btn-success btn-block" type="submit" value="🧙‍♂️ Do Magic!"
65-
@click="generate">
89+
<div class="row">
90+
<div class="col-md-6">
91+
<input class="btn btn-lg btn-success btn-block" type="submit" value="🧙‍♂️ Do Magic!" @click="generate">
92+
</div>
93+
<div class="col-md-6">
94+
<input class="btn btn-lg btn-success btn-block" type="submit" value="🤷‍♂️ I don't know what to ask" @click="generateRandom">
95+
</div>
96+
</div>
97+
6698
</div>
6799
<div v-else>
68100
<button class="btn btn-lg btn-success btn-block" type="button" disabled>
@@ -122,13 +154,21 @@ <h3 class="panel-title">Approve generate SQL query<br /><br /></h3>
122154
</div>
123155

124156
<div class="panel-body">
157+
<div class="alert alert-warning" role="alert" v-if="sql_error">
158+
<b>Error in generated query:</b> [[sql_error]]
159+
</div>
125160
<fieldset>
126161
<div class="form-group">
127-
<textarea class="form-control" name="query" type="text" rows="15" cols="90"
128-
v-model="sql_query"></textarea>
162+
<textarea
163+
class="form-control"
164+
name="query"
165+
type="text"
166+
cols="80"
167+
rows="12"
168+
v-model="sql_query">
169+
</textarea>
129170
</div>
130171
<div v-if="!isLoading">
131-
<!-- row of two buttons - refresh & run -->
132172
<div class="row">
133173
<div class="col-md-6">
134174
<input class="btn btn-lg btn-success btn-block" type="submit" :value="'🧙‍♂️ Regenerate (~' + (used_tokens) + ' tokens)'"
@@ -181,9 +221,6 @@ <h3 class="panel-title">Results of SQL query<br /><br /></h3>
181221
</tbody>
182222
</table>
183223
</div>
184-
185-
<!-- Execution time -->
186-
187224
</div>
188225
<div class="panel-footer">
189226
<div class="pull-right">
@@ -210,11 +247,12 @@ <h3 class="panel-title">Results of SQL query<br /><br /></h3>
210247
</div>
211248

212249
<!-- Vue -->
213-
<script>
250+
<script lang="javascript">
214251
var app = new Vue({
215252
el: '#app',
216253
delimiters: ['[[', ']]'],
217254
data: {
255+
hasOpenAIKey: '{{has_openai_key}}',
218256
sql_schema: '{{sql_schema}}',
219257
in_schema: JSON.parse('{{json_data|safe}}'),
220258
sql_query: '',
@@ -226,10 +264,16 @@ <h3 class="panel-title">Results of SQL query<br /><br /></h3>
226264
isLoading: false,
227265
seconds_elapsed: 0,
228266
used_tokens: 0,
267+
key: '',
268+
sql_error: null
229269
},
230270

231271
created() {
232-
this.schema = this.in_schema;
272+
if(localStorage.getItem('openai_key'))
273+
this.key = localStorage.getItem('openai_key')
274+
if(this.hasOpenAIKey === 'False' && !this.key)
275+
this.view = 'setkey'
276+
this.schema = this.in_schema
233277
for (var key in this.schema) {
234278
if (localStorage.getItem(key) === 'true') {
235279
this.schema[key].selected = true;
@@ -238,12 +282,19 @@ <h3 class="panel-title">Results of SQL query<br /><br /></h3>
238282
}
239283
}
240284

241-
//this.view = 'generate';
242-
//this.sql_query = "SELECT * FROM users WHERE username LIKE 'A%' ORDER BY created_at DESC LIMIT 10;"
285+
this.view = 'generate';
286+
this.sql_query = "SELECT * FROM usfers WHERE fusername LIKE 'A%' ORDER BY created_at DESC LIMIT 10;"
243287
},
244288

245289
methods: {
246290

291+
setKey: function () {
292+
if(!this.key.match(/^sk-([a-zA-Z0-9]{1,64})$/))
293+
return alert('Invalid key, it should be in the format sk-[a-zA-Z0-9]{32}');
294+
this.view = 'index';
295+
localStorage.setItem('openai_key', this.key);
296+
},
297+
247298
updateSchema: function (key) {
248299
// save to localStorage
249300
localStorage.setItem(key, this.schema[key].selected);
@@ -267,10 +318,37 @@ <h3 class="panel-title">Results of SQL query<br /><br /></h3>
267318
this.$forceUpdate();
268319
},
269320

321+
generateRandom: async function () {
322+
if(this.isLoading)
323+
return;
324+
this.isLoading = true;
325+
326+
const response = await fetch('/generate_prompt', {
327+
method: 'POST',
328+
headers: {
329+
'Content-Type': 'application/json'
330+
},
331+
body: JSON.stringify({
332+
temp: this.temp,
333+
api_key: this.key,
334+
selected: Object.keys(this.schema).filter(key => this.schema[key]
335+
.selected)
336+
})
337+
});
338+
339+
const data = await response.json();
340+
this.isLoading = false;
341+
if(!data.success)
342+
return alert(data.error);
343+
this.query = data.query;
344+
},
345+
270346
generate: async function () {
271347
if(this.query === '' || this.isLoading)
272348
return;
273349
this.isLoading = true;
350+
this.sql_error = null;
351+
274352
const response = await fetch('/generate', {
275353
method: 'POST',
276354
headers: {
@@ -279,10 +357,12 @@ <h3 class="panel-title">Results of SQL query<br /><br /></h3>
279357
body: JSON.stringify({
280358
query: this.query,
281359
temp: this.temp,
360+
api_key: this.key,
282361
selected: Object.keys(this.schema).filter(key => this.schema[key]
283362
.selected)
284363
})
285364
});
365+
286366
const data = await response.json();
287367
this.isLoading = false;
288368
if(data.success) {
@@ -297,6 +377,7 @@ <h3 class="panel-title">Results of SQL query<br /><br /></h3>
297377
runQuery: async function () {
298378
// fetch
299379
this.isLoading = true;
380+
this.sql_error = null;
300381
const response = await fetch('/run', {
301382
method: 'POST',
302383
headers: {
@@ -314,7 +395,7 @@ <h3 class="panel-title">Results of SQL query<br /><br /></h3>
314395
this.seconds_elapsed = data.seconds_elapsed;
315396
this.view = 'run';
316397
} else {
317-
alert(data.error);
398+
this.sql_error = data.error;
318399
}
319400
},
320401

0 commit comments

Comments
 (0)