From 91ae0417f0dc210f2b477755ee832418e137447d Mon Sep 17 00:00:00 2001
From: Zain Hoda <7146154+zainhoda@users.noreply.github.com>
Date: Sat, 29 Jul 2023 09:57:49 -0400
Subject: [PATCH 1/3] filtering the training plan
---
src/vanna/__init__.py | 128 ++++++++++++++++++++++++++++++++++++++----
1 file changed, 118 insertions(+), 10 deletions(-)
diff --git a/src/vanna/__init__.py b/src/vanna/__init__.py
index 54ff337c..feb1355b 100644
--- a/src/vanna/__init__.py
+++ b/src/vanna/__init__.py
@@ -583,7 +583,24 @@ def remove_item(self, item: str):
-def get_training_plan() -> TrainingPlan:
+def __get_databases() -> List[str]:
+ try:
+ df_databases = run_sql("SELECT * FROM INFORMATION_SCHEMA.DATABASES")
+ except:
+ try:
+ df_databases = run_sql("SHOW DATABASES")
+ except:
+ return []
+
+ return df_databases['DATABASE_NAME'].unique().tolist()
+
+def __get_information_schema_tables(database: str) -> pd.DataFrame:
+ df_tables = run_sql(f'SELECT * FROM {database}.INFORMATION_SCHEMA.TABLES')
+
+ return df_tables
+
+
+def get_training_plan(filter_databases: Union[List[str], None] = None, filter_schemas: Union[List[str], None] = None, include_information_schema: bool = False, use_historical_queries: bool = True) -> TrainingPlan:
"""
**Example:**
```python
@@ -591,20 +608,111 @@ def get_training_plan() -> TrainingPlan:
vn.train(plan=plan)
```
+ """
- Get the training plan for the model.
+ plan = TrainingPlan([])
- Returns:
- TrainingPlan: The training plan for the model.
- """
- d = __rpc_call(method="get_training_plan", params=[])
+ if run_sql is None:
+ raise ValidationError("Please connect to a database first.")
- if 'result' not in d:
- raise ValidationError("Failed to get training plan")
+ databases = __get_databases()
+
+ for database in databases:
+ if filter_databases is not None and database not in filter_databases:
+ continue
+
+ try:
+ df_tables = __get_information_schema_tables(database=database)
+
+ print(f"Trying INFORMATION_SCHEMA.COLUMNS for {database}")
+ df_columns = run_sql(f"SELECT * FROM {database}.INFORMATION_SCHEMA.COLUMNS")
+
+ for schema in df_tables['TABLE_SCHEMA'].unique().tolist():
+ if filter_schemas is not None and schema not in filter_schemas:
+ continue
- training_plan = TrainingPlan(**d['result'])
+ if not include_information_schema and schema == "INFORMATION_SCHEMA":
+ continue
+
+ df_columns_filtered_to_schema = df_columns.query(f"TABLE_SCHEMA == '{schema}'")
+
+ try:
+ tables = df_columns_filtered_to_schema['TABLE_NAME'].unique().tolist()
+
+ for table in tables:
+ df_columns_filtered_to_table = df_columns_filtered_to_schema.query(f"TABLE_NAME == '{table}'")
+ doc = f"The following columns are in the {table} table in the {database} database:\n\n"
+ doc += df_columns_filtered_to_table[["TABLE_CATALOG", "TABLE_SCHEMA", "TABLE_NAME", "COLUMN_NAME", "DATA_TYPE", "COMMENT"]].to_markdown()
+
+ plan._plan.append(TrainingPlanItem(
+ item_type=TrainingPlanItem.ITEM_TYPE_IS,
+ item_group=f"{database}.{schema}",
+ item_name=table,
+ item_value=doc
+ ))
+
+ except Exception as e:
+ print(e)
+ pass
+ except Exception as e:
+ print(e)
+
+
+ # try:
+ # print("Trying SHOW TABLES")
+ # df_f = run_sql("SHOW TABLES")
+
+ # for schema in df_f.schema_name.unique():
+ # try:
+ # print(f"Trying GET_DDL for {schema}")
+ # ddl_df = run_sql(f"SELECT GET_DDL('schema', '{schema}')")
+
+ # plan._plan.append(TrainingPlanItem(
+ # item_type=TrainingPlanItem.ITEM_TYPE_DDL,
+ # item_group=schema,
+ # item_name="All Tables",
+ # item_value=ddl_df.iloc[0, 0]
+ # ))
+ # except:
+ # pass
+ # except:
+ # try:
+ # print("Trying INFORMATION_SCHEMA.TABLES")
+ # df = run_sql("SELECT * FROM INFORMATION_SCHEMA.TABLES")
+
+ # breakpoint()
+
+ # try:
+ # print("Trying SCHEMATA")
+ # df_schemata = run_sql("SELECT * FROM region-us.INFORMATION_SCHEMA.SCHEMATA")
+
+ # for schema in df_schemata.schema_name.unique():
+ # df = run_sql(f"SELECT * FROM {schema}.information_schema.tables")
+
+ # for table in df.table_name.unique():
+ # plan._plan.append(TrainingPlanItem(
+ # item_type=TrainingPlanItem.ITEM_TYPE_IS,
+ # item_group=schema,
+ # item_name=table,
+ # item_value=None
+ # ))
+
+ # try:
+ # ddl_df = run_sql(f"SELECT GET_DDL('schema', '{schema}')")
+
+ # plan._plan.append(TrainingPlanItem(
+ # item_type=TrainingPlanItem.ITEM_TYPE_DDL,
+ # item_group=schema,
+ # item_name=None,
+ # item_value=ddl_df.iloc[0, 0]
+ # ))
+ # except:
+ # pass
+ # except:
+ # pass
+
+ return plan
- return training_plan
def train(question: str = None, sql: str = None, ddl: str = None, documentation: str = None, json_file: str = None,
sql_file: str = None) -> bool:
From 6f9eb89adadf37f3ba66a93313388078898355e2 Mon Sep 17 00:00:00 2001
From: Zain Hoda <7146154+zainhoda@users.noreply.github.com>
Date: Sat, 29 Jul 2023 16:33:32 -0400
Subject: [PATCH 2/3] training plan
---
pyproject.toml | 2 +-
src/vanna/__init__.py | 57 +++++++++++++++++++++++++++++++++++++------
2 files changed, 51 insertions(+), 8 deletions(-)
diff --git a/pyproject.toml b/pyproject.toml
index 491fa5f6..47c8c94e 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,6 +1,6 @@
[project]
name = "vanna"
-version = "0.0.16"
+version = "0.0.17"
authors = [
{ name="Zain Hoda", email="zain@vanna.ai" },
]
diff --git a/src/vanna/__init__.py b/src/vanna/__init__.py
index feb1355b..78459544 100644
--- a/src/vanna/__init__.py
+++ b/src/vanna/__init__.py
@@ -600,11 +600,15 @@ def __get_information_schema_tables(database: str) -> pd.DataFrame:
return df_tables
-def get_training_plan(filter_databases: Union[List[str], None] = None, filter_schemas: Union[List[str], None] = None, include_information_schema: bool = False, use_historical_queries: bool = True) -> TrainingPlan:
+def get_training_plan_experimental(filter_databases: Union[List[str], None] = None, filter_schemas: Union[List[str], None] = None, include_information_schema: bool = False, use_historical_queries: bool = True) -> TrainingPlan:
"""
+ **EXPERIMENTAL** : This method is experimental and may change in future versions.
+
+ Get a training plan based on the metadata in the database. Currently this only works for Snowflake.
+
**Example:**
```python
- plan = vn.get_training_plan()
+ plan = vn.get_training_plan_experimental(filter_databases=["employees"], filter_schemas=["public"])
vn.train(plan=plan)
```
@@ -615,6 +619,31 @@ def get_training_plan(filter_databases: Union[List[str], None] = None, filter_sc
if run_sql is None:
raise ValidationError("Please connect to a database first.")
+ if use_historical_queries:
+ try:
+ print("Trying query history")
+ df_history = run_sql(""" select * from table(information_schema.query_history(result_limit => 5000)) order by start_time""")
+
+ df_history_filtered = df_history.query('ROWS_PRODUCED > 1')
+ if filter_databases is not None:
+ mask = df_history_filtered['QUERY_TEXT'].str.lower().apply(lambda x: any(s in x for s in [s.lower() for s in filter_databases]))
+ df_history_filtered = df_history_filtered[mask]
+
+ if filter_schemas is not None:
+ mask = df_history_filtered['QUERY_TEXT'].str.lower().apply(lambda x: any(s in x for s in [s.lower() for s in filter_schemas]))
+ df_history_filtered = df_history_filtered[mask]
+
+ for query in df_history_filtered.sample(10)['QUERY_TEXT'].unique().tolist():
+ plan._plan.append(TrainingPlanItem(
+ item_type=TrainingPlanItem.ITEM_TYPE_SQL,
+ item_group="",
+ item_name=generate_question(query),
+ item_value=query
+ ))
+
+ except Exception as e:
+ print(e)
+
databases = __get_databases()
for database in databases:
@@ -657,7 +686,6 @@ def get_training_plan(filter_databases: Union[List[str], None] = None, filter_sc
except Exception as e:
print(e)
-
# try:
# print("Trying SHOW TABLES")
# df_f = run_sql("SHOW TABLES")
@@ -715,7 +743,7 @@ def get_training_plan(filter_databases: Union[List[str], None] = None, filter_sc
def train(question: str = None, sql: str = None, ddl: str = None, documentation: str = None, json_file: str = None,
- sql_file: str = None) -> bool:
+ sql_file: str = None, plan: TrainingPlan = None) -> bool:
"""
**Example:**
```python
@@ -728,6 +756,7 @@ def train(question: str = None, sql: str = None, ddl: str = None, documentation:
If you call it with the ddl argument, it's equivalent to [`add_ddl()`][vanna.add_ddl].
If you call it with the documentation argument, it's equivalent to [`add_documentation()`][vanna.add_documentation].
It can also accept a JSON file path or SQL file path to train on a batch of questions and SQL queries or a list of SQL queries respectively.
+ Additionally, you can pass a [`TrainingPlan`][vanna.TrainingPlan] object. Get a training plan with [`vn.get_training_plan()`][vanna.get_training_plan].
Args:
question (str): The question to train on.
@@ -736,6 +765,7 @@ def train(question: str = None, sql: str = None, ddl: str = None, documentation:
json_file (str): The JSON file path.
ddl (str): The DDL statement.
documentation (str): The documentation to train on.
+ plan (TrainingPlan): The training plan to train on.
"""
if question and not sql:
@@ -764,7 +794,7 @@ def train(question: str = None, sql: str = None, ddl: str = None, documentation:
print("Adding Questions And SQLs using file:", json_file)
for question in data:
if not add_sql(question=question['question'], sql=question['answer']):
- logger.warning(f"Not able to add sql for question: {question['question']} from {json_file}")
+ print(f"Not able to add sql for question: {question['question']} from {json_file}")
return False
return True
@@ -784,11 +814,24 @@ def train(question: str = None, sql: str = None, ddl: str = None, documentation:
if add_sql(question=question, sql=statement):
print("SQL added!")
return True
- logger.warning("Not able to add sql.")
+ print("Not able to add sql.")
return False
return False
- # Here we're going to attempt auto training
+ if plan:
+ for item in plan._plan:
+ if item.item_type == TrainingPlanItem.ITEM_TYPE_DDL:
+ if not add_ddl(item.item_value):
+ print(f"Not able to add ddl for {item.item_group}")
+ return False
+ elif item.item_type == TrainingPlanItem.ITEM_TYPE_IS:
+ if not add_documentation(item.item_value):
+ print(f"Not able to add documentation for {item.item_group}.{item.item_name}")
+ return False
+ elif item.item_type == TrainingPlanItem.ITEM_TYPE_SQL:
+ if not add_sql(question=item.item_name, sql=item.item_value):
+ print(f"Not able to add sql for {item.item_group}.{item.item_name}")
+ return False
def flag_sql_for_review(question: str, sql: Union[str, None] = None, error_msg: Union[str, None] = None) -> bool:
From 252fff3b282442ebc1472e63ded679604eedd823 Mon Sep 17 00:00:00 2001
From: Zain Hoda <7146154+zainhoda@users.noreply.github.com>
Date: Sat, 29 Jul 2023 17:58:06 -0400
Subject: [PATCH 3/3] notebooks
---
docs/notebooks/vn-ask.md | 179 ++++++--
docs/notebooks/vn-ask_files/vn-ask_10_2.png | Bin 0 -> 125336 bytes
docs/notebooks/vn-ask_files/vn-ask_11_2.png | Bin 0 -> 78743 bytes
docs/notebooks/vn-ask_files/vn-ask_12_2.png | Bin 0 -> 142409 bytes
docs/notebooks/vn-train.md | 266 ++++++++++++
mkdocs.yml | 6 +-
notebooks/vn-train.ipynb | 452 +++++++++++++++++++-
src/vanna/__init__.py | 2 +-
8 files changed, 855 insertions(+), 50 deletions(-)
create mode 100644 docs/notebooks/vn-ask_files/vn-ask_10_2.png
create mode 100644 docs/notebooks/vn-ask_files/vn-ask_11_2.png
create mode 100644 docs/notebooks/vn-ask_files/vn-ask_12_2.png
diff --git a/docs/notebooks/vn-ask.md b/docs/notebooks/vn-ask.md
index 1249c16d..f6f4bd90 100644
--- a/docs/notebooks/vn-ask.md
+++ b/docs/notebooks/vn-ask.md
@@ -1,10 +1,10 @@
-![Vanna AI](https://img.vanna.ai/vanna-full.svg)
+![Vanna AI](https://img.vanna.ai/vanna-ask.svg)
-This notebook will help you unleash the full potential of AI-powered data analysis at your organization. We'll go through how to "bulk train" Vanna and generate SQL, tables, charts, and explanations, all with minimal code and effort. For more about Vanna, see our [intro blog post](https://medium.com/vanna-ai/intro-to-vanna-a-python-based-ai-sql-co-pilot-218c25b19c6a).
+The following notebook goes through the process of asking questions from your data using Vanna AI. Here we use a demo model that is pre-trained on the [TPC-H dataset](https://docs.snowflake.com/en/user-guide/sample-data-tpch.html) that is available in Snowflake.
-[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/vanna-ai/vanna-py/blob/main/notebooks/vn-full.ipynb)
+[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/vanna-ai/vanna-py/blob/main/notebooks/vn-ask.ipynb)
-[![Open in GitHub](https://img.vanna.ai/github.svg)](https://github.com/vanna-ai/vanna-py/blob/main/notebooks/vn-full.ipynb)
+[![Open in GitHub](https://img.vanna.ai/github.svg)](https://github.com/vanna-ai/vanna-py/blob/main/notebooks/vn-ask.ipynb)
# Install Vanna
First we install Vanna from [PyPI](https://pypi.org/project/vanna/) and import it.
@@ -20,7 +20,6 @@ Here, we'll also install the Snowflake connector. If you're using a different da
```python
import vanna as vn
import snowflake.connector
-import pandas as pd
```
# Login
@@ -37,7 +36,7 @@ You need to choose a globally unique model name. Try using your company name or
```python
-vn.set_model('my-model') # Enter your dataset name here. This is a globally unique identifier for your dataset.
+vn.set_model('tpc') # Enter your model name here. This is a globally unique identifier for your model.
```
# Set Database Connection
@@ -64,20 +63,105 @@ vn.ask("What are the top 10 customers by sales?")
GROUP BY customer_name
ORDER BY total_sales desc limit 10;
-![plot1](plot1.png)
- AI-generated follow-up questions:
- What are the countries of the top 10 customers by sales?
- How many orders did each of the top 10 customers place?
- What is the average sales amount per customer in the top 10?
- Can you provide a breakdown of the sales by country for the top 10 customers?
- Who are the top 10 customers in terms of returned parts gross value?
- What are the total sales for each customer in the top 3?
- Can you provide a breakdown of the sales by region for the top customers?
- How many customers are there in each country?
- What is the total revenue for the top 5 countries?
- Can you provide a breakdown of the sales by customer for the top 5 countries?
+
+
+
+
+
+ |
+ CUSTOMER_NAME |
+ TOTAL_SALES |
+
+
+
+
+ 0 |
+ Customer#000143500 |
+ 6757566.0218 |
+
+
+ 1 |
+ Customer#000095257 |
+ 6294115.3340 |
+
+
+ 2 |
+ Customer#000087115 |
+ 6184649.5176 |
+
+
+ 3 |
+ Customer#000131113 |
+ 6080943.8305 |
+
+
+ 4 |
+ Customer#000134380 |
+ 6075141.9635 |
+
+
+ 5 |
+ Customer#000103834 |
+ 6059770.3232 |
+
+
+ 6 |
+ Customer#000069682 |
+ 6057779.0348 |
+
+
+ 7 |
+ Customer#000102022 |
+ 6039653.6335 |
+
+
+ 8 |
+ Customer#000098587 |
+ 6027021.5855 |
+
+
+ 9 |
+ Customer#000064660 |
+ 5905659.6159 |
+
+
+
+
+
+
+
+
+![png](vn-ask_files/vn-ask_10_2.png)
+
+
+
+
+AI-generated follow-up questions:
+
+* What is the country name for each of the top 10 customers by sales?
+* How many orders does each of the top 10 customers by sales have?
+* What is the total revenue for each of the top 10 customers by sales?
+* What are the customer names and total sales for customers in the United States?
+* Which customers in Africa have returned the most parts with a gross value?
+* What are the total sales for the top 3 customers?
+* What are the customer names and total sales for the top 5 customers?
+* What are the total sales for customers in Europe?
+* How many customers are there in each country?
+
@@ -149,19 +233,26 @@ vn.ask("Which 5 countries have the highest sales?")
-![plot2](plot2.png)
+
+![png](vn-ask_files/vn-ask_11_2.png)
+
+
+
+
+AI-generated follow-up questions:
+
+* What are the total sales for each country in descending order?
+* Which country has the highest number of customers?
+* What are the total sales for each customer in descending order?
+* Which customers in the United States have the highest total sales?
+* What are the total sales and number of orders for each customer in each country?
+* What are the total sales for customers in Europe?
+* What are the top 10 countries with the highest total order amount?
+* Which country has the highest number of failed orders?
+* Which customers have the highest total sales?
+*
- AI-generated follow-up questions:
- What are the total sales for each country?
- Which country has the highest number of customers?
- What are the total sales for each customer?
- What are the top 3 customers with the highest sales?
- What is the total revenue for each customer and country?
- What are the total sales for each customer in Europe?
- What are the top 10 countries with the highest total order amount?
- Which country has the highest number of failed orders?
- What are the top 3 customers with the highest sales?
@@ -264,20 +355,26 @@ vn.ask("Who are the top 2 biggest customers in each region?")
-![plot3](plot3.png)
+
+![png](vn-ask_files/vn-ask_12_2.png)
+
+
+
+
+AI-generated follow-up questions:
+
+* - What are the total sales for each customer in the Asia region?
+* - How many orders does each customer in the Americas region have?
+* - Who are the top 5 customers with the highest total sales?
+* - What is the total revenue for each customer in the Europe region?
+* - Can you provide a breakdown of the number of customers in each country?
+* - Which customers in the United States have the highest total sales?
+* - What are the total sales for each customer in the Asia region?
+* - What are the top 10 customers with the highest returned parts gross value in Africa?
+* - What are the top 3 customers with the highest total sales overall?
+* - Can you provide a list of the first 10 customers in the database?
- AI-generated follow-up questions:
- - What are the total sales for each customer in Europe?
- - What are the total sales for each customer in the United States?
- - How many customers are there in each country?
- - What is the total revenue for each customer in each country?
- - Which customers have the highest total sales?
- - Which customers have the highest number of orders?
- - Which customers have the highest returned parts gross value in Africa?
- - What are the total sales for the top 3 customers?
- - What are the total sales for the top 10 customers?
- - What is the total sales for each customer?
# Run as a Web App
diff --git a/docs/notebooks/vn-ask_files/vn-ask_10_2.png b/docs/notebooks/vn-ask_files/vn-ask_10_2.png
new file mode 100644
index 0000000000000000000000000000000000000000..0a4fb6011d86bdd2e24a21653f541d704b370544
GIT binary patch
literal 125336
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
z
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