nolanzandi commited on
Commit
c76addc
·
verified ·
1 Parent(s): ac253c3

integrate_graphql (#36)

Browse files

- integrate graphql (d5fdc24444980e5064742cf4de726094d66bcd1d)

app.py CHANGED
@@ -1,6 +1,6 @@
1
  from utils import TEMP_DIR, message_dict
2
  import gradio as gr
3
- import templates.data_file as data_file, templates.sql_db as sql_db, templates.doc_db as doc_db
4
 
5
  import os
6
  from getpass import getpass
@@ -76,6 +76,8 @@ with gr.Blocks(theme=theme, css=css, head=head, delete_cache=(3600,3600)) as dem
76
  sql_db.demo.render()
77
  with gr.Tab("Document (MongoDB) Database"):
78
  doc_db.demo.render()
 
 
79
 
80
  footer = gr.HTML("""<!-- Footer -->
81
  <footer class="max-w-4xl mx-auto mt-12 text-center text-gray-500 text-sm">
 
1
  from utils import TEMP_DIR, message_dict
2
  import gradio as gr
3
+ import templates.data_file as data_file, templates.sql_db as sql_db, templates.doc_db as doc_db, templates.graphql as graphql
4
 
5
  import os
6
  from getpass import getpass
 
76
  sql_db.demo.render()
77
  with gr.Tab("Document (MongoDB) Database"):
78
  doc_db.demo.render()
79
+ with gr.Tab("GraphQL API"):
80
+ graphql.demo.render()
81
 
82
  footer = gr.HTML("""<!-- Footer -->
83
  <footer class="max-w-4xl mx-auto mt-12 text-center text-gray-500 text-sm">
data_sources/__init__.py CHANGED
@@ -1,5 +1,6 @@
1
  from .upload_file import process_data_upload
2
  from .connect_sql_db import connect_sql_db
3
  from .connect_doc_db import connect_doc_db
 
4
 
5
- __all__ = ["process_data_upload","connect_sql_db","connect_doc_db"]
 
1
  from .upload_file import process_data_upload
2
  from .connect_sql_db import connect_sql_db
3
  from .connect_doc_db import connect_doc_db
4
+ from .connect_graphql import connect_graphql
5
 
6
+ __all__ = ["process_data_upload","connect_sql_db","connect_doc_db","connect_graphql"]
data_sources/connect_graphql.py ADDED
@@ -0,0 +1,145 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import requests
2
+ import os
3
+ import json
4
+ from utils import TEMP_DIR
5
+
6
+ def connect_graphql(graphql_url, api_token, graphql_token_header, session_hash):
7
+ try:
8
+ # Create the GraphQL Introspection Query
9
+ query = """
10
+ query IntrospectionQuery {
11
+ __schema {
12
+ queryType { name }
13
+ mutationType { name }
14
+ subscriptionType { name }
15
+ types {
16
+ ...FullType
17
+ }
18
+ directives {
19
+ name
20
+ description
21
+ locations
22
+ args {
23
+ ...InputValue
24
+ }
25
+ }
26
+ }
27
+ }
28
+ fragment FullType on __Type {
29
+ kind
30
+ name
31
+ description
32
+ fields(includeDeprecated: true) {
33
+ name
34
+ description
35
+ args {
36
+ ...InputValue
37
+ }
38
+ type {
39
+ ...TypeRef
40
+ }
41
+ isDeprecated
42
+ deprecationReason
43
+ }
44
+ inputFields {
45
+ ...InputValue
46
+ }
47
+ interfaces {
48
+ ...TypeRef
49
+ }
50
+ enumValues(includeDeprecated: true) {
51
+ name
52
+ description
53
+ isDeprecated
54
+ deprecationReason
55
+ }
56
+ possibleTypes {
57
+ ...TypeRef
58
+ }
59
+ }
60
+ fragment InputValue on __InputValue {
61
+ name
62
+ description
63
+ type { ...TypeRef }
64
+ defaultValue
65
+ }
66
+ fragment TypeRef on __Type {
67
+ kind
68
+ name
69
+ ofType {
70
+ kind
71
+ name
72
+ ofType {
73
+ kind
74
+ name
75
+ ofType {
76
+ kind
77
+ name
78
+ ofType {
79
+ kind
80
+ name
81
+ ofType {
82
+ kind
83
+ name
84
+ ofType {
85
+ kind
86
+ name
87
+ ofType {
88
+ kind
89
+ name
90
+ }
91
+ }
92
+ }
93
+ }
94
+ }
95
+ }
96
+ }
97
+ }
98
+ """
99
+ print("Connecting to GraphQL Endpoint")
100
+
101
+ # Access a database
102
+ headers = {"Content-Type": "application/json"}
103
+ if graphql_token_header and api_token:
104
+ headers[graphql_token_header] = api_token
105
+ response = requests.post(graphql_url, headers=headers, json={"query": query})
106
+ response.raise_for_status()
107
+
108
+ introspection_result = response.json()
109
+
110
+ client_schema = introspection_result["data"]["__schema"]
111
+
112
+ #Generate the list of types
113
+ type_names_query = """
114
+ query IntrospectionQuery {
115
+ __schema {
116
+ types {
117
+ name
118
+ }
119
+ }
120
+ }
121
+ """
122
+ types_response = requests.post(graphql_url, headers=headers, json={"query": type_names_query})
123
+
124
+ types_response_results =types_response.json()
125
+
126
+ types_names = types_response_results["data"]
127
+
128
+ type_names = []
129
+ for name in types_names["__schema"]["types"]:
130
+ type_names.append(name["name"])
131
+
132
+ session_path = 'graphql'
133
+
134
+ dir_path = TEMP_DIR / str(session_hash) / str(session_path)
135
+ os.makedirs(dir_path, exist_ok=True)
136
+
137
+ with open(f'{dir_path}/schema.json', 'w') as fp:
138
+ json.dump(client_schema, fp, indent=2)
139
+
140
+ return ["success","<p style='color:green;text-align:center;font-size:18px;'>GraphQL API connected successful</p>", type_names]
141
+ except Exception as e:
142
+ print("GraphQL CONNECTION ERROR")
143
+ print(e)
144
+ return ["error",f"<p style='color:red;text-align:center;font-size:18px;font-weight:bold;'>ERROR: {e}</p>"]
145
+
functions/__init__.py CHANGED
@@ -1,9 +1,9 @@
1
- from .query_functions import SQLiteQuery, sqlite_query_func, PostgreSQLQuery, sql_query_func, doc_db_query_func
2
  from .chart_functions import table_generation_func, scatter_chart_generation_func, \
3
  line_chart_generation_func, bar_chart_generation_func, pie_chart_generation_func, histogram_generation_func, scatter_chart_fig
4
- from .chat_functions import sql_example_question_generator, example_question_generator, doc_db_example_question_generator, chatbot_with_fc, sql_chatbot_with_fc, doc_db_chatbot_with_fc
5
  from .stat_functions import regression_func
6
 
7
- __all__ = ["SQLiteQuery","sqlite_query_func","sql_query_func","doc_db_query_func","table_generation_func","scatter_chart_generation_func",
8
  "line_chart_generation_func","bar_chart_generation_func","regression_func", "pie_chart_generation_func", "histogram_generation_func",
9
- "scatter_chart_fig","doc_db_example_question_generator","sql_example_question_generator","example_question_generator","chatbot_with_fc","sql_chatbot_with_fc","doc_db_chatbot_with_fc"]
 
1
+ from .query_functions import SQLiteQuery, sqlite_query_func, sql_query_func, doc_db_query_func, graphql_query_func, graphql_schema_query, graphql_csv_query
2
  from .chart_functions import table_generation_func, scatter_chart_generation_func, \
3
  line_chart_generation_func, bar_chart_generation_func, pie_chart_generation_func, histogram_generation_func, scatter_chart_fig
4
+ from .chat_functions import sql_example_question_generator, example_question_generator, doc_db_example_question_generator, chatbot_with_fc, sql_chatbot_with_fc, doc_db_chatbot_with_fc, graphql_chatbot_with_fc, graphql_example_question_generator
5
  from .stat_functions import regression_func
6
 
7
+ __all__ = ["SQLiteQuery","sqlite_query_func","sql_query_func","doc_db_query_func","graphql_query_func","graphql_schema_query","graphql_csv_query","table_generation_func","scatter_chart_generation_func",
8
  "line_chart_generation_func","bar_chart_generation_func","regression_func", "pie_chart_generation_func", "histogram_generation_func",
9
+ "scatter_chart_fig","doc_db_example_question_generator","sql_example_question_generator","example_question_generator","chatbot_with_fc","sql_chatbot_with_fc","doc_db_chatbot_with_fc","graphql_chatbot_with_fc","graphql_example_question_generator"]
functions/chat_functions.py CHANGED
@@ -78,6 +78,25 @@ def doc_db_example_question_generator(session_hash, db_collections, db_name, db_
78
 
79
  return example_response["replies"][0].text
80
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
81
  def chatbot_with_fc(message, history, session_hash):
82
  from functions import sqlite_query_func, table_generation_func, regression_func, scatter_chart_generation_func, \
83
  line_chart_generation_func,bar_chart_generation_func,pie_chart_generation_func,histogram_generation_func
@@ -218,15 +237,15 @@ def doc_db_chatbot_with_fc(message, history, session_hash, db_connection_string,
218
  else:
219
  messages = [
220
  ChatMessage.from_system(
221
- f"""You are a helpful and knowledgeable agent who has access to an NoSQL MongoDB Document database which has a series of collections called {db_collections}.
222
  The schema of these collections is: {db_schema}.
223
- You also have access to a function, called table_generation_func, that can take a query.csv file generated from our sql query and returns an iframe that we should display in our chat window.
224
- You also have access to a scatter plot function, called scatter_chart_generation_func, that can take a query.csv file generated from our sql query and uses plotly dictionaries to generate a scatter plot and returns an iframe that we should display in our chat window.
225
- You also have access to a line chart function, called line_chart_generation_func, that can take a query.csv file generated from our sql query and uses plotly dictionaries to generate a line chart and returns an iframe that we should display in our chat window.
226
- You also have access to a bar graph function, called line_chart_generation_func, that can take a query.csv file generated from our sql query and uses plotly dictionaries to generate a bar graph and returns an iframe that we should display in our chat window.
227
- You also have access to a pie chart function, called pie_chart_generation_func, that can take a query.csv file generated from our sql query and uses plotly dictionaries to generate a pie chart and returns an iframe that we should display in our chat window.
228
- You also have access to a histogram function, called histogram_generation_func, that can take a query.csv file generated from our sql query and uses plotly dictionaries to generate a histogram and returns an iframe that we should display in our chat window.
229
- You also have access to a linear regression function, called regression_func, that can take a query.csv file generated from our sql query and a list of column names for our independent and dependent variables and return a regression data string and a regression chart which is returned as an iframe.
230
  Could you please always display the generated charts, tables, and visualizations as part of your output?"""
231
  )
232
  ]
@@ -259,4 +278,63 @@ def doc_db_chatbot_with_fc(message, history, session_hash, db_connection_string,
259
  message_dict[session_hash]['doc_db'].append(response["replies"][0])
260
  break
261
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
262
  return response["replies"][0].text
 
78
 
79
  return example_response["replies"][0].text
80
 
81
+ def graphql_example_question_generator(session_hash, graphql_endpoint, graphql_types):
82
+ example_response = None
83
+ example_messages = [
84
+ ChatMessage.from_system(
85
+ f"You are a helpful and knowledgeable agent who has access to an GraphQL API endpoint called {graphql_endpoint}."
86
+ )
87
+ ]
88
+
89
+ example_messages.append(ChatMessage.from_user(text=f"""We have a GraphQL API endpoint with the following types: {graphql_types}.
90
+ We also have an AI agent with access to the same GraphQL API endpoint that will be performing data analysis.
91
+ Please return an array of seven strings, each one being a question for our data analysis agent
92
+ that we can suggest that you believe will be insightful or helpful to a data analysis looking for
93
+ data insights. Return nothing more than the array of questions because I need that specific data structure
94
+ to process your response. No other response type or data structure will work."""))
95
+
96
+ example_response = chat_generator.run(messages=example_messages)
97
+
98
+ return example_response["replies"][0].text
99
+
100
  def chatbot_with_fc(message, history, session_hash):
101
  from functions import sqlite_query_func, table_generation_func, regression_func, scatter_chart_generation_func, \
102
  line_chart_generation_func,bar_chart_generation_func,pie_chart_generation_func,histogram_generation_func
 
237
  else:
238
  messages = [
239
  ChatMessage.from_system(
240
+ f"""You are a helpful and knowledgeable agent who has access to a NoSQL MongoDB Document database which has a series of collections called {db_collections}.
241
  The schema of these collections is: {db_schema}.
242
+ You also have access to a function, called table_generation_func, that can take a query.csv file generated from our MongoDB query and returns an iframe that we should display in our chat window.
243
+ You also have access to a scatter plot function, called scatter_chart_generation_func, that can take a query.csv file generated from our MongoDB query and uses plotly dictionaries to generate a scatter plot and returns an iframe that we should display in our chat window.
244
+ You also have access to a line chart function, called line_chart_generation_func, that can take a query.csv file generated from our MongoDB query and uses plotly dictionaries to generate a line chart and returns an iframe that we should display in our chat window.
245
+ You also have access to a bar graph function, called line_chart_generation_func, that can take a query.csv file generated from our MongoDB query and uses plotly dictionaries to generate a bar graph and returns an iframe that we should display in our chat window.
246
+ You also have access to a pie chart function, called pie_chart_generation_func, that can take a query.csv file generated from our MongoDB query and uses plotly dictionaries to generate a pie chart and returns an iframe that we should display in our chat window.
247
+ You also have access to a histogram function, called histogram_generation_func, that can take a query.csv file generated from our MongoDB query and uses plotly dictionaries to generate a histogram and returns an iframe that we should display in our chat window.
248
+ You also have access to a linear regression function, called regression_func, that can take a query.csv file generated from our MongoDB query and a list of column names for our independent and dependent variables and return a regression data string and a regression chart which is returned as an iframe.
249
  Could you please always display the generated charts, tables, and visualizations as part of your output?"""
250
  )
251
  ]
 
278
  message_dict[session_hash]['doc_db'].append(response["replies"][0])
279
  break
280
 
281
+ return response["replies"][0].text
282
+
283
+ def graphql_chatbot_with_fc(message, history, session_hash, graphql_api_string, graphql_api_token, graphql_token_header, graphql_types):
284
+ from functions import graphql_query_func, graphql_schema_query, graphql_csv_query, table_generation_func, regression_func, scatter_chart_generation_func, \
285
+ line_chart_generation_func,bar_chart_generation_func,pie_chart_generation_func,histogram_generation_func
286
+ import tools.tools as tools
287
+
288
+ available_functions = {"graphql_query_func": graphql_query_func,"graphql_schema_query": graphql_schema_query,"graphql_csv_query": graphql_csv_query,"table_generation_func":table_generation_func,
289
+ "line_chart_generation_func":line_chart_generation_func,"bar_chart_generation_func":bar_chart_generation_func,
290
+ "scatter_chart_generation_func":scatter_chart_generation_func, "pie_chart_generation_func":pie_chart_generation_func,
291
+ "histogram_generation_func":histogram_generation_func,
292
+ "regression_func":regression_func }
293
+
294
+ if message_dict[session_hash]['graphql'] != None:
295
+ message_dict[session_hash]['graphql'].append(ChatMessage.from_user(message))
296
+ else:
297
+ messages = [
298
+ ChatMessage.from_system(
299
+ f"""You are a helpful and knowledgeable agent who has access to a GraphQL API which has the following types: {graphql_types}.
300
+ We have also saved a schema.json file that contains the entire introspection query that we can use to find out more about each type before making a query.
301
+ You also have access to a function, called table_generation_func, that can take a query.csv file generated from our GraphQL API query and returns an iframe that we should display in our chat window.
302
+ You also have access to a scatter plot function, called scatter_chart_generation_func, that can take a query.csv file generated from our GraphQL API query and uses plotly dictionaries to generate a scatter plot and returns an iframe that we should display in our chat window.
303
+ You also have access to a line chart function, called line_chart_generation_func, that can take a query.csv file generated from our GraphQL API query and uses plotly dictionaries to generate a line chart and returns an iframe that we should display in our chat window.
304
+ You also have access to a bar graph function, called line_chart_generation_func, that can take a query.csv file generated from our GraphQL API query and uses plotly dictionaries to generate a bar graph and returns an iframe that we should display in our chat window.
305
+ You also have access to a pie chart function, called pie_chart_generation_func, that can take a query.csv file generated from our GraphQL API query and uses plotly dictionaries to generate a pie chart and returns an iframe that we should display in our chat window.
306
+ You also have access to a histogram function, called histogram_generation_func, that can take a query.csv file generated from our GraphQL API query and uses plotly dictionaries to generate a histogram and returns an iframe that we should display in our chat window.
307
+ You also have access to a linear regression function, called regression_func, that can take a query.csv file generated from our GraphQL API query and a list of column names for our independent and dependent variables and return a regression data string and a regression chart which is returned as an iframe.
308
+ Could you please always display the generated charts, tables, and visualizations as part of your output?"""
309
+ )
310
+ ]
311
+ messages.append(ChatMessage.from_user(message))
312
+ message_dict[session_hash]['graphql'] = messages
313
+
314
+ response = chat_generator.run(messages=message_dict[session_hash]['graphql'], generation_kwargs={"tools": tools.graphql_tools_call(graphql_types)})
315
+
316
+ while True:
317
+ # if OpenAI response is a tool call
318
+ if response and response["replies"][0].meta["finish_reason"] == "tool_calls" or response["replies"][0].tool_calls:
319
+ function_calls = response["replies"][0].tool_calls
320
+ for function_call in function_calls:
321
+ message_dict[session_hash]['graphql'].append(ChatMessage.from_assistant(tool_calls=[function_call]))
322
+ ## Parse function calling information
323
+ function_name = function_call.tool_name
324
+ function_args = function_call.arguments
325
+
326
+ ## Find the corresponding function and call it with the given arguments
327
+ function_to_call = available_functions[function_name]
328
+ function_response = function_to_call(**function_args, session_hash=session_hash, graphql_api_string=graphql_api_string,
329
+ graphql_api_token=graphql_api_token, graphql_token_header=graphql_token_header, session_folder='graphql')
330
+ print(function_name)
331
+ ## Append function response to the messages list using `ChatMessage.from_tool`
332
+ message_dict[session_hash]['graphql'].append(ChatMessage.from_tool(tool_result=function_response['reply'], origin=function_call))
333
+ response = chat_generator.run(messages=message_dict[session_hash]['graphql'], generation_kwargs={"tools": tools.graphql_tools_call(graphql_types)})
334
+
335
+ # Regular Conversation
336
+ else:
337
+ message_dict[session_hash]['graphql'].append(response["replies"][0])
338
+ break
339
+
340
  return response["replies"][0].text
functions/query_functions.py CHANGED
@@ -2,6 +2,7 @@ from typing import List
2
  from typing import AnyStr
3
  from haystack import component
4
  import pandas as pd
 
5
  pd.set_option('display.max_rows', None)
6
  pd.set_option('display.max_columns', None)
7
  pd.set_option('display.width', None)
@@ -10,6 +11,8 @@ import sqlite3
10
  import psycopg2
11
  from pymongo import MongoClient
12
  import pymongoarrow.monkey
 
 
13
  from utils import TEMP_DIR
14
  import ast
15
 
@@ -164,4 +167,103 @@ def doc_db_query_func(aggregation_pipeline: List[str], db_collection: AnyStr, se
164
  You should probably try again.
165
  """
166
  print(reply)
167
- return {"reply": reply}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2
  from typing import AnyStr
3
  from haystack import component
4
  import pandas as pd
5
+ from pandasql import sqldf
6
  pd.set_option('display.max_rows', None)
7
  pd.set_option('display.max_columns', None)
8
  pd.set_option('display.width', None)
 
11
  import psycopg2
12
  from pymongo import MongoClient
13
  import pymongoarrow.monkey
14
+ import json
15
+ import pluck
16
  from utils import TEMP_DIR
17
  import ast
18
 
 
167
  You should probably try again.
168
  """
169
  print(reply)
170
+ return {"reply": reply}
171
+
172
+ @component
173
+ class GraphQLQuery:
174
+
175
+ def __init__(self):
176
+
177
+ self.connection = pluck
178
+
179
+ @component.output_types(results=List[str], queries=List[str])
180
+ def run(self, graphql_query, graphql_api_string, graphql_api_token, graphql_token_header, session_hash):
181
+ print("ATTEMPTING TO RUN GRAPHQL QUERY")
182
+ dir_path = TEMP_DIR / str(session_hash)
183
+ results = []
184
+
185
+ headers = {"Content-Type": "application/json"}
186
+ if graphql_token_header and graphql_api_token:
187
+ headers[graphql_token_header] = graphql_api_token
188
+
189
+ print(graphql_query)
190
+
191
+ response = self.connection.execute(url=graphql_api_string, headers=headers, query=graphql_query, column_names="short")
192
+
193
+ if response.errors:
194
+ raise ValueError(response.errors)
195
+ elif response.data:
196
+ print("DATA FRAME COMPLETE")
197
+ print(response)
198
+ response_frame = response.frames['default']
199
+ print("RESPONSE FRAME")
200
+ #print(response_frame)
201
+
202
+ response_frame.to_csv(f'{dir_path}/graphql/query.csv', index=False)
203
+ print("CSV COMPLETE")
204
+ results.append(f"{response_frame}")
205
+ return {"results": results, "queries": graphql_query}
206
+
207
+
208
+
209
+ def graphql_query_func(graphql_query: AnyStr, session_hash, graphql_api_string, graphql_api_token, graphql_token_header, **kwargs):
210
+ graphql_object = GraphQLQuery()
211
+ try:
212
+ result = graphql_object.run(graphql_query, graphql_api_string, graphql_api_token, graphql_token_header, session_hash)
213
+ print("RESULT")
214
+ if len(result["results"][0]) > 1000:
215
+ print("QUERY TOO LARGE")
216
+ return {"reply": "query result too large to be processed by llm, the query results are in our query.csv file. If you need to display the results directly, perhaps use the table_generation_func function."}
217
+ else:
218
+ return {"reply": result["results"][0]}
219
+
220
+ except Exception as e:
221
+ reply = f"""There was an error running the GraphQL Query = {graphql_query}
222
+ The error is {e},
223
+ You should probably try again.
224
+ """
225
+ print(reply)
226
+ return {"reply": reply}
227
+
228
+ def graphql_schema_query(graphql_type: AnyStr, session_hash, **kwargs):
229
+ dir_path = TEMP_DIR / str(session_hash)
230
+ try:
231
+ with open(f'{dir_path}/graphql/schema.json', 'r') as file:
232
+ data = json.load(file)
233
+
234
+ types_list = data["types"]
235
+ result = list(filter(lambda item: item["name"] == graphql_type, types_list))
236
+
237
+ print("SCHEMA RESULT")
238
+ print(graphql_type)
239
+ print(str(result))
240
+
241
+ return {"reply": str(result)}
242
+
243
+ except Exception as e:
244
+ reply = f"""There was an error querying our schema.json file with the type:{graphql_type}
245
+ The error is {e},
246
+ You should probably try again.
247
+ """
248
+ print(reply)
249
+ return {"reply": reply}
250
+
251
+ def graphql_csv_query(csv_query: AnyStr, session_hash, **kwargs):
252
+ dir_path = TEMP_DIR / str(session_hash)
253
+ try:
254
+ query = pd.read_csv(f'{dir_path}/graphql/query.csv')
255
+ query.Name = 'query'
256
+ print("GRAPHQL CSV QUERY")
257
+ queried_df = sqldf(csv_query, locals())
258
+ print(queried_df)
259
+ queried_df.to_csv(f'{dir_path}/graphql/query.csv', index=False)
260
+
261
+ return {"reply": "The new query results are in our query.csv file. If you need to display the results directly, perhaps use the table_generation_func function."}
262
+
263
+ except Exception as e:
264
+ reply = f"""There was an error querying our query.csv file with the query:{csv_query}
265
+ The error is {e},
266
+ You should probably try again.
267
+ """
268
+ print(reply)
269
+ return {"reply": reply}
requirements.txt CHANGED
@@ -10,3 +10,5 @@ psycopg2-binary
10
  pymongo
11
  pymongoarrow
12
  pymongo_schema
 
 
 
10
  pymongo
11
  pymongoarrow
12
  pymongo_schema
13
+ pandasql
14
+ pluck
templates/doc_db.py CHANGED
@@ -17,7 +17,7 @@ with gr.Blocks() as demo:
17
  <p style="font-weight:bold;">Notice: the way this system is designed, no login information is retained and credentials are passed as session variables until the user leaves or
18
  refreshes the page in which they disappear. They are never saved to any files. I also make use of the PyMongoArrow aggregate_pandas_all function to apply pipelines,
19
  which can't delete, drop, or add database lines to avoid unhappy accidents or glitches.
20
- That being said, it's probably not a good idea to connect a production database to a strange AI tool with an unfamiliar author.
21
  This should be for demonstration purposes.</p>
22
  <p>Contact me if this is something you would like built in your organization, on your infrastructure, and with the requisite privacy and control a production
23
  database analytics tool requires.</p>
@@ -38,7 +38,7 @@ with gr.Blocks() as demo:
38
  submit.click(fn=hide_info, outputs=description)
39
 
40
  @gr.render(inputs=[connection_string,connection_user,connection_password,doc_db_name], triggers=[submit.click])
41
- def sql_chat(request: gr.Request, connection_string=connection_string.value, connection_user=connection_user.value, connection_password=connection_password.value, doc_db_name=doc_db_name.value):
42
  if request.session_hash not in message_dict:
43
  message_dict[request.session_hash] = {}
44
  message_dict[request.session_hash]['doc_db'] = None
@@ -78,7 +78,7 @@ with gr.Blocks() as demo:
78
  db_name = gr.Textbox(visible=False, value=doc_db_name)
79
  db_collections = gr.Textbox(value=process_message[2], interactive=False, label="DB Collections")
80
  db_schema = gr.Textbox(visible=False, value=process_message[3])
81
- bot = gr.Chatbot(type='messages', label="CSV Chat Window", render_markdown=True, sanitize_html=False, show_label=True, render=False, visible=True, elem_classes="chatbot")
82
  chat = gr.ChatInterface(
83
  fn=doc_db_chatbot_with_fc,
84
  type='messages',
 
17
  <p style="font-weight:bold;">Notice: the way this system is designed, no login information is retained and credentials are passed as session variables until the user leaves or
18
  refreshes the page in which they disappear. They are never saved to any files. I also make use of the PyMongoArrow aggregate_pandas_all function to apply pipelines,
19
  which can't delete, drop, or add database lines to avoid unhappy accidents or glitches.
20
+ That being said, it's probably best to use caution when connecting to a production database to a strange AI tool with an unfamiliar author.
21
  This should be for demonstration purposes.</p>
22
  <p>Contact me if this is something you would like built in your organization, on your infrastructure, and with the requisite privacy and control a production
23
  database analytics tool requires.</p>
 
38
  submit.click(fn=hide_info, outputs=description)
39
 
40
  @gr.render(inputs=[connection_string,connection_user,connection_password,doc_db_name], triggers=[submit.click])
41
+ def db_chat(request: gr.Request, connection_string=connection_string.value, connection_user=connection_user.value, connection_password=connection_password.value, doc_db_name=doc_db_name.value):
42
  if request.session_hash not in message_dict:
43
  message_dict[request.session_hash] = {}
44
  message_dict[request.session_hash]['doc_db'] = None
 
78
  db_name = gr.Textbox(visible=False, value=doc_db_name)
79
  db_collections = gr.Textbox(value=process_message[2], interactive=False, label="DB Collections")
80
  db_schema = gr.Textbox(visible=False, value=process_message[3])
81
+ bot = gr.Chatbot(type='messages', label="DocDB Chat Window", render_markdown=True, sanitize_html=False, show_label=True, render=False, visible=True, elem_classes="chatbot")
82
  chat = gr.ChatInterface(
83
  fn=doc_db_chatbot_with_fc,
84
  type='messages',
templates/graphql.py ADDED
@@ -0,0 +1,109 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import ast
2
+ import gradio as gr
3
+ from functions import graphql_example_question_generator, graphql_chatbot_with_fc
4
+ from data_sources import connect_graphql
5
+ from utils import message_dict
6
+
7
+ import os
8
+ from dotenv import load_dotenv
9
+
10
+ load_dotenv()
11
+
12
+ graphql_sample_endpoint = os.getenv("GRAPHQL_SAMPLE_ENDPOINT")
13
+ graphql_sample_api_token = os.getenv("GRAPHQL_SAMPLE_API_TOKEN")
14
+ graphql_sample_header_name = os.getenv("GRAPHQL_SAMPLE_HEADER_NAME")
15
+
16
+ def hide_info():
17
+ return gr.update(visible=False)
18
+
19
+ with gr.Blocks() as demo:
20
+ description = gr.HTML("""
21
+ <!-- Header -->
22
+ <div class="max-w-4xl mx-auto mb-12 text-center">
23
+ <div class="bg-blue-50 border border-blue-200 rounded-lg max-w-2xl mx-auto">
24
+ <p>This tool allows users to communicate with and query real time data from a GraphQL API endpoint using natural
25
+ language and the above features.</p>
26
+ <p style="font-weight:bold;">Notice: the way this system is designed, no login information is retained and credentials are passed as session variables until the user leaves or
27
+ refreshes the page in which they disappear. They are never saved to any files.</p>
28
+ <p style="font-weight:bold;"> I don't include a function that allows the system to run mutations and I instruct the agent to not alter any data, but it could in theory be possible,
29
+ although my testing wasn't able to get the system to alter or write to the api. I would be careful to make sure permissions are restricted for the
30
+ api token being used.
31
+ And of course, it's probably best to use caution when connecting to a strange AI tool with an unfamiliar author.
32
+ This should be for demonstration purposes.</p>
33
+ <p>Contact me if this is something you would like built in your organization, on your infrastructure, and with the requisite privacy and control a production
34
+ database analytics tool requires.</p>
35
+ </div>
36
+ </div>
37
+ """, elem_classes="description_component")
38
+
39
+ status_message = gr.HTML(value='<p style="color:green;text-align:center;font-size:18px;">Please be patient while connecting as we need to generate '
40
+ 'and read a schema before connection can be successful. This process can take a few minutes.</p>', padding=False)
41
+
42
+ graphql_url = gr.Textbox(label="GraphQL Endpoint URL", value=graphql_sample_endpoint)
43
+ with gr.Row():
44
+ api_token_header_name = gr.Textbox(label="API Token Header Name", value=graphql_sample_header_name)
45
+ api_token = gr.Textbox(label="API Token", value=graphql_sample_api_token, type="password")
46
+
47
+ submit = gr.Button(value="Submit")
48
+ submit.click(fn=hide_info, outputs=description)
49
+
50
+ @gr.render(inputs=[graphql_url,api_token,api_token_header_name], triggers=[submit.click])
51
+ def api_chat(request: gr.Request, graphql_url=graphql_url.value, api_token=api_token.value, api_token_header_name=api_token_header_name.value):
52
+ if request.session_hash not in message_dict:
53
+ message_dict[request.session_hash] = {}
54
+ message_dict[request.session_hash]['graphql'] = None
55
+ if graphql_url:
56
+ print("GraphQL API")
57
+ process_message = process_graphql(graphql_url, api_token, api_token_header_name, request.session_hash)
58
+ gr.HTML(value=process_message[1], padding=False)
59
+ if process_message[0] == "success":
60
+ if "qdl-app-testing" in graphql_url:
61
+ example_questions = [
62
+ ["Describe the dataset"],
63
+ ["What is the total revenue for this shopify store?"],
64
+ ["What is the average duration from the fulfillment of an order to its delivery?"],
65
+ ["What is the total value of orders processed in the current month?"],
66
+ ["Which product has the highest number of variants in the inventory?"],
67
+ ["How many gift cards have been issued this year, and what is their total value?"],
68
+ ["How many active apps are currently installed on the store?"],
69
+ ["What is the total count of abandoned checkouts over the last month?"]
70
+ ]
71
+ else:
72
+ try:
73
+ generated_examples = ast.literal_eval(graphql_example_question_generator(request.session_hash, graphql_url, process_message[2]))
74
+ example_questions = [
75
+ ["Describe the dataset"]
76
+ ]
77
+ for example in generated_examples:
78
+ example_questions.append([example])
79
+ except Exception as e:
80
+ print("GRAPHQL QUESTION GENERATION ERROR")
81
+ print(e)
82
+ example_questions = [
83
+ ["Describe the dataset"],
84
+ ["List the columns in the dataset"],
85
+ ["What could this data be used for?"],
86
+ ]
87
+ session_hash = gr.Textbox(visible=False, value=request.session_hash)
88
+ graphql_api_string = gr.Textbox(visible=False, value=graphql_url)
89
+ graphql_api_token = gr.Textbox(visible=False, value=api_token)
90
+ graphql_token_header = gr.Textbox(visible=False, value=api_token_header_name)
91
+ graphql_types = gr.Textbox(value=process_message[2], interactive=False, label="GraphQL Types")
92
+ bot = gr.Chatbot(type='messages', label="GraphQL Chat Window", render_markdown=True, sanitize_html=False, show_label=True, render=False, visible=True, elem_classes="chatbot")
93
+ chat = gr.ChatInterface(
94
+ fn=graphql_chatbot_with_fc,
95
+ type='messages',
96
+ chatbot=bot,
97
+ title="Chat with your Graphql API",
98
+ examples=example_questions,
99
+ concurrency_limit=None,
100
+ additional_inputs=[session_hash, graphql_api_string, graphql_api_token, graphql_token_header, graphql_types]
101
+ )
102
+
103
+ def process_graphql(graphql_url, api_token, api_token_header_name, session_hash):
104
+ if graphql_url:
105
+ process_message = connect_graphql(graphql_url, api_token, api_token_header_name, session_hash)
106
+ return process_message
107
+
108
+ if __name__ == "__main__":
109
+ demo.launch()
templates/sql_db.py CHANGED
@@ -17,7 +17,7 @@ with gr.Blocks() as demo:
17
  <p style="font-weight:bold;">Notice: the way this system is designed, no login information is retained and credentials are passed as session variables until the user leaves or
18
  refreshes the page in which they disappear. They are never saved to any files. I also make use of the Pandas read_sql_query function to apply SQL
19
  queries, which can't delete, drop, or add database lines to avoid unhappy accidents or glitches.
20
- That being said, it's probably not a good idea to connect a production database to a strange AI tool with an unfamiliar author.
21
  This should be for demonstration purposes.</p>
22
  <p>Contact me if this is something you would like built in your organization, on your infrastructure, and with the requisite privacy and control a production
23
  database analytics tool requires.</p>
@@ -76,7 +76,7 @@ with gr.Blocks() as demo:
76
  db_pass = gr.Textbox(visible=False, value=sql_pass)
77
  db_name = gr.Textbox(visible=False, value=sql_db_name)
78
  db_tables = gr.Textbox(value=process_message[2], interactive=False, label="SQL Tables")
79
- bot = gr.Chatbot(type='messages', label="CSV Chat Window", render_markdown=True, sanitize_html=False, show_label=True, render=False, visible=True, elem_classes="chatbot")
80
  chat = gr.ChatInterface(
81
  fn=sql_chatbot_with_fc,
82
  type='messages',
 
17
  <p style="font-weight:bold;">Notice: the way this system is designed, no login information is retained and credentials are passed as session variables until the user leaves or
18
  refreshes the page in which they disappear. They are never saved to any files. I also make use of the Pandas read_sql_query function to apply SQL
19
  queries, which can't delete, drop, or add database lines to avoid unhappy accidents or glitches.
20
+ That being said, it's probably best to use caution when connecting to a production database to a strange AI tool with an unfamiliar author.
21
  This should be for demonstration purposes.</p>
22
  <p>Contact me if this is something you would like built in your organization, on your infrastructure, and with the requisite privacy and control a production
23
  database analytics tool requires.</p>
 
76
  db_pass = gr.Textbox(visible=False, value=sql_pass)
77
  db_name = gr.Textbox(visible=False, value=sql_db_name)
78
  db_tables = gr.Textbox(value=process_message[2], interactive=False, label="SQL Tables")
79
+ bot = gr.Chatbot(type='messages', label="SQL DB Chat Window", render_markdown=True, sanitize_html=False, show_label=True, render=False, visible=True, elem_classes="chatbot")
80
  chat = gr.ChatInterface(
81
  fn=sql_chatbot_with_fc,
82
  type='messages',
tools/tools.py CHANGED
@@ -98,14 +98,84 @@ def doc_db_tools_call(db_collections):
98
  "properties": {
99
  "aggregation_pipeline": {
100
  "type": "string",
101
- "description": "The MongoDB aggregation pipeline to use in the search. Infer this from the user's message. It should be a question or a statement"
102
  },
103
  "db_collection": {
104
  "type": "string",
105
- "description": "The MongoDB collection to use in the search. Infer this from the user's message. It should be a question or a statement",
106
  }
107
  },
108
- "required": ["queries","db_collection"],
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
109
  },
110
  },
111
  },
 
98
  "properties": {
99
  "aggregation_pipeline": {
100
  "type": "string",
101
+ "description": "The MongoDB aggregation pipeline to use in the search. Infer this from the user's message. It should be a question or a statement."
102
  },
103
  "db_collection": {
104
  "type": "string",
105
+ "description": "The MongoDB collection to use in the search. Infer this from the user's message. It should be a question or a statement.",
106
  }
107
  },
108
+ "required": ["aggregation_pipeline","db_collection"],
109
+ },
110
+ },
111
+ },
112
+ ]
113
+
114
+ tools_calls.extend(chart_tools)
115
+ tools_calls.extend(stats_tools)
116
+
117
+ return tools_calls
118
+
119
+ def graphql_tools_call(graphql_types):
120
+
121
+ types_string = (graphql_types[:625] + '..') if len(graphql_types) > 625 else graphql_types
122
+
123
+ tools_calls = [
124
+ {
125
+ "type": "function",
126
+ "function": {
127
+ "name": "graphql_query_func",
128
+ "description": f"""This is a tool useful to build a GraphQL query for a GraphQL API endpoint with the following types, {types_string}.
129
+ There may also be more types in the GraphQL endpoint if the number of types is too large to process.
130
+ This function also saves the results of the query to a csv file called query.csv.""",
131
+ "parameters": {
132
+ "type": "object",
133
+ "properties": {
134
+ "graphql_query": {
135
+ "type": "string",
136
+ "description": "The GraphQL query to use in the search. Infer this from the user's message. It should be a question or a statement."
137
+ }
138
+ },
139
+ "required": ["graphql_query"],
140
+ },
141
+ },
142
+ },
143
+ {
144
+ "type": "function",
145
+ "function": {
146
+ "name": "graphql_schema_query",
147
+ "description": f"""This is a tool useful to query a GraphQL type and receive back information about its schema. This is useful because
148
+ the GraphQL introspection query is too large to be ingested all at once and this allows us to query the schema one type at a time to
149
+ view it in manageable bites. You may realize after viewing the schema, that the type you selected was not appropriate for the question
150
+ you are attempting answer. You may then query additional types to find the appropriate types to use for your GraphQL API query.""",
151
+ "parameters": {
152
+ "type": "object",
153
+ "properties": {
154
+ "graphql_type": {
155
+ "type": "string",
156
+ "description": "The GraphQL type that we want to view the schema of in order to make the proper query with our graphql_query_func. Infer this from the user's message. It should be a question or a statement."
157
+ }
158
+ },
159
+ "required": ["graphql_type"],
160
+ },
161
+ },
162
+ },
163
+ {
164
+ "type": "function",
165
+ "function": {
166
+ "name": "graphql_csv_query",
167
+ "description": f"""This is a tool useful to SQL query our query.csv file that is generated from our GraphQL query. This is useful in a situation
168
+ where the results of the GraphQL query need additional querying to answer the user question. The query.csv file is converted to a Pandas dataframe
169
+ and we query that dataframe with SQL on a table called 'query' before converting it back to a csv file.""",
170
+ "parameters": {
171
+ "type": "object",
172
+ "properties": {
173
+ "csv_query": {
174
+ "type": "string",
175
+ "description": "The pandas dataframe SQL query to use in the search. The table that we query is named 'query'. Infer this from the user's message. It should be a question or a statement"
176
+ }
177
+ },
178
+ "required": ["csv_query"],
179
  },
180
  },
181
  },