Skip to content

Commit 1a1e3de

Browse files
Initial UI to connect chat with sidekick
2 parents 6d35db8 + a0241d5 commit 1a1e3de

File tree

4 files changed

+93
-38
lines changed

4 files changed

+93
-38
lines changed

sidekick/configs/.env.toml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,3 +14,8 @@ LOG-LEVEL = "INFO"
1414

1515
[DB-DIALECT]
1616
DB_TYPE = "sqlite"
17+
18+
[TABLE_INFO]
19+
TABLE_INFO_PATH = "/examples/test/table_info.jsonl"
20+
TABLE_SAMPLES_PATH = "/examples/test/masked_data_and_columns.csv"
21+
TABLE_NAME = "demo"

sidekick/db_config.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -196,9 +196,11 @@ def execute_query_db(self, query=None, n_rows=100):
196196
engine.dispose()
197197
else:
198198
logger.info("Query Empty or None!")
199-
return output
199+
return output, None
200200
except Exception as e:
201-
logger.info(f"Error occurred : {format(e)}")
201+
err = f"Error occurred : {format(e)}"
202+
logger.info(err)
203+
return None, err
202204
finally:
203205
connection.close()
204206
engine.dispose()

sidekick/prompter.py

Lines changed: 65 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,9 @@ def update_table_info(cache_path: str, table_info_path: str = None, table_name:
127127
@click.option("--port", "-P", default=5432, help="Database port", prompt="Enter port (default 5432)")
128128
@click.option("--table-info-path", "-t", help="Table info path", default=None)
129129
def db_setup(db_name: str, hostname: str, user_name: str, password: str, port: int, table_info_path: str):
130+
db_setup_api(db_name=db_name, hostname=hostname, user_name=user_name, password=password, port=port, table_info_path=table_info_path, table_samples_path=None, table_name=None, is_command=True)
131+
132+
def db_setup_api(db_name: str, hostname: str, user_name: str, password: str, port: int, table_info_path: str, table_samples_path: str, table_name: str, is_command:bool=False):
130133
"""Creates context for the new Database"""
131134
click.echo(f" Information supplied:\n {db_name}, {hostname}, {user_name}, {password}, {port}")
132135
try:
@@ -151,7 +154,7 @@ def db_setup(db_name: str, hostname: str, user_name: str, password: str, port: i
151154
else:
152155
click.echo("Database already exists!")
153156

154-
val = enter_table_name()
157+
val = enter_table_name() if is_command else "y"
155158
while True:
156159
if val.lower() != "y" and val.lower() != "n":
157160
click.echo("In-correct values. Enter Yes(y) or no(n)")
@@ -163,7 +166,7 @@ def db_setup(db_name: str, hostname: str, user_name: str, password: str, port: i
163166
table_info_path = _get_table_info(path)
164167

165168
if val.lower() == "y" or val.lower() == "yes":
166-
table_value = input("Enter table name: ")
169+
table_value = input("Enter table name: ") if is_command else table_name
167170
click.echo(f"Table name: {table_value}")
168171
# set table name
169172
db_obj.table_name = table_value.replace(" ", "_")
@@ -173,17 +176,23 @@ def db_setup(db_name: str, hostname: str, user_name: str, password: str, port: i
173176
# Check if table exists; pending --> and doesn't have any rows
174177
if db_obj.has_table():
175178
click.echo(f"Checked table {db_obj.table_name} exists in the DB.")
176-
val = input(color(F.GREEN, "", "Would you like to add few sample rows (at-least 3)? (y/n):"))
179+
val = input(color(F.GREEN, "", "Would you like to add few sample rows (at-least 3)? (y/n):")) if is_command else "y"
177180
if val.lower().strip() == "y" or val.lower().strip() == "yes":
178-
val = input("Path to a CSV file to insert data from:")
181+
val = input("Path to a CSV file to insert data from:") if is_command else table_samples_path
179182
db_obj.add_samples(val)
180183
else:
181184
click.echo("Exiting...")
182185
return
183186
else:
184-
click.echo("Job done. Ask a question now!")
187+
echo_msg = "Job done. Ask a question now!"
188+
click.echo(echo_msg)
189+
190+
return f"Created a Database {db_name}. Inserted sample values from {table_samples_path} into table {table_name}, please ask questions!"
185191
except Exception as e:
186-
click.echo(f"Error creating database. Check configuration parameters.\n: {e}")
192+
echo_msg = f"Error creating database. Check configuration parameters.\n: {e}"
193+
click.echo(echo_msg)
194+
if not is_command:
195+
return echo_msg
187196

188197

189198
@cli.group("learn")
@@ -250,7 +259,11 @@ def update_context():
250259
@click.option("--table-info-path", "-t", help="Table info path", default=None)
251260
@click.option("--sample-queries", "-s", help="Samples path", default=None)
252261
def query(question: str, table_info_path: str, sample_queries: str):
262+
query_api(question= question, table_info_path=table_info_path, sample_queries=sample_queries, is_command=True)
263+
264+
def query_api(question: str, table_info_path: str, sample_queries: str, is_command:bool=False):
253265
"""Asks question and returns SQL."""
266+
results = []
254267
# Book-keeping
255268
setup_dir(base_path)
256269

@@ -273,11 +286,16 @@ def query(question: str, table_info_path: str, sample_queries: str):
273286
api_key = env_settings["OPENAI"]["OPENAI_API_KEY"]
274287
if api_key is None or api_key == "":
275288
if os.getenv("OPENAI_API_KEY") is None or os.getenv("OPENAI_API_KEY") == "":
276-
val = input(
277-
color(F.GREEN, "", "Looks like API key is not set, would you like to set OPENAI_API_KEY? (y/n):")
278-
)
279-
if val.lower() == "y":
280-
api_key = input(color(F.GREEN, "", "Enter OPENAI_API_KEY :"))
289+
if is_command:
290+
val = input(
291+
color(F.GREEN, "", "Looks like API key is not set, would you like to set OPENAI_API_KEY? (y/n):")
292+
)
293+
if val.lower() == "y":
294+
api_key = input(color(F.GREEN, "", "Enter OPENAI_API_KEY :"))
295+
296+
if api_key is None and is_command:
297+
return ["Looks like API key is not set, please set OPENAI_API_KEY!"]
298+
281299
os.environ["OPENAI_API_KEY"] = api_key
282300
env_settings["OPENAI"]["OPENAI_API_KEY"] = api_key
283301

@@ -310,10 +328,11 @@ def query(question: str, table_info_path: str, sample_queries: str):
310328
db_url, api_key, job_path=base_path, data_input_path=table_info_path, samples_queries=sample_queries
311329
)
312330
sql_g._tasks = sql_g.generate_tasks(table_names, question)
331+
results.extend(["List of Actions Generated: \n", sql_g._tasks, "\n"])
313332
click.echo(sql_g._tasks)
314333

315334
updated_tasks = None
316-
if sql_g._tasks is not None:
335+
if sql_g._tasks is not None and is_command:
317336
edit_val = click.prompt("Would you like to edit the tasks? (y/n)")
318337
if edit_val.lower() == "y":
319338
updated_tasks = click.edit(sql_g._tasks)
@@ -331,23 +350,27 @@ def query(question: str, table_info_path: str, sample_queries: str):
331350
if res is not None:
332351
updated_sql = None
333352
res_val = "e"
334-
while res_val.lower() in ["e", "edit", "r", "regenerate"]:
335-
res_val = click.prompt(
336-
"Would you like to 'edit' or 'regenerate' the SQL? Use 'e' to edit or 'r' to regenerate. "
337-
"To skip, enter 's' or 'skip'"
338-
)
339-
if res_val.lower() == "e" or res_val.lower() == "edit":
340-
updated_sql = click.edit(res)
341-
click.echo(f"Updated SQL:\n {updated_sql}")
342-
elif res_val.lower() == "r" or res_val.lower() == "regenerate":
343-
click.echo("Attempting to regenerate...")
344-
res = sql_g.generate_sql(table_names, question, model_name=model_name, _dialect=db_dialect)
345-
logger.info(f"Input query: {question}")
346-
logger.info(f"Generated response:\n\n{res}")
347-
348-
exe_sql = click.prompt("Would you like to execute the generated SQL (y/n)?")
353+
if is_command:
354+
while res_val.lower() in ["e", "edit", "r", "regenerate"]:
355+
res_val = click.prompt(
356+
"Would you like to 'edit' or 'regenerate' the SQL? Use 'e' to edit or 'r' to regenerate. "
357+
"To skip, enter 's' or 'skip'"
358+
)
359+
if res_val.lower() == "e" or res_val.lower() == "edit":
360+
updated_sql = click.edit(res)
361+
click.echo(f"Updated SQL:\n {updated_sql}")
362+
elif res_val.lower() == "r" or res_val.lower() == "regenerate":
363+
click.echo("Attempting to regenerate...")
364+
res = sql_g.generate_sql(table_names, question, model_name=model_name, _dialect=db_dialect)
365+
logger.info(f"Input query: {question}")
366+
logger.info(f"Generated response:\n\n{res}")
367+
368+
results.extend(["Generated Query:\n", res, "\n"])
369+
370+
exe_sql = click.prompt("Would you like to execute the generated SQL (y/n)?") if is_command else "y"
349371
if exe_sql.lower() == "y" or exe_sql.lower() == "yes":
350372
# For the time being, the default option is Pandas, but the user can be asked to select Database or pandas DF later.
373+
q_res = None
351374
option = "DB" # or DB
352375
_val = updated_sql if updated_sql else res
353376
if option == "DB":
@@ -359,8 +382,8 @@ def query(question: str, table_info_path: str, sample_queries: str):
359382

360383
db_obj = DBConfig(db_name, hostname, user_name, password, port, base_path=base_path, dialect=db_dialect)
361384

362-
output_res = db_obj.execute_query_db(query=_val)
363-
click.echo(f"The query results are:\n {output_res}")
385+
q_res, err = db_obj.execute_query_db(query=_val)
386+
364387
elif option == "pandas":
365388
tables = extract_table_names(_val)
366389
tables_path = dict()
@@ -383,21 +406,30 @@ def query(question: str, table_info_path: str, sample_queries: str):
383406
with open(f"{path}/table_context.json", "w") as outfile:
384407
json.dump(table_metadata, outfile, indent=4, sort_keys=False)
385408
try:
386-
res = execute_query_pd(query=_val, tables_path=tables_path, n_rows=100)
387-
click.echo(f"The query results are:\n {res}")
409+
q_res = execute_query_pd(query=_val, tables_path=tables_path, n_rows=100)
410+
click.echo(f"The query results are:\n {q_res}")
388411
except sqldf.PandaSQLException as e:
389412
logger.error(f"Error in executing the query: {e}")
390-
click.echo("Error in executing the query. Validate generate SQL and try again.")
413+
click.echo("Error in executing the query. Validate generated SQL and try again.")
391414
click.echo("No result to display.")
392415

393-
save_sql = click.prompt("Would you like to save the generated SQL (y/n)?")
416+
results.append("Query Results: \n")
417+
if q_res:
418+
click.echo(f"The query results are:\n {q_res}")
419+
results.extend([str(q_res), "\n"])
420+
else:
421+
click.echo(f"While executing query:\n {err}")
422+
results.extend([str(err), "\n"])
423+
# results.extend(["Query Results:", q_res])
424+
save_sql = click.prompt("Would you like to save the generated SQL (y/n)?") if is_command else "n"
394425
if save_sql.lower() == "y" or save_sql.lower() == "yes":
395426
# Persist for future use
396427
_val = updated_sql if updated_sql else res
397428
save_query(base_path, query=question, response=_val)
398429
else:
399430
click.echo("Exiting...")
400431

432+
return results
401433

402434
if __name__ == "__main__":
403435
cli()

ui/app.py

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import logging
22
from pathlib import Path
33
from typing import List, Optional
4+
from sidekick.prompter import db_setup_api, query_api
45

56
import openai
67
import toml
@@ -12,6 +13,16 @@
1213
ui_title = env_settings["WAVE_UI"]["TITLE"]
1314
ui_description = env_settings["WAVE_UI"]["SUB_TITLE"]
1415

16+
db_settings = toml.load(f"{base_path}/sidekick/configs/.env.toml")
17+
db_dialect = db_settings["DB-DIALECT"]["DB_TYPE"]
18+
host_name = db_settings["LOCAL_DB_CONFIG"]["HOST_NAME"]
19+
user_name = db_settings["LOCAL_DB_CONFIG"]["USER_NAME"]
20+
password = db_settings["LOCAL_DB_CONFIG"]["PASSWORD"]
21+
db_name = db_settings["LOCAL_DB_CONFIG"]["DB_NAME"]
22+
port = db_settings["LOCAL_DB_CONFIG"]["PORT"]
23+
table_info_path = f'{base_path}/{db_settings["TABLE_INFO"]["TABLE_INFO_PATH"]}'
24+
table_samples_path = f'{base_path}/{db_settings["TABLE_INFO"]["TABLE_SAMPLES_PATH"]}'
25+
table_name = db_settings["TABLE_INFO"]["TABLE_NAME"]
1526

1627
logging.basicConfig(format="%(asctime)s %(levelname)s %(message)s")
1728

@@ -62,9 +73,14 @@ async def chatbot(q: Q):
6273
question = f"{q.args.chatbot}"
6374
logging.info(f"Question: {question}")
6475

65-
######################## TODO: INTEGRATE SQL SIDEKICK HERE ###################
66-
# llm_response = sql_sidekick_result(question, q.client.data)
67-
llm_response = "This is a test response"
76+
if q.args.chatbot.lower() == "db setup":
77+
llm_response = db_setup_api(db_name=db_name, hostname=host_name, user_name=user_name, password=password, port=port, table_info_path=table_info_path, table_samples_path=table_samples_path, table_name= table_name)
78+
else:
79+
llm_response = query_api(question = question,
80+
sample_queries=None,
81+
table_info_path=table_info_path)
82+
llm_response = "\n".join(llm_response)
83+
6884
q.page["chat_card"].data += [llm_response, False]
6985

7086

0 commit comments

Comments
 (0)